@arbidocs/blocks 0.3.185 → 0.4.1

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 (612) hide show
  1. package/README.md +122 -0
  2. package/dist/agents/approvals/AuthorizeAgentParts.d.ts +79 -0
  3. package/dist/agents/approvals/ConsentPage.d.ts +76 -0
  4. package/dist/agents/approvals/ConsentParts.d.ts +37 -0
  5. package/dist/agents/approvals/index.d.ts +3 -0
  6. package/dist/agents/config/ConfigField.d.ts +43 -0
  7. package/dist/agents/config/ConfigForm.d.ts +35 -0
  8. package/dist/agents/config/TupleArrayField.d.ts +32 -0
  9. package/dist/agents/config/index.d.ts +4 -0
  10. package/dist/agents/config/schema-utils.d.ts +28 -0
  11. package/dist/agents/definitions/AgentCard.d.ts +63 -0
  12. package/dist/agents/definitions/AgentCreator.d.ts +118 -0
  13. package/dist/agents/definitions/AgentEditorParts.d.ts +63 -0
  14. package/dist/agents/definitions/AgentGallery.d.ts +29 -0
  15. package/dist/agents/definitions/index.d.ts +4 -0
  16. package/dist/agents/index.cjs +595 -0
  17. package/dist/agents/index.cjs.map +1 -0
  18. package/dist/agents/index.d.ts +7 -0
  19. package/dist/agents/index.js +14 -0
  20. package/dist/agents/index.js.map +1 -0
  21. package/dist/agents/runs/AgentBar.d.ts +103 -0
  22. package/dist/agents/runs/AgentSteps.d.ts +157 -0
  23. package/dist/agents/runs/SubagentRoster.d.ts +37 -0
  24. package/dist/agents/runs/index.d.ts +3 -0
  25. package/dist/agents/skills/SkillBrowserParts.d.ts +70 -0
  26. package/dist/agents/skills/SkillCard.d.ts +48 -0
  27. package/dist/agents/skills/SkillForms.d.ts +64 -0
  28. package/dist/agents/skills/SkillParts.d.ts +31 -0
  29. package/dist/agents/skills/SkillRow.d.ts +34 -0
  30. package/dist/agents/skills/index.d.ts +5 -0
  31. package/dist/agents/tasks/LiveRunsBanner.d.ts +26 -0
  32. package/dist/agents/tasks/RecurrenceBuilder.d.ts +36 -0
  33. package/dist/agents/tasks/TaskComposer.d.ts +108 -0
  34. package/dist/agents/tasks/TaskPlan.d.ts +78 -0
  35. package/dist/agents/tasks/TaskRowActions.d.ts +43 -0
  36. package/dist/agents/tasks/TaskRunsDialog.d.ts +45 -0
  37. package/dist/agents/tasks/TasksChrome.d.ts +36 -0
  38. package/dist/agents/tasks/UploadTaskItem.d.ts +39 -0
  39. package/dist/agents/tasks/UploadTaskList.d.ts +35 -0
  40. package/dist/agents/tasks/elapsed.d.ts +2 -0
  41. package/dist/agents/tasks/index.d.ts +13 -0
  42. package/dist/agents/tasks/rrule.d.ts +36 -0
  43. package/dist/agents/tasks/taskColumns.d.ts +68 -0
  44. package/dist/agents/tasks/taskLabels.d.ts +26 -0
  45. package/dist/agents/workflows/AgentSelect.d.ts +33 -0
  46. package/dist/agents/workflows/SchemaForm.d.ts +37 -0
  47. package/dist/agents/workflows/WorkflowConfigDialog.d.ts +73 -0
  48. package/dist/agents/workflows/WorkflowLauncher.d.ts +38 -0
  49. package/dist/agents/workflows/index.d.ts +6 -0
  50. package/dist/agents/workflows/types.d.ts +125 -0
  51. package/dist/agents/workflows/workflow.d.ts +62 -0
  52. package/dist/auth/AuthFooter.d.ts +56 -0
  53. package/dist/auth/AuthForm.d.ts +105 -0
  54. package/dist/auth/AuthLayout.d.ts +46 -0
  55. package/dist/auth/PasswordStrengthInput.d.ts +44 -0
  56. package/dist/auth/RecoveryDialogs.d.ts +90 -0
  57. package/dist/auth/SsoLoginButton.d.ts +19 -0
  58. package/dist/auth/index.cjs +123 -0
  59. package/dist/auth/index.cjs.map +1 -0
  60. package/dist/auth/index.d.ts +6 -0
  61. package/dist/auth/index.js +6 -0
  62. package/dist/auth/index.js.map +1 -0
  63. package/dist/builder/AgentsPanel.d.ts +9 -0
  64. package/dist/builder/ModuleManager.d.ts +6 -0
  65. package/dist/builder/VerticalBuilder.d.ts +32 -0
  66. package/dist/builder/agents/store.d.ts +21 -0
  67. package/dist/builder/export.d.ts +30 -0
  68. package/dist/builder/modules/registry.d.ts +11 -0
  69. package/dist/builder/modules/store.d.ts +29 -0
  70. package/dist/builder/modules/types.d.ts +29 -0
  71. package/dist/builder/types.d.ts +28 -0
  72. package/dist/chat/BranchControls.d.ts +33 -0
  73. package/dist/chat/Composer.d.ts +156 -0
  74. package/dist/chat/ContextChip.d.ts +19 -0
  75. package/dist/chat/ContextPanel.d.ts +108 -0
  76. package/dist/chat/ConversationEmpty.d.ts +53 -0
  77. package/dist/chat/ConversationLayout.d.ts +40 -0
  78. package/dist/chat/ConversationParts.d.ts +62 -0
  79. package/dist/chat/ConversationSearchView.d.ts +57 -0
  80. package/dist/chat/LabelPinRow.d.ts +30 -0
  81. package/dist/chat/Message.d.ts +189 -0
  82. package/dist/chat/MessageAlert.d.ts +25 -0
  83. package/dist/chat/MessageList.d.ts +102 -0
  84. package/dist/chat/ModelPicker.d.ts +103 -0
  85. package/dist/chat/PendingAnswerBar.d.ts +36 -0
  86. package/dist/chat/PinnedDocChip.d.ts +22 -0
  87. package/dist/chat/SlashMenu.d.ts +57 -0
  88. package/dist/chat/ThreadMap.d.ts +45 -0
  89. package/dist/chat/index.cjs +423 -0
  90. package/dist/chat/index.cjs.map +1 -0
  91. package/dist/chat/index.d.ts +21 -0
  92. package/dist/chat/index.js +6 -0
  93. package/dist/chat/index.js.map +1 -0
  94. package/dist/chat/test-id.d.ts +16 -0
  95. package/dist/chat/useStreamAnnouncement.d.ts +19 -0
  96. package/dist/chunk-223D4727.cjs +50 -0
  97. package/dist/chunk-223D4727.cjs.map +1 -0
  98. package/dist/chunk-2K33YGM6.cjs +1846 -0
  99. package/dist/chunk-2K33YGM6.cjs.map +1 -0
  100. package/dist/chunk-2ORA62OF.js +266 -0
  101. package/dist/chunk-2ORA62OF.js.map +1 -0
  102. package/dist/chunk-4EZVCGRL.js +803 -0
  103. package/dist/chunk-4EZVCGRL.js.map +1 -0
  104. package/dist/chunk-54Q3RNJU.cjs +516 -0
  105. package/dist/chunk-54Q3RNJU.cjs.map +1 -0
  106. package/dist/chunk-5B4PXAKI.cjs +287 -0
  107. package/dist/chunk-5B4PXAKI.cjs.map +1 -0
  108. package/dist/chunk-6HIPFEID.cjs +574 -0
  109. package/dist/chunk-6HIPFEID.cjs.map +1 -0
  110. package/dist/{chunk-FZ2S6VAZ.js → chunk-6P6AEQJB.js} +413 -1132
  111. package/dist/chunk-6P6AEQJB.js.map +1 -0
  112. package/dist/chunk-76JDDMSP.js +31 -0
  113. package/dist/chunk-76JDDMSP.js.map +1 -0
  114. package/dist/chunk-7F6CBP3Z.js +12 -0
  115. package/dist/chunk-7F6CBP3Z.js.map +1 -0
  116. package/dist/chunk-7G2LJMUG.cjs +45 -0
  117. package/dist/chunk-7G2LJMUG.cjs.map +1 -0
  118. package/dist/chunk-7PEELPMA.js +1799 -0
  119. package/dist/chunk-7PEELPMA.js.map +1 -0
  120. package/dist/chunk-A2YAC22G.js +93 -0
  121. package/dist/chunk-A2YAC22G.js.map +1 -0
  122. package/dist/chunk-ADOKTJHV.cjs +262 -0
  123. package/dist/chunk-ADOKTJHV.cjs.map +1 -0
  124. package/dist/chunk-AE5FHFZ5.cjs +272 -0
  125. package/dist/chunk-AE5FHFZ5.cjs.map +1 -0
  126. package/dist/chunk-AXIZ2MDV.js +751 -0
  127. package/dist/chunk-AXIZ2MDV.js.map +1 -0
  128. package/dist/chunk-AZQDNYH4.js +2865 -0
  129. package/dist/chunk-AZQDNYH4.js.map +1 -0
  130. package/dist/chunk-C54XEN5T.cjs +34 -0
  131. package/dist/chunk-C54XEN5T.cjs.map +1 -0
  132. package/dist/chunk-CQCC7LE7.js +210 -0
  133. package/dist/chunk-CQCC7LE7.js.map +1 -0
  134. package/dist/chunk-CRIKTUEU.cjs +47 -0
  135. package/dist/chunk-CRIKTUEU.cjs.map +1 -0
  136. package/dist/chunk-CTZ5745I.js +564 -0
  137. package/dist/chunk-CTZ5745I.js.map +1 -0
  138. package/dist/chunk-DPLRHEJZ.js +78 -0
  139. package/dist/chunk-DPLRHEJZ.js.map +1 -0
  140. package/dist/{chunk-DBAUZD7A.cjs → chunk-DREI7EQ2.cjs} +437 -1152
  141. package/dist/chunk-DREI7EQ2.cjs.map +1 -0
  142. package/dist/chunk-ELR74ME6.cjs +68 -0
  143. package/dist/chunk-ELR74ME6.cjs.map +1 -0
  144. package/dist/chunk-G3G7LZPM.cjs +205 -0
  145. package/dist/chunk-G3G7LZPM.cjs.map +1 -0
  146. package/dist/chunk-GDFWJ73C.cjs +2973 -0
  147. package/dist/chunk-GDFWJ73C.cjs.map +1 -0
  148. package/dist/chunk-ITGHMJOM.js +5528 -0
  149. package/dist/chunk-ITGHMJOM.js.map +1 -0
  150. package/dist/chunk-J2BGSWXM.cjs +990 -0
  151. package/dist/chunk-J2BGSWXM.cjs.map +1 -0
  152. package/dist/chunk-K4BGOPGG.js +79 -0
  153. package/dist/chunk-K4BGOPGG.js.map +1 -0
  154. package/dist/chunk-LGBAZD37.js +341 -0
  155. package/dist/chunk-LGBAZD37.js.map +1 -0
  156. package/dist/chunk-LRUCMZK6.cjs +680 -0
  157. package/dist/chunk-LRUCMZK6.cjs.map +1 -0
  158. package/dist/chunk-MHVNT4J3.cjs +15 -0
  159. package/dist/chunk-MHVNT4J3.cjs.map +1 -0
  160. package/dist/chunk-NBV56SGU.js +42 -0
  161. package/dist/chunk-NBV56SGU.js.map +1 -0
  162. package/dist/chunk-NPLYHR2E.js +13 -0
  163. package/dist/chunk-NPLYHR2E.js.map +1 -0
  164. package/dist/chunk-NUNKSZKU.js +203 -0
  165. package/dist/chunk-NUNKSZKU.js.map +1 -0
  166. package/dist/chunk-OB5F4577.cjs +40 -0
  167. package/dist/chunk-OB5F4577.cjs.map +1 -0
  168. package/dist/chunk-OBLSURHV.js +973 -0
  169. package/dist/chunk-OBLSURHV.js.map +1 -0
  170. package/dist/chunk-OUW6G4O2.cjs +96 -0
  171. package/dist/chunk-OUW6G4O2.cjs.map +1 -0
  172. package/dist/chunk-OVEQERPO.js +66 -0
  173. package/dist/chunk-OVEQERPO.js.map +1 -0
  174. package/dist/chunk-P6GHJSLR.js +34 -0
  175. package/dist/chunk-P6GHJSLR.js.map +1 -0
  176. package/dist/chunk-PCB2BPN3.cjs +41 -0
  177. package/dist/chunk-PCB2BPN3.cjs.map +1 -0
  178. package/dist/chunk-PPCATBOY.cjs +15 -0
  179. package/dist/chunk-PPCATBOY.cjs.map +1 -0
  180. package/dist/chunk-QN42OA5J.js +887 -0
  181. package/dist/chunk-QN42OA5J.js.map +1 -0
  182. package/dist/chunk-QXEJXIP4.js +675 -0
  183. package/dist/chunk-QXEJXIP4.js.map +1 -0
  184. package/dist/chunk-RC6VTY2H.js +47 -0
  185. package/dist/chunk-RC6VTY2H.js.map +1 -0
  186. package/dist/chunk-RFIBUWLF.cjs +232 -0
  187. package/dist/chunk-RFIBUWLF.cjs.map +1 -0
  188. package/dist/chunk-S7PSMMRG.js +38 -0
  189. package/dist/chunk-S7PSMMRG.js.map +1 -0
  190. package/dist/chunk-SCFYPAG6.cjs +18 -0
  191. package/dist/chunk-SCFYPAG6.cjs.map +1 -0
  192. package/dist/chunk-SYDHKKRZ.cjs +818 -0
  193. package/dist/chunk-SYDHKKRZ.cjs.map +1 -0
  194. package/dist/chunk-TX2ZICS7.js +139 -0
  195. package/dist/chunk-TX2ZICS7.js.map +1 -0
  196. package/dist/chunk-TYZUCAT7.js +787 -0
  197. package/dist/chunk-TYZUCAT7.js.map +1 -0
  198. package/dist/chunk-U2E3WT3J.js +45 -0
  199. package/dist/chunk-U2E3WT3J.js.map +1 -0
  200. package/dist/chunk-UBNVIIBT.js +284 -0
  201. package/dist/chunk-UBNVIIBT.js.map +1 -0
  202. package/dist/chunk-UFHMLHIR.cjs +66 -0
  203. package/dist/chunk-UFHMLHIR.cjs.map +1 -0
  204. package/dist/chunk-UJE32SST.js +39 -0
  205. package/dist/chunk-UJE32SST.js.map +1 -0
  206. package/dist/chunk-ULWRNGGE.cjs +146 -0
  207. package/dist/chunk-ULWRNGGE.cjs.map +1 -0
  208. package/dist/chunk-UWGIB3VQ.js +64 -0
  209. package/dist/chunk-UWGIB3VQ.js.map +1 -0
  210. package/dist/chunk-V4ISCZJ7.cjs +207 -0
  211. package/dist/chunk-V4ISCZJ7.cjs.map +1 -0
  212. package/dist/chunk-VFUWWH52.cjs +83 -0
  213. package/dist/chunk-VFUWWH52.cjs.map +1 -0
  214. package/dist/chunk-VILAPTCB.js +259 -0
  215. package/dist/chunk-VILAPTCB.js.map +1 -0
  216. package/dist/chunk-VLCY46K6.cjs +82 -0
  217. package/dist/chunk-VLCY46K6.cjs.map +1 -0
  218. package/dist/chunk-VPPX5XUF.cjs +813 -0
  219. package/dist/chunk-VPPX5XUF.cjs.map +1 -0
  220. package/dist/chunk-WJMEXTVD.cjs +122 -0
  221. package/dist/chunk-WJMEXTVD.cjs.map +1 -0
  222. package/dist/chunk-WTNMWEBC.js +504 -0
  223. package/dist/chunk-WTNMWEBC.js.map +1 -0
  224. package/dist/chunk-WW6EOVZA.js +112 -0
  225. package/dist/chunk-WW6EOVZA.js.map +1 -0
  226. package/dist/chunk-XNBAXRY7.cjs +5673 -0
  227. package/dist/chunk-XNBAXRY7.cjs.map +1 -0
  228. package/dist/chunk-XQ7PKKDY.cjs +787 -0
  229. package/dist/chunk-XQ7PKKDY.cjs.map +1 -0
  230. package/dist/chunk-XZIYRKLW.cjs +347 -0
  231. package/dist/chunk-XZIYRKLW.cjs.map +1 -0
  232. package/dist/chunk-YBXCZEWS.js +187 -0
  233. package/dist/chunk-YBXCZEWS.js.map +1 -0
  234. package/dist/chunk-YDHO4AVL.js +16 -0
  235. package/dist/chunk-YDHO4AVL.js.map +1 -0
  236. package/dist/chunk-ZORQ5M3W.cjs +934 -0
  237. package/dist/chunk-ZORQ5M3W.cjs.map +1 -0
  238. package/dist/chunk-ZW6FPIFC.cjs +36 -0
  239. package/dist/chunk-ZW6FPIFC.cjs.map +1 -0
  240. package/dist/citations/CitationEditor.d.ts +58 -0
  241. package/dist/citations/PassagePill.d.ts +49 -0
  242. package/dist/citations/QuietMarks.d.ts +81 -0
  243. package/dist/citations/index.cjs +47 -0
  244. package/dist/citations/index.cjs.map +1 -0
  245. package/dist/citations/index.d.ts +3 -0
  246. package/dist/citations/index.js +6 -0
  247. package/dist/citations/index.js.map +1 -0
  248. package/dist/collaboration/contacts/AddContactsParts.d.ts +46 -0
  249. package/dist/collaboration/contacts/index.d.ts +1 -0
  250. package/dist/collaboration/direct-messages/DirectMessages.d.ts +33 -0
  251. package/dist/collaboration/direct-messages/DmParts.d.ts +139 -0
  252. package/dist/collaboration/direct-messages/index.d.ts +2 -0
  253. package/dist/collaboration/index.cjs +141 -0
  254. package/dist/collaboration/index.cjs.map +1 -0
  255. package/dist/collaboration/index.d.ts +5 -0
  256. package/dist/collaboration/index.js +8 -0
  257. package/dist/collaboration/index.js.map +1 -0
  258. package/dist/collaboration/mail/MailFolderTree.d.ts +34 -0
  259. package/dist/collaboration/mail/index.d.ts +2 -0
  260. package/dist/collaboration/mail/mailFolders.d.ts +25 -0
  261. package/dist/collaboration/members/WorkspaceMembers.d.ts +74 -0
  262. package/dist/collaboration/members/index.d.ts +1 -0
  263. package/dist/collaboration/sharing/ShareControl.d.ts +38 -0
  264. package/dist/collaboration/sharing/index.d.ts +1 -0
  265. package/dist/components/Accordion.d.ts +12 -0
  266. package/dist/components/ActionLink.d.ts +18 -0
  267. package/dist/components/AskBar.d.ts +48 -0
  268. package/dist/components/AvatarBlock.d.ts +9 -0
  269. package/dist/components/Banner.d.ts +12 -0
  270. package/dist/components/BlockImage.d.ts +18 -0
  271. package/dist/components/CTABanner.d.ts +17 -0
  272. package/dist/components/Callout.d.ts +9 -0
  273. package/dist/components/Chart.d.ts +35 -0
  274. package/dist/components/CollectionDetail.d.ts +23 -0
  275. package/dist/components/Columns.d.ts +11 -0
  276. package/dist/components/ContactForm.d.ts +54 -0
  277. package/dist/components/Container.d.ts +11 -0
  278. package/dist/components/DataTable.d.ts +47 -0
  279. package/dist/components/DataTableBlock.d.ts +26 -0
  280. package/dist/components/EmptyState.d.ts +11 -0
  281. package/dist/components/EntityChip.d.ts +24 -0
  282. package/dist/components/EntityIdentityBar.d.ts +90 -0
  283. package/dist/components/EntityTooltip.d.ts +43 -0
  284. package/dist/components/FAQ.d.ts +14 -0
  285. package/dist/components/FeatureGrid.d.ts +18 -0
  286. package/dist/components/Footer.d.ts +25 -0
  287. package/dist/components/Gallery.d.ts +15 -0
  288. package/dist/components/Hero.d.ts +35 -0
  289. package/dist/components/ListBlock.d.ts +10 -0
  290. package/dist/components/LogoCloud.d.ts +12 -0
  291. package/dist/components/MediaText.d.ts +15 -0
  292. package/dist/components/MemberAvatarStack.d.ts +36 -0
  293. package/dist/components/MetricCard.d.ts +28 -0
  294. package/dist/components/Navbar.d.ts +39 -0
  295. package/dist/components/PageHeader.d.ts +15 -0
  296. package/dist/components/PricingTable.d.ts +19 -0
  297. package/dist/components/RichTextBlock.d.ts +10 -0
  298. package/dist/components/SectionHeading.d.ts +11 -0
  299. package/dist/components/Spacer.d.ts +7 -0
  300. package/dist/components/StatGroup.d.ts +12 -0
  301. package/dist/components/Steps.d.ts +12 -0
  302. package/dist/components/Tabs.d.ts +15 -0
  303. package/dist/components/TeamGrid.d.ts +30 -0
  304. package/dist/components/Testimonial.d.ts +9 -0
  305. package/dist/components/TestimonialGrid.d.ts +13 -0
  306. package/dist/components/ToneBadge.d.ts +18 -0
  307. package/dist/components/Toolbar.d.ts +11 -0
  308. package/dist/components/UsageMeter.d.ts +21 -0
  309. package/dist/components/VideoEmbed.d.ts +16 -0
  310. package/dist/components/assetImage.d.ts +16 -0
  311. package/dist/components/assetResolver.d.ts +57 -0
  312. package/dist/components/blockLink.d.ts +26 -0
  313. package/dist/components/editorial/ArticleList.d.ts +32 -0
  314. package/dist/components/editorial/Bands.d.ts +29 -0
  315. package/dist/components/editorial/EditorialHeading.d.ts +17 -0
  316. package/dist/components/editorial/EditorialHero.d.ts +46 -0
  317. package/dist/components/editorial/EditorialSection.d.ts +47 -0
  318. package/dist/components/editorial/Eyebrow.d.ts +14 -0
  319. package/dist/components/editorial/PanelGrid.d.ts +45 -0
  320. package/dist/components/editorial/ProfileCard.d.ts +27 -0
  321. package/dist/components/editorial/PullQuote.d.ts +17 -0
  322. package/dist/components/editorial/Reveal.d.ts +15 -0
  323. package/dist/components/editorial/index.d.ts +12 -0
  324. package/dist/components/editorial/lists.d.ts +42 -0
  325. package/dist/components/editorial/tone.d.ts +33 -0
  326. package/dist/components/tones.d.ts +14 -0
  327. package/dist/connect/index.cjs +65 -0
  328. package/dist/connect/index.cjs.map +1 -0
  329. package/dist/connect/index.d.ts +8 -0
  330. package/dist/connect/index.js +28 -0
  331. package/dist/connect/index.js.map +1 -0
  332. package/dist/connection/ArbiAppProvider.d.ts +16 -0
  333. package/dist/connection/ConnectionProvider.d.ts +50 -0
  334. package/dist/connection/ai.d.ts +99 -0
  335. package/dist/connection/types.d.ts +54 -0
  336. package/dist/connection/useImageGen.d.ts +22 -0
  337. package/dist/facts/FactComposer.d.ts +75 -0
  338. package/dist/facts/FactDateInput.d.ts +34 -0
  339. package/dist/facts/FactLabelsCell.d.ts +35 -0
  340. package/dist/facts/FactPassages.d.ts +52 -0
  341. package/dist/facts/FactRowActions.d.ts +18 -0
  342. package/dist/facts/FactSearchParts.d.ts +80 -0
  343. package/dist/facts/FactSourceGroup.d.ts +26 -0
  344. package/dist/facts/FactTimelineViews.d.ts +37 -0
  345. package/dist/facts/FactsBulkBar.d.ts +53 -0
  346. package/dist/facts/FactsEmptyState.d.ts +38 -0
  347. package/dist/facts/FactsFieldsButton.d.ts +21 -0
  348. package/dist/facts/FactsFilterInput.d.ts +46 -0
  349. package/dist/facts/FactsTable.d.ts +28 -0
  350. package/dist/facts/FactsToolbar.d.ts +53 -0
  351. package/dist/facts/FactsViewToggle.d.ts +38 -0
  352. package/dist/facts/fact-date.d.ts +22 -0
  353. package/dist/facts/fact-row.d.ts +32 -0
  354. package/dist/facts/fact-text.d.ts +14 -0
  355. package/dist/facts/factColumns.d.ts +88 -0
  356. package/dist/facts/index.cjs +2401 -0
  357. package/dist/facts/index.cjs.map +1 -0
  358. package/dist/facts/index.d.ts +35 -0
  359. package/dist/facts/index.js +2343 -0
  360. package/dist/facts/index.js.map +1 -0
  361. package/dist/facts/timezone.d.ts +4 -0
  362. package/dist/files/AiReviewDialog.d.ts +77 -0
  363. package/dist/files/DocStatusIcon.d.ts +12 -0
  364. package/dist/files/DocumentGrid.d.ts +27 -0
  365. package/dist/files/Explorer.d.ts +10 -0
  366. package/dist/files/ExplorerSurface.d.ts +34 -0
  367. package/dist/files/ExplorerTile.d.ts +142 -0
  368. package/dist/files/ExportParts.d.ts +114 -0
  369. package/dist/files/FieldParts.d.ts +64 -0
  370. package/dist/files/FilesPage.d.ts +20 -0
  371. package/dist/files/FilesToolbar.d.ts +59 -0
  372. package/dist/files/FolderPicker.d.ts +82 -0
  373. package/dist/files/GridView.d.ts +59 -0
  374. package/dist/files/HtmlViewerParts.d.ts +183 -0
  375. package/dist/files/MoveToFolderDialog.d.ts +32 -0
  376. package/dist/files/RedlineParts.d.ts +72 -0
  377. package/dist/files/SelectionBar.d.ts +65 -0
  378. package/dist/files/StatusPill.d.ts +23 -0
  379. package/dist/files/TagPassagePanel.d.ts +60 -0
  380. package/dist/files/UploadDropzone.d.ts +33 -0
  381. package/dist/files/UploadFooter.d.ts +59 -0
  382. package/dist/files/UploadParts.d.ts +78 -0
  383. package/dist/files/UploadSources.d.ts +77 -0
  384. package/dist/files/VersionTimeline.d.ts +60 -0
  385. package/dist/files/ViewToggle.d.ts +53 -0
  386. package/dist/files/ViewerChrome.d.ts +152 -0
  387. package/dist/files/ViewerLayout.d.ts +32 -0
  388. package/dist/files/ViewerParts.d.ts +62 -0
  389. package/dist/files/bulk/BulkActionsBar.d.ts +76 -0
  390. package/dist/files/bulk/FolderFilterButton.d.ts +35 -0
  391. package/dist/files/bulk/index.d.ts +2 -0
  392. package/dist/files/cells/DocumentNameCell.d.ts +31 -0
  393. package/dist/files/cells/FolderCell.d.ts +25 -0
  394. package/dist/files/cells/HoverIconCell.d.ts +21 -0
  395. package/dist/files/cells/LabelMultiSelect.d.ts +50 -0
  396. package/dist/files/cells/RelatedDocsCell.d.ts +22 -0
  397. package/dist/files/cells/TagColumnParts.d.ts +29 -0
  398. package/dist/files/cells/index.d.ts +6 -0
  399. package/dist/files/documentGridLayout.d.ts +31 -0
  400. package/dist/files/grid-test-ids.d.ts +18 -0
  401. package/dist/files/import/CalendarImportRow.d.ts +26 -0
  402. package/dist/files/import/ImportParts.d.ts +114 -0
  403. package/dist/files/import/TaskSection.d.ts +51 -0
  404. package/dist/files/import/UrlInputDialogView.d.ts +63 -0
  405. package/dist/files/import/VendorLogo.d.ts +15 -0
  406. package/dist/files/import/index.d.ts +5 -0
  407. package/dist/files/index.cjs +7555 -0
  408. package/dist/files/index.cjs.map +1 -0
  409. package/dist/files/index.d.ts +34 -0
  410. package/dist/files/index.js +7340 -0
  411. package/dist/files/index.js.map +1 -0
  412. package/dist/files/page/FilesAddMenu.d.ts +30 -0
  413. package/dist/files/page/FilesPageParts.d.ts +24 -0
  414. package/dist/files/page/FilesPageTitle.d.ts +24 -0
  415. package/dist/files/page/index.d.ts +3 -0
  416. package/dist/files/tags/BulkTagEditor.d.ts +136 -0
  417. package/dist/files/tags/DocTagPopover.d.ts +113 -0
  418. package/dist/files/tags/TagManager.d.ts +54 -0
  419. package/dist/files/tags/index.d.ts +3 -0
  420. package/dist/files/views/FieldsEditorParts.d.ts +151 -0
  421. package/dist/files/views/FieldsManagerParts.d.ts +17 -0
  422. package/dist/files/views/TagFormParts.d.ts +109 -0
  423. package/dist/files/views/ViewSelectorMenu.d.ts +66 -0
  424. package/dist/files/views/fieldsEditorModel.d.ts +43 -0
  425. package/dist/files/views/index.d.ts +5 -0
  426. package/dist/home/ActionMenuDialog.d.ts +26 -0
  427. package/dist/home/AddFilesFirst.d.ts +22 -0
  428. package/dist/home/CardHeaderBar.d.ts +40 -0
  429. package/dist/home/CollapsibleSection.d.ts +43 -0
  430. package/dist/home/CreateTile.d.ts +17 -0
  431. package/dist/home/DashboardParts.d.ts +32 -0
  432. package/dist/home/FactsStrip.d.ts +41 -0
  433. package/dist/home/FoldSection.d.ts +19 -0
  434. package/dist/home/GettingStartedStrip.d.ts +49 -0
  435. package/dist/home/Greeting.d.ts +18 -0
  436. package/dist/home/HomeLayout.d.ts +28 -0
  437. package/dist/home/KeyValueRow.d.ts +43 -0
  438. package/dist/home/ProjectBandParts.d.ts +70 -0
  439. package/dist/home/QuickActionDialogHeader.d.ts +21 -0
  440. package/dist/home/RecentList.d.ts +48 -0
  441. package/dist/home/ScopePicker.d.ts +65 -0
  442. package/dist/home/StartTiles.d.ts +33 -0
  443. package/dist/home/index.cjs +854 -0
  444. package/dist/home/index.cjs.map +1 -0
  445. package/dist/home/index.d.ts +37 -0
  446. package/dist/home/index.js +763 -0
  447. package/dist/home/index.js.map +1 -0
  448. package/dist/home/sections.d.ts +33 -0
  449. package/dist/index.cjs +1992 -5547
  450. package/dist/index.cjs.map +1 -1
  451. package/dist/index.d.ts +72 -1716
  452. package/dist/index.js +142 -5316
  453. package/dist/index.js.map +1 -1
  454. package/dist/lib/balanced-columns.d.ts +8 -0
  455. package/dist/lib/format.d.ts +37 -0
  456. package/dist/lib/observe-resize.d.ts +6 -0
  457. package/dist/lib/password-strength.d.ts +24 -0
  458. package/dist/lib/sidebar-mode.d.ts +46 -0
  459. package/dist/lib/testid.d.ts +7 -0
  460. package/dist/lib/use-drawer.d.ts +11 -0
  461. package/dist/lib/useControllableState.d.ts +5 -0
  462. package/dist/lib/useDismissibleCard.d.ts +15 -0
  463. package/dist/lib/useListKeyNav.d.ts +42 -0
  464. package/dist/lib/useLongPaste.d.ts +15 -0
  465. package/dist/lib/useRestoreFocus.d.ts +11 -0
  466. package/dist/lib/useScopeSelection.d.ts +34 -0
  467. package/dist/nav/types.d.ts +19 -0
  468. package/dist/provider/BlocksProvider.d.ts +20 -0
  469. package/dist/provider/TooltipScope.d.ts +9 -0
  470. package/dist/record/index.cjs +2 -4
  471. package/dist/record/index.cjs.map +1 -1
  472. package/dist/record/index.d.ts +12 -16
  473. package/dist/record/index.js +2 -4
  474. package/dist/record/index.js.map +1 -1
  475. package/dist/retrieval/HighlightText.d.ts +21 -0
  476. package/dist/retrieval/SearchEmptyState.d.ts +39 -0
  477. package/dist/retrieval/SearchGroupHeader.d.ts +21 -0
  478. package/dist/retrieval/SearchHit.d.ts +36 -0
  479. package/dist/retrieval/SearchPage.d.ts +187 -0
  480. package/dist/retrieval/SearchPagination.d.ts +21 -0
  481. package/dist/retrieval/highlight.d.ts +21 -0
  482. package/dist/retrieval/index.cjs +160 -0
  483. package/dist/retrieval/index.cjs.map +1 -0
  484. package/dist/retrieval/index.d.ts +7 -0
  485. package/dist/retrieval/index.js +7 -0
  486. package/dist/retrieval/index.js.map +1 -0
  487. package/dist/settings/AccountDialogs.d.ts +89 -0
  488. package/dist/settings/AvatarPicker.d.ts +25 -0
  489. package/dist/settings/Billing.d.ts +113 -0
  490. package/dist/settings/ConfigSwitcherBar.d.ts +46 -0
  491. package/dist/settings/ConfirmDialog.d.ts +28 -0
  492. package/dist/settings/ConnectionCard.d.ts +58 -0
  493. package/dist/settings/ConversationSettingsParts.d.ts +53 -0
  494. package/dist/settings/DocumentPicker.d.ts +57 -0
  495. package/dist/settings/EmailWorkspaceDialogView.d.ts +60 -0
  496. package/dist/settings/ErrorDialog.d.ts +36 -0
  497. package/dist/settings/FloatingPanel.d.ts +32 -0
  498. package/dist/settings/JsonTree.d.ts +24 -0
  499. package/dist/settings/LogoutDialog.d.ts +51 -0
  500. package/dist/settings/ModalBreadcrumbHeader.d.ts +32 -0
  501. package/dist/settings/ModalScaffold.d.ts +60 -0
  502. package/dist/settings/MultiSelect.d.ts +111 -0
  503. package/dist/settings/NotificationCategoryList.d.ts +26 -0
  504. package/dist/settings/PanelFrame.d.ts +47 -0
  505. package/dist/settings/PasswordChangeDialog.d.ts +64 -0
  506. package/dist/settings/PlanBadge.d.ts +14 -0
  507. package/dist/settings/PlanCard.d.ts +97 -0
  508. package/dist/settings/PremiumModelsFrame.d.ts +17 -0
  509. package/dist/settings/ProfileCard.d.ts +67 -0
  510. package/dist/settings/Projects.d.ts +108 -0
  511. package/dist/settings/QuotaMeter.d.ts +31 -0
  512. package/dist/settings/ReferralCredit.d.ts +46 -0
  513. package/dist/settings/SegmentedControl.d.ts +20 -0
  514. package/dist/settings/ServerConnectionControls.d.ts +31 -0
  515. package/dist/settings/SettingsPanel.d.ts +61 -0
  516. package/dist/settings/SettingsSwitchRow.d.ts +21 -0
  517. package/dist/settings/SubscriptionParts.d.ts +215 -0
  518. package/dist/settings/SupportPanel.d.ts +32 -0
  519. package/dist/settings/SyncDialogParts.d.ts +155 -0
  520. package/dist/settings/ThreadPreviewDialog.d.ts +18 -0
  521. package/dist/settings/TipsEditorDialog.d.ts +94 -0
  522. package/dist/settings/TriStateFilterMenu.d.ts +66 -0
  523. package/dist/settings/Usage.d.ts +102 -0
  524. package/dist/settings/WelcomeDialog.d.ts +79 -0
  525. package/dist/settings/index.cjs +5131 -0
  526. package/dist/settings/index.cjs.map +1 -0
  527. package/dist/settings/index.d.ts +38 -0
  528. package/dist/settings/index.js +4922 -0
  529. package/dist/settings/index.js.map +1 -0
  530. package/dist/shell/ActionCluster.d.ts +33 -0
  531. package/dist/shell/AskField.d.ts +76 -0
  532. package/dist/shell/InfoBar.d.ts +20 -0
  533. package/dist/shell/MobileBars.d.ts +47 -0
  534. package/dist/shell/OfflinePill.d.ts +28 -0
  535. package/dist/shell/RemoteControlPill.d.ts +23 -0
  536. package/dist/shell/Shell.d.ts +123 -0
  537. package/dist/shell/Sidebar.d.ts +104 -0
  538. package/dist/shell/SidebarBadgeRow.d.ts +29 -0
  539. package/dist/shell/SidebarRowAction.d.ts +24 -0
  540. package/dist/shell/StatusRow.d.ts +20 -0
  541. package/dist/shell/TabHeader.d.ts +70 -0
  542. package/dist/shell/TipCard.d.ts +46 -0
  543. package/dist/shell/ToolButton.d.ts +12 -0
  544. package/dist/shell/Workspace.d.ts +90 -0
  545. package/dist/shell/index.cjs +187 -0
  546. package/dist/shell/index.cjs.map +1 -0
  547. package/dist/shell/index.d.ts +20 -0
  548. package/dist/shell/index.js +10 -0
  549. package/dist/shell/index.js.map +1 -0
  550. package/dist/shell/sidebar-context.d.ts +19 -0
  551. package/dist/shell/types.d.ts +48 -0
  552. package/dist/site/index.cjs +173 -90
  553. package/dist/site/index.d.ts +60 -4
  554. package/dist/site/index.js +8 -1
  555. package/dist/studio/PageRenderer.d.ts +35 -0
  556. package/dist/studio/Section.d.ts +24 -0
  557. package/dist/studio/StudioEditor.d.ts +32 -0
  558. package/dist/studio/aiFields.d.ts +17 -0
  559. package/dist/studio/blockKit.d.ts +36 -0
  560. package/dist/studio/blocksConfig.d.ts +42 -0
  561. package/dist/studio/imageFields.d.ts +14 -0
  562. package/dist/studio/index.cjs +3353 -0
  563. package/dist/studio/index.cjs.map +1 -0
  564. package/dist/studio/index.d.ts +32 -0
  565. package/dist/studio/index.js +3297 -0
  566. package/dist/studio/index.js.map +1 -0
  567. package/dist/studio/liveData.d.ts +4 -0
  568. package/dist/studio/liveWidgetRuntime.d.ts +41 -0
  569. package/dist/studio/persistence.d.ts +38 -0
  570. package/dist/studio/publish.d.ts +39 -0
  571. package/dist/studio/siteBlocks.d.ts +14 -0
  572. package/dist/studio/types.d.ts +46 -0
  573. package/dist/studio/websiteBlocks.d.ts +4 -0
  574. package/dist/studio/widgets.d.ts +20 -0
  575. package/dist/theme/ThemeEditor.d.ts +16 -0
  576. package/dist/theme/fontPairings.d.ts +48 -0
  577. package/dist/theme/store.d.ts +53 -0
  578. package/dist/theme/tokens.d.ts +105 -0
  579. package/dist/timeline/Timeline.d.ts +27 -0
  580. package/dist/timeline/TimelineCalendar.d.ts +54 -0
  581. package/dist/timeline/TimelineGantt.d.ts +71 -0
  582. package/dist/timeline/TimelineZoomControl.d.ts +17 -0
  583. package/dist/timeline/index.cjs +50 -0
  584. package/dist/timeline/index.cjs.map +1 -0
  585. package/dist/timeline/index.d.ts +13 -0
  586. package/dist/timeline/index.js +5 -0
  587. package/dist/timeline/index.js.map +1 -0
  588. package/dist/timeline/span.d.ts +29 -0
  589. package/dist/timeline/types.d.ts +30 -0
  590. package/dist/workspaces/WorkspaceBadges.d.ts +27 -0
  591. package/dist/workspaces/WorkspaceCard.d.ts +142 -0
  592. package/dist/workspaces/WorkspaceFormParts.d.ts +98 -0
  593. package/dist/workspaces/WorkspaceGroup.d.ts +31 -0
  594. package/dist/workspaces/WorkspaceSettingsParts.d.ts +70 -0
  595. package/dist/workspaces/index.cjs +690 -0
  596. package/dist/workspaces/index.cjs.map +1 -0
  597. package/dist/workspaces/index.d.ts +9 -0
  598. package/dist/workspaces/index.js +661 -0
  599. package/dist/workspaces/index.js.map +1 -0
  600. package/package.json +154 -9
  601. package/src/blocks.css +365 -0
  602. package/src/components/grid.css +60 -107
  603. package/src/source.css +20 -0
  604. package/src/studio/studio.css +3 -3
  605. package/src/tokens.css +226 -0
  606. package/dist/chunk-DBAUZD7A.cjs.map +0 -1
  607. package/dist/chunk-FZ2S6VAZ.js.map +0 -1
  608. package/dist/index-DduoEosi.d.cts +0 -846
  609. package/dist/index-DduoEosi.d.ts +0 -846
  610. package/dist/index.d.cts +0 -1717
  611. package/dist/record/index.d.cts +0 -96
  612. package/dist/site/index.d.cts +0 -4
@@ -0,0 +1,3297 @@
1
+ "use client";
2
+ import { PageHeader, MetricCard, DataTableBlock, StatGroup } from '../chunk-2ORA62OF.js';
3
+ import { assetRefId, applyStoredTheme, AssetResolverProvider, ThemeEditor, applyThemeVars, tid, SectionHeading, PricingTable, Tabs as Tabs$1, Accordion, Gallery, VideoEmbed, ContactForm, Banner, MediaText, TeamGrid, Steps, TestimonialGrid, Columns, Spacer, Container, Hero, FeatureGrid, Testimonial, CTABanner, LogoCloud, FAQ, RichTextBlock, BlockImage, AvatarBlock, Navbar, Footer, ListBlock, isAssetRef, asAssetRef } from '../chunk-6P6AEQJB.js';
4
+ export { ASSET_REF_PREFIX, AssetResolverProvider, asAssetRef, assetRefId, isAssetRef, useAssetRenderer, useAssetResolverActive } from '../chunk-6P6AEQJB.js';
5
+ import { Callout } from '../chunk-UJE32SST.js';
6
+ import { Timeline } from '../chunk-OVEQERPO.js';
7
+ import { EmptyState } from '../chunk-P6GHJSLR.js';
8
+ import '../chunk-S7PSMMRG.js';
9
+ import { useConnectedAiTask, useConnection } from '../chunk-YBXCZEWS.js';
10
+ import '../chunk-4EZVCGRL.js';
11
+ import '../chunk-WW6EOVZA.js';
12
+ import { useBlocksEnvironment } from '../chunk-76JDDMSP.js';
13
+ import { MessageSquare, Cpu, Code, Cloud, Layers, Target, Award, MapPin, Phone, Mail, Lock, Globe, Heart, Star, Rocket, Shield, Zap, Sparkles, Gavel, Scale, Bell, Activity, BarChart3, DollarSign, TrendingUp, Info, AlertCircle, CheckCircle, Clock, Calendar, Briefcase, Users, Folder, FileText, ArrowLeft, Palette, Loader2, RotateCcw, ChevronUp, ChevronDown, BrainCircuit, Bot, LayoutGrid, FileJson, Download, Crop, ImagePlus, Upload, Link2, Check, MessagesSquare } from 'lucide-react';
14
+ import { useArbi, useThumbnails, useConfigs, useAgents, useWorkspaceDocs, useAiTask, useDocuments, ImageCropModal, DEFAULT_ASPECT_OPTIONS, useConversations } from '@arbidocs/react';
15
+ import { cn, Button, Card, CardHeader, CardTitle, CardContent, Badge, Switch, Checkbox, Tabs, TabsList, TabsTrigger, TabsContent, Separator } from '@arbidocs/react/ui';
16
+ import { createContext, useContext, useEffect, useMemo, useState, useRef, useCallback } from 'react';
17
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
18
+ import { Link, useParams } from 'react-router-dom';
19
+ import { Puck, Render } from '@measured/puck';
20
+ import { useQuery, useQueryClient } from '@tanstack/react-query';
21
+ import { create } from 'zustand';
22
+ import { persist } from 'zustand/middleware';
23
+
24
+ var LiveDataContext = createContext(false);
25
+ function useLiveDataActive() {
26
+ return useContext(LiveDataContext);
27
+ }
28
+ function LiveDataProvider({ children }) {
29
+ return /* @__PURE__ */ jsx(LiveDataContext.Provider, { value: true, children });
30
+ }
31
+ function WidgetPlaceholder({
32
+ label,
33
+ icon: Icon = Sparkles,
34
+ testId
35
+ }) {
36
+ return /* @__PURE__ */ jsxs(
37
+ "div",
38
+ {
39
+ className: "flex items-center gap-3 rounded-lg bg-muted px-5 py-6 text-sm text-muted-foreground",
40
+ "data-testid": testId,
41
+ children: [
42
+ /* @__PURE__ */ jsx("span", { className: "flex size-9 shrink-0 items-center justify-center rounded-full bg-background text-muted-foreground", children: /* @__PURE__ */ jsx(Icon, { className: "size-4" }) }),
43
+ /* @__PURE__ */ jsxs("span", { children: [
44
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: label }),
45
+ /* @__PURE__ */ jsx("span", { className: "ml-1", children: "\xB7 live in the app" })
46
+ ] })
47
+ ]
48
+ }
49
+ );
50
+ }
51
+ function useActiveWorkspaceId() {
52
+ const { workspaceId, fallbackWorkspaceId } = useConnection();
53
+ return workspaceId ?? fallbackWorkspaceId ?? void 0;
54
+ }
55
+ function timeAgo(iso) {
56
+ if (!iso) return "";
57
+ const then = new Date(iso).getTime();
58
+ if (Number.isNaN(then)) return "";
59
+ const s = Math.max(0, Math.round((Date.now() - then) / 1e3));
60
+ if (s < 60) return "just now";
61
+ const m = Math.round(s / 60);
62
+ if (m < 60) return `${m}m ago`;
63
+ const h = Math.round(m / 60);
64
+ if (h < 24) return `${h}h ago`;
65
+ return `${Math.round(h / 24)}d ago`;
66
+ }
67
+ var METRIC_SOURCES = {
68
+ documents: { label: "Documents", icon: FileText },
69
+ conversations: { label: "Agent threads", icon: Bot }
70
+ };
71
+ var asMetricSource = (s) => s in METRIC_SOURCES ? s : "documents";
72
+ function AgentActivityLive({
73
+ title,
74
+ limit,
75
+ testId
76
+ }) {
77
+ const wsId = useActiveWorkspaceId();
78
+ const { data, isLoading } = useConversations(wsId);
79
+ const rows = [...data ?? []].sort((a, b) => +new Date(b.updated_at ?? 0) - +new Date(a.updated_at ?? 0)).slice(0, limit);
80
+ return /* @__PURE__ */ jsxs(Card, { variant: "elevated", className: "rounded-lg p-5", "data-testid": testId, children: [
81
+ /* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center gap-2", children: [
82
+ /* @__PURE__ */ jsx(Bot, { className: "size-4 text-muted-foreground" }),
83
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: title })
84
+ ] }),
85
+ isLoading ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", "data-testid": `${testId}-loading`, children: "Loading agent threads\u2026" }) : rows.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", "data-testid": `${testId}-empty`, children: "No agent threads yet." }) : /* @__PURE__ */ jsx("ul", { className: "space-y-2", "data-testid": `${testId}-list`, children: rows.map((c, i) => /* @__PURE__ */ jsxs(
86
+ "li",
87
+ {
88
+ className: "flex items-center justify-between gap-3",
89
+ "data-testid": `${testId}-item-${i}`,
90
+ children: [
91
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 truncate", children: [
92
+ /* @__PURE__ */ jsx(MessagesSquare, { className: "size-3.5 shrink-0 text-muted-foreground" }),
93
+ /* @__PURE__ */ jsx("span", { className: "truncate text-sm text-foreground", children: c.title?.trim() || "Untitled thread" })
94
+ ] }),
95
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: timeAgo(c.updated_at) })
96
+ ]
97
+ },
98
+ c.external_id ?? i
99
+ )) })
100
+ ] });
101
+ }
102
+ function LiveMetricInner({
103
+ label,
104
+ source,
105
+ accent,
106
+ testId
107
+ }) {
108
+ const wsId = useActiveWorkspaceId();
109
+ const docs = useDocuments(wsId, { enabled: source === "documents" });
110
+ const convos = useConversations(wsId, { enabled: source === "conversations" });
111
+ const query = source === "documents" ? docs : convos;
112
+ const count = Array.isArray(query.data) ? query.data.length : 0;
113
+ return /* @__PURE__ */ jsx(
114
+ MetricCard,
115
+ {
116
+ label: label || METRIC_SOURCES[source].label,
117
+ value: query.isLoading ? "\u2014" : String(count),
118
+ accent,
119
+ icon: METRIC_SOURCES[source].icon,
120
+ testId
121
+ }
122
+ );
123
+ }
124
+ function AgentActivityBlock({
125
+ title,
126
+ limit,
127
+ testId
128
+ }) {
129
+ return useLiveDataActive() ? /* @__PURE__ */ jsx(AgentActivityLive, { title, limit, testId }) : /* @__PURE__ */ jsx(WidgetPlaceholder, { label: String(title || "Agent activity"), icon: Bot, testId });
130
+ }
131
+ function LiveMetricBlock({
132
+ label,
133
+ source,
134
+ accent,
135
+ testId
136
+ }) {
137
+ const src = asMetricSource(source);
138
+ return useLiveDataActive() ? /* @__PURE__ */ jsx(LiveMetricInner, { label, source: src, accent, testId }) : /* @__PURE__ */ jsx(
139
+ WidgetPlaceholder,
140
+ {
141
+ label: String(label || METRIC_SOURCES[src].label),
142
+ icon: METRIC_SOURCES[src].icon,
143
+ testId
144
+ }
145
+ );
146
+ }
147
+ function createLiveWidgets(kit) {
148
+ const { blockTestId } = kit;
149
+ const AgentActivityWidget = {
150
+ label: "Agent activity (live)",
151
+ fields: {
152
+ title: { type: "text", label: "Title" },
153
+ limit: { type: "number", label: "Rows", min: 1, max: 8 }
154
+ },
155
+ defaultProps: { title: "Recent agent activity", limit: 4 },
156
+ render: ({ id, title, limit }) => /* @__PURE__ */ jsx(AgentActivityBlock, { title, limit, testId: blockTestId(id) })
157
+ };
158
+ const LiveMetricWidget = {
159
+ label: "Live metric",
160
+ fields: {
161
+ label: { type: "text", label: "Label" },
162
+ source: {
163
+ type: "select",
164
+ label: "Source",
165
+ options: [
166
+ { label: "Documents", value: "documents" },
167
+ { label: "Agent threads", value: "conversations" }
168
+ ]
169
+ },
170
+ accent: {
171
+ type: "radio",
172
+ label: "Accent",
173
+ options: [
174
+ { label: "Default", value: "default" },
175
+ { label: "Primary", value: "primary" },
176
+ { label: "Success", value: "success" }
177
+ ]
178
+ }
179
+ },
180
+ defaultProps: { label: "", source: "documents", accent: "primary" },
181
+ render: ({ id, label, source, accent }) => /* @__PURE__ */ jsx(LiveMetricBlock, { label, source, accent, testId: blockTestId(id) })
182
+ };
183
+ return {
184
+ AgentActivity: AgentActivityWidget,
185
+ LiveMetric: LiveMetricWidget
186
+ };
187
+ }
188
+ function useAssetImage(docId) {
189
+ const arbi = useArbi();
190
+ const { data: thumbs } = useThumbnails([docId]);
191
+ const thumb = thumbs?.[docId];
192
+ const { data: objectUrl } = useQuery({
193
+ queryKey: ["arbi", "asset-image", docId],
194
+ queryFn: async () => {
195
+ const res = await arbi.documents.download(docId);
196
+ const blob = await res.blob();
197
+ return URL.createObjectURL(blob);
198
+ },
199
+ // Only download when the thumbnail is genuinely absent (not merely loading).
200
+ enabled: !!docId && thumbs != null && !thumb && arbi.hasWorkspace,
201
+ staleTime: 10 * 6e4
202
+ });
203
+ useEffect(() => {
204
+ return () => {
205
+ if (objectUrl) URL.revokeObjectURL(objectUrl);
206
+ };
207
+ }, [objectUrl]);
208
+ return thumb ?? objectUrl;
209
+ }
210
+ function AssetThumb({ docId, alt, className, testId }) {
211
+ const src = useAssetImage(docId);
212
+ return /* @__PURE__ */ jsx("img", { src: src ?? void 0, alt: alt ?? "", className, "data-testid": testId });
213
+ }
214
+
215
+ // src/studio/publish.ts
216
+ function blobToDataUrl(blob) {
217
+ return new Promise((resolve, reject) => {
218
+ const reader = new FileReader();
219
+ reader.onload = () => resolve(reader.result);
220
+ reader.onerror = () => reject(reader.error);
221
+ reader.readAsDataURL(blob);
222
+ });
223
+ }
224
+ function createArbiMaterializer(arbi, opts = {}) {
225
+ return async (docId) => {
226
+ try {
227
+ const res = await arbi.documents.download(docId);
228
+ const blob = await res.blob();
229
+ if (opts.publishAsset) return await opts.publishAsset(docId, blob);
230
+ return await blobToDataUrl(blob);
231
+ } catch {
232
+ return null;
233
+ }
234
+ };
235
+ }
236
+ function collectAssetRefs(node, acc) {
237
+ if (typeof node === "string") {
238
+ if (isAssetRef(node)) acc.add(node);
239
+ return;
240
+ }
241
+ if (Array.isArray(node)) {
242
+ for (const child of node) collectAssetRefs(child, acc);
243
+ return;
244
+ }
245
+ if (node && typeof node === "object") {
246
+ for (const value of Object.values(node)) collectAssetRefs(value, acc);
247
+ }
248
+ }
249
+ function replaceAssetRefs(node, replacements) {
250
+ if (typeof node === "string") {
251
+ return replacements.get(node) ?? node;
252
+ }
253
+ if (Array.isArray(node)) {
254
+ return node.map((child) => replaceAssetRefs(child, replacements));
255
+ }
256
+ if (node && typeof node === "object") {
257
+ const out = {};
258
+ for (const [key, value] of Object.entries(node)) {
259
+ out[key] = replaceAssetRefs(value, replacements);
260
+ }
261
+ return out;
262
+ }
263
+ return node;
264
+ }
265
+ async function materializePageData(data, materialize) {
266
+ const refs = /* @__PURE__ */ new Set();
267
+ collectAssetRefs(data, refs);
268
+ if (refs.size === 0) return data;
269
+ const replacements = /* @__PURE__ */ new Map();
270
+ await Promise.all(
271
+ [...refs].map(async (ref) => {
272
+ const src = await materialize(assetRefId(ref));
273
+ if (src) replacements.set(ref, src);
274
+ })
275
+ );
276
+ if (replacements.size === 0) return data;
277
+ return replaceAssetRefs(data, replacements);
278
+ }
279
+ var EMPTY_DATA = { content: [], root: {} };
280
+ function StudioEditor({
281
+ config,
282
+ pages,
283
+ persistence,
284
+ themeBundle,
285
+ defaultTheme,
286
+ brandLabel,
287
+ backHref,
288
+ backLabel = "Back",
289
+ live,
290
+ initialSlug,
291
+ testIdPrefix = "studio",
292
+ publishAsset,
293
+ resetSeam = false
294
+ }) {
295
+ const arbi = useArbi();
296
+ const { notify } = useBlocksEnvironment();
297
+ const materialize = useMemo(
298
+ () => createArbiMaterializer(arbi, { publishAsset }),
299
+ [arbi, publishAsset]
300
+ );
301
+ const [slug, setSlug] = useState(initialSlug ?? pages[0]?.slug ?? "home");
302
+ const [data, setData] = useState(EMPTY_DATA);
303
+ const [rev, setRev] = useState(0);
304
+ const [loading, setLoading] = useState(true);
305
+ const [themeOpen, setThemeOpen] = useState(false);
306
+ const [via, setVia] = useState(null);
307
+ const [saving, setSaving] = useState(false);
308
+ const [loadError, setLoadError] = useState(null);
309
+ const latest = useRef(EMPTY_DATA);
310
+ useEffect(() => {
311
+ let cancelled = false;
312
+ setLoading(true);
313
+ setLoadError(null);
314
+ void (async () => {
315
+ let loaded;
316
+ try {
317
+ loaded = (await persistence.loadPage(arbi, live, slug)).data;
318
+ } catch (e) {
319
+ if (cancelled) return;
320
+ const message = e instanceof Error ? e.message : "Could not load the page";
321
+ setLoadError(message);
322
+ notify(message, "error");
323
+ setLoading(false);
324
+ return;
325
+ }
326
+ if (cancelled) return;
327
+ const next = loaded ?? EMPTY_DATA;
328
+ setData(next);
329
+ latest.current = next;
330
+ setRev((r) => r + 1);
331
+ setLoading(false);
332
+ })();
333
+ return () => {
334
+ cancelled = true;
335
+ };
336
+ }, [slug, live]);
337
+ useEffect(() => {
338
+ if (!resetSeam) return;
339
+ const w = window;
340
+ w.__studioResetSlug = (s) => persistence.deletePage(arbi, live, s);
341
+ return () => {
342
+ delete w.__studioResetSlug;
343
+ };
344
+ }, [resetSeam, arbi, live, persistence]);
345
+ const persist3 = useCallback(
346
+ async (toSave) => {
347
+ setSaving(true);
348
+ try {
349
+ const where = await persistence.savePage(arbi, live, slug, toSave);
350
+ const published = await materializePageData(toSave, materialize);
351
+ await persistence.savePublished(arbi, live, slug, published);
352
+ setVia(where);
353
+ } catch (e) {
354
+ notify(e instanceof Error ? e.message : "Could not save the page", "error");
355
+ } finally {
356
+ setSaving(false);
357
+ }
358
+ },
359
+ [arbi, live, slug, persistence, materialize, notify]
360
+ );
361
+ useEffect(() => {
362
+ let cancelled = false;
363
+ void (async () => {
364
+ let stored;
365
+ try {
366
+ stored = (await persistence.loadTheme(arbi, live)).data;
367
+ } catch (e) {
368
+ notify(e instanceof Error ? e.message : "Could not load the theme", "error");
369
+ return;
370
+ }
371
+ if (cancelled || !stored) return;
372
+ themeBundle.useStore.setState({
373
+ colors: { ...defaultTheme.colors, ...stored.colors },
374
+ fonts: stored.fonts ?? defaultTheme.fonts,
375
+ radius: stored.radius ?? defaultTheme.radius,
376
+ activePresetId: null
377
+ });
378
+ applyStoredTheme(themeBundle);
379
+ })();
380
+ return () => {
381
+ cancelled = true;
382
+ };
383
+ }, [arbi, live]);
384
+ const saveThemeToArbi = useCallback(async () => {
385
+ const snapshot = themeBundle.useStore.getState().snapshot();
386
+ try {
387
+ return await persistence.saveTheme(arbi, live, snapshot);
388
+ } catch (e) {
389
+ notify(e instanceof Error ? e.message : "Could not save the theme", "error");
390
+ throw e;
391
+ }
392
+ }, [arbi, live, persistence, themeBundle, notify]);
393
+ return /* @__PURE__ */ jsxs("div", { className: "al-studio", "data-testid": "page-studio", children: [
394
+ /* @__PURE__ */ jsxs("header", { className: "flex items-center gap-3 border-b border-border bg-card px-4 py-2", children: [
395
+ backHref && /* @__PURE__ */ jsxs(Fragment, { children: [
396
+ /* @__PURE__ */ jsxs(
397
+ Link,
398
+ {
399
+ to: backHref,
400
+ className: "inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-sm text-muted-foreground hover:text-primary",
401
+ "data-testid": `${testIdPrefix}-back`,
402
+ children: [
403
+ /* @__PURE__ */ jsx(ArrowLeft, { className: "size-4" }),
404
+ " ",
405
+ backLabel
406
+ ]
407
+ }
408
+ ),
409
+ /* @__PURE__ */ jsx("span", { className: "h-4 w-px bg-border" })
410
+ ] }),
411
+ /* @__PURE__ */ jsx("span", { className: "font-display text-sm font-semibold text-foreground", children: brandLabel }),
412
+ /* @__PURE__ */ jsx(
413
+ "select",
414
+ {
415
+ value: slug,
416
+ onChange: (e) => setSlug(e.target.value),
417
+ className: "rounded border border-border bg-background px-2 py-1 text-sm",
418
+ "data-testid": `${testIdPrefix}-slug`,
419
+ children: pages.map((p) => /* @__PURE__ */ jsx("option", { value: p.slug, children: p.label }, p.slug))
420
+ }
421
+ ),
422
+ /* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-2", children: [
423
+ via && /* @__PURE__ */ jsxs(
424
+ "span",
425
+ {
426
+ className: "text-xs text-muted-foreground",
427
+ "data-testid": `${testIdPrefix}-save-via`,
428
+ children: [
429
+ "saved via ",
430
+ via
431
+ ]
432
+ }
433
+ ),
434
+ /* @__PURE__ */ jsxs(
435
+ "button",
436
+ {
437
+ type: "button",
438
+ onClick: () => setThemeOpen((o) => !o),
439
+ className: "inline-flex items-center gap-1.5 rounded-md border border-border px-3 py-1.5 text-sm font-medium text-foreground hover:border-primary hover:text-primary",
440
+ "data-testid": `${testIdPrefix}-theme-toggle`,
441
+ children: [
442
+ /* @__PURE__ */ jsx(Palette, { className: "size-4" }),
443
+ " Theme"
444
+ ]
445
+ }
446
+ ),
447
+ /* @__PURE__ */ jsxs(
448
+ "button",
449
+ {
450
+ type: "button",
451
+ onClick: () => void persist3(latest.current),
452
+ disabled: saving || loading || loadError !== null,
453
+ className: "inline-flex items-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-60",
454
+ "data-testid": `${testIdPrefix}-publish`,
455
+ children: [
456
+ saving ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx(Rocket, { className: "size-4" }),
457
+ saving ? "Publishing\u2026" : "Publish"
458
+ ]
459
+ }
460
+ )
461
+ ] })
462
+ ] }),
463
+ /* @__PURE__ */ jsxs("div", { className: "al-studio__puck relative flex", children: [
464
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: loadError ? /* @__PURE__ */ jsx(
465
+ "div",
466
+ {
467
+ role: "alert",
468
+ className: "flex h-full items-center justify-center p-6 text-sm text-destructive",
469
+ "data-testid": `${testIdPrefix}-load-error`,
470
+ children: loadError
471
+ }
472
+ ) : loading ? /* @__PURE__ */ jsxs(
473
+ "div",
474
+ {
475
+ className: "flex h-full items-center justify-center text-sm text-muted-foreground",
476
+ "data-testid": `${testIdPrefix}-loading`,
477
+ children: [
478
+ /* @__PURE__ */ jsx(Loader2, { className: "mr-2 size-4 animate-spin" }),
479
+ " Loading \u201C",
480
+ slug,
481
+ "\u201D\u2026"
482
+ ]
483
+ }
484
+ ) : /* @__PURE__ */ jsx(AssetResolverProvider, { render: AssetThumb, children: /* @__PURE__ */ jsx(
485
+ Puck,
486
+ {
487
+ config,
488
+ data,
489
+ onChange: (d) => {
490
+ latest.current = d;
491
+ },
492
+ onPublish: (d) => void persist3(d)
493
+ },
494
+ `${slug}-${rev}`
495
+ ) }) }),
496
+ themeOpen && /* @__PURE__ */ jsx(
497
+ "aside",
498
+ {
499
+ className: "w-80 shrink-0 border-l border-border",
500
+ "data-testid": `${testIdPrefix}-theme-drawer`,
501
+ children: /* @__PURE__ */ jsx(
502
+ ThemeEditor,
503
+ {
504
+ bundle: themeBundle,
505
+ variant: "drawer",
506
+ testIdPrefix: `${testIdPrefix}-theme`,
507
+ onSave: saveThemeToArbi
508
+ }
509
+ )
510
+ }
511
+ )
512
+ ] })
513
+ ] });
514
+ }
515
+ function PageRenderer({
516
+ config,
517
+ persistence,
518
+ defaultTheme,
519
+ live,
520
+ slug: slugProp,
521
+ emptyState,
522
+ testIdPrefix = "studio",
523
+ liveData = false,
524
+ initialData,
525
+ applyTheme = true
526
+ }) {
527
+ const params = useParams();
528
+ const slug = slugProp ?? params.slug ?? "home";
529
+ const arbi = useArbi();
530
+ const [data, setData] = useState(initialData ?? null);
531
+ const [loading, setLoading] = useState(!initialData);
532
+ const [error, setError] = useState(null);
533
+ useEffect(() => {
534
+ let cancelled = false;
535
+ setLoading(!initialData);
536
+ setError(null);
537
+ void (async () => {
538
+ try {
539
+ const theme = applyTheme ? (await persistence.loadTheme(arbi, live)).data : void 0;
540
+ if (cancelled) return;
541
+ const page = initialData ?? (await persistence.loadPublished(arbi, live, slug)).data ?? (await persistence.loadPage(arbi, live, slug)).data;
542
+ if (cancelled) return;
543
+ if (applyTheme) {
544
+ applyThemeVars(
545
+ theme ? { ...defaultTheme, ...theme, colors: { ...defaultTheme.colors, ...theme.colors } } : defaultTheme
546
+ );
547
+ }
548
+ setData(page);
549
+ setLoading(false);
550
+ } catch (e) {
551
+ if (cancelled) return;
552
+ setError(e instanceof Error ? e.message : "Could not load the page");
553
+ setLoading(false);
554
+ }
555
+ })();
556
+ return () => {
557
+ cancelled = true;
558
+ };
559
+ }, [arbi, live, slug, persistence, defaultTheme, initialData, applyTheme]);
560
+ if (loading) {
561
+ return /* @__PURE__ */ jsx(
562
+ "div",
563
+ {
564
+ className: "flex min-h-[60vh] items-center justify-center text-sm text-muted-foreground",
565
+ "data-testid": `${testIdPrefix}-render-loading`,
566
+ children: "Loading page\u2026"
567
+ }
568
+ );
569
+ }
570
+ if (error) {
571
+ return /* @__PURE__ */ jsx(
572
+ "div",
573
+ {
574
+ role: "alert",
575
+ className: "mx-auto max-w-2xl px-5 py-24 text-center text-sm text-destructive",
576
+ "data-testid": `${testIdPrefix}-render-error`,
577
+ children: error
578
+ }
579
+ );
580
+ }
581
+ if (!data || data.content.length === 0) {
582
+ const title = emptyState?.title ?? "Nothing published yet";
583
+ const body = emptyState?.body ?? `Publish the \u201C${slug}\u201D page to see it here.`;
584
+ return /* @__PURE__ */ jsxs(
585
+ "div",
586
+ {
587
+ className: "mx-auto max-w-2xl px-5 py-24 text-center",
588
+ "data-testid": `${testIdPrefix}-render-empty`,
589
+ children: [
590
+ /* @__PURE__ */ jsx("h1", { className: "font-display text-2xl font-semibold text-foreground", children: title }),
591
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: body })
592
+ ]
593
+ }
594
+ );
595
+ }
596
+ const rendered = /* @__PURE__ */ jsx(Render, { config, data });
597
+ return /* @__PURE__ */ jsx("main", { "data-testid": `page-${testIdPrefix}-render`, children: liveData ? /* @__PURE__ */ jsx(LiveDataProvider, { children: rendered }) : rendered });
598
+ }
599
+
600
+ // src/studio/persistence.ts
601
+ var StudioPersistenceError = class extends Error {
602
+ constructor(message, options) {
603
+ super(message, options);
604
+ this.name = "StudioPersistenceError";
605
+ }
606
+ };
607
+ function createStudioPersistence(cfg) {
608
+ const WORKSPACE_ID = cfg.workspaceId;
609
+ const FOLDER = cfg.folder ?? "site";
610
+ const MODE = cfg.localStorageMode ?? "off";
611
+ function scoped(name) {
612
+ const scope = cfg.storageScope?.();
613
+ if (!scope) {
614
+ throw new StudioPersistenceError(
615
+ "Studio local storage needs a user/workspace scope (storageScope) before it can be used"
616
+ );
617
+ }
618
+ return `${cfg.storagePrefix}${scope}:${name}`;
619
+ }
620
+ const pageKey = (slug) => scoped(slug);
621
+ const publishedKey = (slug) => scoped(`published-${slug}`);
622
+ const themeKey = () => scoped(cfg.themeStorageKey ?? "theme");
623
+ const localAllowed = (arbiFailed) => MODE === "always" || MODE === "fallback" && arbiFailed;
624
+ function unavailable(what, cause) {
625
+ return new StudioPersistenceError(
626
+ `${what}: no ARBI session or the ARBI request failed, and local storage is not enabled`,
627
+ { cause }
628
+ );
629
+ }
630
+ const THEME_FILE = cfg.themeFileName ?? "theme.json";
631
+ const pageFile = cfg.pageFileName ?? ((slug) => `${slug}.json`);
632
+ const publishedFile = cfg.publishedFileName ?? ((slug) => `${slug}.published.json`);
633
+ function canUseArbi(arbi, live) {
634
+ return live && !!arbi;
635
+ }
636
+ async function uploadJson(arbi, fileName, obj) {
637
+ await arbi.selectWorkspace(WORKSPACE_ID);
638
+ const blob = new Blob([JSON.stringify(obj, null, 2)], { type: "application/json" });
639
+ await arbi.documents.uploadFile(blob, fileName, { folder: FOLDER });
640
+ }
641
+ async function downloadJson(arbi, fileName) {
642
+ await arbi.selectWorkspace(WORKSPACE_ID);
643
+ const docs = await arbi.documents.list();
644
+ const match = docs.filter((d) => (d.file_name ?? "").endsWith(fileName) && (d.folder ?? "").includes(FOLDER)).sort((a, b) => a.created_at < b.created_at ? 1 : -1)[0];
645
+ if (!match) return null;
646
+ try {
647
+ const res = await arbi.documents.download(match.external_id);
648
+ return JSON.parse(await res.text());
649
+ } catch {
650
+ const parsed = await arbi.documents.getParsedContent(match.external_id, "content");
651
+ const raw = typeof parsed?.content === "string" ? parsed.content : JSON.stringify(parsed);
652
+ return JSON.parse(raw);
653
+ }
654
+ }
655
+ function writeLocal(key, data) {
656
+ try {
657
+ localStorage.setItem(key, JSON.stringify(data));
658
+ } catch (e) {
659
+ throw new StudioPersistenceError("Could not write to local storage", { cause: e });
660
+ }
661
+ }
662
+ async function writePage(arbi, live, kind, localKey, fileName, data) {
663
+ let failure;
664
+ if (canUseArbi(arbi, live)) {
665
+ try {
666
+ await uploadJson(arbi, fileName, data);
667
+ if (localAllowed(false)) writeLocal(localKey(), data);
668
+ return "arbi";
669
+ } catch (e) {
670
+ if (e instanceof StudioPersistenceError) throw e;
671
+ failure = e;
672
+ }
673
+ }
674
+ if (!localAllowed(true)) throw unavailable(`Cannot save ${kind} "${fileName}"`, failure);
675
+ writeLocal(localKey(), data);
676
+ return "local";
677
+ }
678
+ async function readPage(arbi, live, localKey, fileName) {
679
+ let arbiTried = false;
680
+ if (canUseArbi(arbi, live)) {
681
+ arbiTried = true;
682
+ try {
683
+ const data = await downloadJson(arbi, fileName);
684
+ if (data) return { data, via: "arbi" };
685
+ } catch (e) {
686
+ if (MODE === "off") throw unavailable(`Cannot load "${fileName}"`, e);
687
+ }
688
+ }
689
+ if (MODE === "off") {
690
+ if (arbiTried) return { data: null, via: "arbi" };
691
+ throw unavailable(`Cannot load "${fileName}"`);
692
+ }
693
+ return readLocal(localKey());
694
+ }
695
+ function readLocal(key) {
696
+ const raw = safeLocalGet(key);
697
+ return { data: raw ? JSON.parse(raw) : null, via: "local" };
698
+ }
699
+ function savePage(arbi, live, slug, data) {
700
+ return writePage(arbi, live, "page", () => pageKey(slug), pageFile(slug), data);
701
+ }
702
+ function loadPage(arbi, live, slug) {
703
+ return readPage(arbi, live, () => pageKey(slug), pageFile(slug));
704
+ }
705
+ function savePublished(arbi, live, slug, data) {
706
+ return writePage(
707
+ arbi,
708
+ live,
709
+ "published page",
710
+ () => publishedKey(slug),
711
+ publishedFile(slug),
712
+ data
713
+ );
714
+ }
715
+ function loadPublished(arbi, live, slug) {
716
+ return readPage(arbi, live, () => publishedKey(slug), publishedFile(slug));
717
+ }
718
+ async function deletePage(arbi, live, slug) {
719
+ if (MODE !== "off") {
720
+ try {
721
+ localStorage.removeItem(pageKey(slug));
722
+ localStorage.removeItem(publishedKey(slug));
723
+ } catch (e) {
724
+ if (e instanceof StudioPersistenceError) throw e;
725
+ }
726
+ }
727
+ if (!canUseArbi(arbi, live)) return;
728
+ try {
729
+ await arbi.selectWorkspace(WORKSPACE_ID);
730
+ const docs = await arbi.documents.list();
731
+ const names = [pageFile(slug), publishedFile(slug)];
732
+ const ids = docs.filter(
733
+ (d) => (d.folder ?? "").includes(FOLDER) && names.some((n) => (d.file_name ?? "").endsWith(n))
734
+ ).map((d) => d.external_id);
735
+ if (ids.length) await arbi.documents.delete(ids);
736
+ } catch (e) {
737
+ throw new StudioPersistenceError(`Could not delete the stored page "${slug}" from ARBI`, {
738
+ cause: e
739
+ });
740
+ }
741
+ }
742
+ async function saveTheme(arbi, live, theme) {
743
+ return writePage(arbi, live, "theme", themeKey, THEME_FILE, theme);
744
+ }
745
+ async function loadTheme(arbi, live) {
746
+ return readPage(arbi, live, themeKey, THEME_FILE);
747
+ }
748
+ function safeLocalGet(key) {
749
+ try {
750
+ return localStorage.getItem(key);
751
+ } catch {
752
+ return null;
753
+ }
754
+ }
755
+ return { savePage, loadPage, savePublished, loadPublished, deletePage, saveTheme, loadTheme };
756
+ }
757
+ function AiTextField({
758
+ value,
759
+ onChange,
760
+ name,
761
+ label,
762
+ multiline,
763
+ placeholder,
764
+ kind,
765
+ workspaceId,
766
+ buildPrompt,
767
+ testIdPrefix
768
+ }) {
769
+ const { docIds } = useWorkspaceDocs(workspaceId);
770
+ const task = useAiTask();
771
+ const [active, setActive] = useState(false);
772
+ const activeRef = useRef(false);
773
+ useEffect(() => {
774
+ if (activeRef.current && task.text) onChange(task.text);
775
+ }, [task.text, onChange]);
776
+ useEffect(() => {
777
+ if (active && !task.isRunning) {
778
+ activeRef.current = false;
779
+ setActive(false);
780
+ }
781
+ }, [active, task.isRunning]);
782
+ const write = async () => {
783
+ if (task.isRunning) return;
784
+ activeRef.current = true;
785
+ setActive(true);
786
+ await task.run(buildPrompt({ kind, label, current: value ?? "" }), docIds);
787
+ };
788
+ const inputTid = tid(`${testIdPrefix}-field`, name);
789
+ return /* @__PURE__ */ jsxs("div", { className: "al-ai-field", children: [
790
+ multiline ? /* @__PURE__ */ jsx(
791
+ "textarea",
792
+ {
793
+ className: "al-ai-field__input",
794
+ rows: 5,
795
+ value: value ?? "",
796
+ placeholder,
797
+ onChange: (e) => onChange(e.target.value),
798
+ "data-testid": inputTid
799
+ }
800
+ ) : /* @__PURE__ */ jsx(
801
+ "input",
802
+ {
803
+ className: "al-ai-field__input",
804
+ value: value ?? "",
805
+ placeholder,
806
+ onChange: (e) => onChange(e.target.value),
807
+ "data-testid": inputTid
808
+ }
809
+ ),
810
+ /* @__PURE__ */ jsxs(
811
+ "button",
812
+ {
813
+ type: "button",
814
+ className: "al-ai-field__btn",
815
+ onClick: () => void write(),
816
+ disabled: task.isRunning,
817
+ "data-testid": tid(`${testIdPrefix}-ai-write`, name),
818
+ title: "Draft this field with AI, grounded in the marketing workspace",
819
+ children: [
820
+ task.isRunning ? /* @__PURE__ */ jsx(Loader2, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsx(Sparkles, { className: "size-3" }),
821
+ task.isRunning ? "Writing\u2026" : "Write with AI"
822
+ ]
823
+ }
824
+ )
825
+ ] });
826
+ }
827
+ function createAiFields(cfg) {
828
+ const { workspaceId, buildPrompt } = cfg;
829
+ const defaultKind = cfg.defaultKind ?? "copy";
830
+ const testIdPrefix = cfg.testIdPrefix ?? "studio";
831
+ const aiText = (label, placeholder) => ({
832
+ type: "custom",
833
+ label,
834
+ render: ({ value, onChange, name }) => /* @__PURE__ */ jsx(
835
+ AiTextField,
836
+ {
837
+ value: value ?? "",
838
+ onChange,
839
+ name,
840
+ label,
841
+ placeholder,
842
+ kind: defaultKind,
843
+ workspaceId,
844
+ buildPrompt,
845
+ testIdPrefix
846
+ }
847
+ )
848
+ });
849
+ const aiTextarea = (label, placeholder, kind = defaultKind) => ({
850
+ type: "custom",
851
+ label,
852
+ render: ({ value, onChange, name }) => /* @__PURE__ */ jsx(
853
+ AiTextField,
854
+ {
855
+ value: value ?? "",
856
+ onChange,
857
+ name,
858
+ label,
859
+ placeholder,
860
+ kind,
861
+ workspaceId,
862
+ buildPrompt,
863
+ testIdPrefix,
864
+ multiline: true
865
+ }
866
+ )
867
+ });
868
+ return { aiText, aiTextarea };
869
+ }
870
+ function latestImageDocId(artifacts) {
871
+ for (let i = artifacts.length - 1; i >= 0; i--) {
872
+ if (artifacts[i].mode === "image") return artifacts[i].doc_ext_id;
873
+ }
874
+ return null;
875
+ }
876
+ function useImageGen() {
877
+ const task = useConnectedAiTask();
878
+ const { run } = task;
879
+ const generate = useCallback(
880
+ async (prompt, referenceDocIds = []) => {
881
+ const trimmed = prompt.trim();
882
+ if (!trimmed) return null;
883
+ const result = await run(`/image ${trimmed}`, referenceDocIds);
884
+ return latestImageDocId(result?.artifacts ?? []);
885
+ },
886
+ [run]
887
+ );
888
+ const docExtId = useMemo(() => latestImageDocId(task.artifacts), [task.artifacts]);
889
+ return {
890
+ docExtId,
891
+ status: task.status,
892
+ error: task.error,
893
+ isGenerating: task.isRunning,
894
+ generate,
895
+ reset: task.reset
896
+ };
897
+ }
898
+ function toJpgName(name) {
899
+ return name.replace(/\.[^.]+$/, "") + ".jpg";
900
+ }
901
+ var IMAGE_EXT = /\.(png|jpe?g|webp|gif|avif|svg)$/i;
902
+ function ImageFieldControl({
903
+ value,
904
+ onChange,
905
+ name,
906
+ workspaceId,
907
+ folder,
908
+ testIdPrefix
909
+ }) {
910
+ const [mode, setMode] = useState("asset");
911
+ const [url, setUrl] = useState("");
912
+ const [prompt, setPrompt] = useState("");
913
+ const [busy, setBusy] = useState(false);
914
+ const [refIds, setRefIds] = useState([]);
915
+ const [cropSrc, setCropSrc] = useState(null);
916
+ const [cropName, setCropName] = useState("image.jpg");
917
+ const arbi = useArbi();
918
+ const gen = useImageGen();
919
+ const queryClient = useQueryClient();
920
+ const currentAssetImage = useAssetImage(isAssetRef(value) ? assetRefId(value) : "");
921
+ const tp = (...q) => tid(`${testIdPrefix}-image`, name, ...q);
922
+ const refreshAssets = () => queryClient.invalidateQueries({ queryKey: ["arbi", "documents", workspaceId] });
923
+ const { data: docs } = useDocuments(workspaceId);
924
+ const imageDocs = useMemo(
925
+ () => (docs ?? []).filter(
926
+ (d) => d.file_name && IMAGE_EXT.test(d.file_name)
927
+ ),
928
+ [docs]
929
+ );
930
+ const imageIds = useMemo(
931
+ () => imageDocs.map((d) => d.external_id),
932
+ [imageDocs]
933
+ );
934
+ const { data: thumbs } = useThumbnails(imageIds);
935
+ const onUpload = (e) => {
936
+ const file = e.target.files?.[0];
937
+ if (!file) return;
938
+ setCropName(toJpgName(file.name));
939
+ setCropSrc(URL.createObjectURL(file));
940
+ e.target.value = "";
941
+ };
942
+ const openReframe = () => {
943
+ if (!currentAssetImage) return;
944
+ setCropName("reframe.jpg");
945
+ setCropSrc(currentAssetImage);
946
+ };
947
+ const uploadBlobAsAsset = async (blob, name2) => {
948
+ setBusy(true);
949
+ try {
950
+ await arbi.selectWorkspace(workspaceId);
951
+ const file = new File([blob], name2, { type: blob.type });
952
+ const res = await arbi.documents.uploadFile(file, name2, { folder });
953
+ const id = res.doc_ext_ids?.[0];
954
+ if (id) {
955
+ onChange(asAssetRef(id));
956
+ void refreshAssets();
957
+ }
958
+ } finally {
959
+ setBusy(false);
960
+ }
961
+ };
962
+ const onCropComplete = async (result) => {
963
+ const src = cropSrc;
964
+ setCropSrc(null);
965
+ await uploadBlobAsAsset(result.blob, cropName);
966
+ if (src?.startsWith("blob:")) URL.revokeObjectURL(src);
967
+ };
968
+ const toggleRef = (id) => setRefIds((prev) => prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]);
969
+ const onGenerate = async () => {
970
+ if (!prompt.trim() || gen.isGenerating) return;
971
+ const id = await gen.generate(prompt, refIds);
972
+ if (id) {
973
+ onChange(asAssetRef(id));
974
+ setRefIds([]);
975
+ void refreshAssets();
976
+ }
977
+ };
978
+ const MODES = [
979
+ { key: "asset", label: "Assets", icon: ImagePlus },
980
+ { key: "upload", label: "Upload", icon: Upload },
981
+ { key: "url", label: "URL", icon: Link2 },
982
+ { key: "generate", label: "Generate", icon: Sparkles }
983
+ ];
984
+ const inputClass = "w-full rounded-md border border-border bg-card px-2.5 py-1.5 text-sm text-foreground outline-none focus:border-primary";
985
+ const tabClass = (on) => `inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs font-medium ${on ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground"}`;
986
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", "data-testid": tp(), children: [
987
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
988
+ /* @__PURE__ */ jsx(BlockImage, { src: value, aspect: "16:9", rounded: "md", testId: tp("preview") }),
989
+ currentAssetImage ? /* @__PURE__ */ jsxs(
990
+ "button",
991
+ {
992
+ type: "button",
993
+ onClick: openReframe,
994
+ className: "absolute right-1.5 top-1.5 inline-flex items-center gap-1 rounded-md bg-background/85 px-2 py-1 text-xs font-medium text-foreground shadow-sm backdrop-blur hover:bg-background",
995
+ "data-testid": tp("reframe"),
996
+ children: [
997
+ /* @__PURE__ */ jsx(Crop, { className: "size-3" }),
998
+ " Crop"
999
+ ]
1000
+ }
1001
+ ) : null
1002
+ ] }),
1003
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: MODES.map((m) => /* @__PURE__ */ jsxs(
1004
+ "button",
1005
+ {
1006
+ type: "button",
1007
+ className: tabClass(mode === m.key),
1008
+ onClick: () => setMode(m.key),
1009
+ "data-testid": tp("mode", m.key),
1010
+ children: [
1011
+ /* @__PURE__ */ jsx(m.icon, { className: "size-3" }),
1012
+ " ",
1013
+ m.label
1014
+ ]
1015
+ },
1016
+ m.key
1017
+ )) }),
1018
+ mode === "asset" && /* @__PURE__ */ jsxs("div", { className: "grid max-h-48 grid-cols-3 gap-1.5 overflow-auto", "data-testid": tp("assets"), children: [
1019
+ imageDocs.length === 0 && /* @__PURE__ */ jsx("p", { className: "col-span-3 py-3 text-center text-xs text-muted-foreground", children: "No image assets yet \u2014 upload or generate one." }),
1020
+ imageDocs.map((d) => {
1021
+ const thumb = thumbs?.[d.external_id];
1022
+ const selected = value === asAssetRef(d.external_id);
1023
+ return /* @__PURE__ */ jsx(
1024
+ "button",
1025
+ {
1026
+ type: "button",
1027
+ onClick: () => onChange(asAssetRef(d.external_id)),
1028
+ className: `overflow-hidden rounded border ${selected ? "border-primary ring-1 ring-primary" : "border-border"}`,
1029
+ title: d.file_name ?? "",
1030
+ "data-testid": tp("asset", d.external_id),
1031
+ children: thumb ? /* @__PURE__ */ jsx(
1032
+ "img",
1033
+ {
1034
+ src: thumb,
1035
+ alt: d.file_name ?? "",
1036
+ className: "aspect-square w-full bg-muted object-contain"
1037
+ }
1038
+ ) : /* @__PURE__ */ jsx("span", { className: "flex aspect-square w-full items-center justify-center bg-muted text-muted-foreground", children: /* @__PURE__ */ jsx(ImagePlus, { className: "size-4" }) })
1039
+ },
1040
+ d.external_id
1041
+ );
1042
+ })
1043
+ ] }),
1044
+ mode === "upload" && /* @__PURE__ */ jsxs("label", { className: "inline-flex cursor-pointer items-center gap-2 rounded-md border border-border px-2.5 py-1.5 text-sm text-foreground", children: [
1045
+ busy ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx(Upload, { className: "size-4" }),
1046
+ busy ? "Uploading\u2026" : "Choose a file",
1047
+ /* @__PURE__ */ jsx(
1048
+ "input",
1049
+ {
1050
+ type: "file",
1051
+ accept: "image/*",
1052
+ className: "hidden",
1053
+ disabled: busy,
1054
+ onChange: (e) => void onUpload(e),
1055
+ "data-testid": tp("upload")
1056
+ }
1057
+ )
1058
+ ] }),
1059
+ mode === "url" && /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5", children: [
1060
+ /* @__PURE__ */ jsx(
1061
+ "input",
1062
+ {
1063
+ className: inputClass,
1064
+ placeholder: "https://\u2026",
1065
+ value: url,
1066
+ onChange: (e) => setUrl(e.target.value),
1067
+ "data-testid": tp("url")
1068
+ }
1069
+ ),
1070
+ /* @__PURE__ */ jsx(
1071
+ "button",
1072
+ {
1073
+ type: "button",
1074
+ className: "rounded-md bg-primary px-2.5 py-1.5 text-sm font-medium text-primary-foreground disabled:opacity-50",
1075
+ disabled: !url.trim(),
1076
+ onClick: () => onChange(url.trim()),
1077
+ "data-testid": tp("url-apply"),
1078
+ children: "Use"
1079
+ }
1080
+ )
1081
+ ] }),
1082
+ mode === "generate" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
1083
+ imageDocs.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", "data-testid": tp("refs"), children: [
1084
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1085
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
1086
+ "Reference images",
1087
+ refIds.length > 0 ? ` (${refIds.length})` : " (optional)"
1088
+ ] }),
1089
+ isAssetRef(value) && /* @__PURE__ */ jsx(
1090
+ "button",
1091
+ {
1092
+ type: "button",
1093
+ className: "text-xs font-medium text-primary hover:underline",
1094
+ onClick: () => toggleRef(assetRefId(value)),
1095
+ "data-testid": tp("vary-current"),
1096
+ children: refIds.includes(assetRefId(value)) ? "Varying current" : "Vary current image"
1097
+ }
1098
+ )
1099
+ ] }),
1100
+ /* @__PURE__ */ jsx("div", { className: "grid max-h-28 grid-cols-4 gap-1.5 overflow-auto", children: imageDocs.map((d) => {
1101
+ const thumb = thumbs?.[d.external_id];
1102
+ const on = refIds.includes(d.external_id);
1103
+ return /* @__PURE__ */ jsxs(
1104
+ "button",
1105
+ {
1106
+ type: "button",
1107
+ onClick: () => toggleRef(d.external_id),
1108
+ "aria-pressed": on,
1109
+ className: `relative overflow-hidden rounded border ${on ? "border-primary ring-1 ring-primary" : "border-border"}`,
1110
+ title: d.file_name ?? "",
1111
+ "data-testid": tp("ref", d.external_id),
1112
+ children: [
1113
+ thumb ? /* @__PURE__ */ jsx(
1114
+ "img",
1115
+ {
1116
+ src: thumb,
1117
+ alt: d.file_name ?? "",
1118
+ className: "aspect-square w-full bg-muted object-contain"
1119
+ }
1120
+ ) : /* @__PURE__ */ jsx("span", { className: "flex aspect-square w-full items-center justify-center bg-muted text-muted-foreground", children: /* @__PURE__ */ jsx(ImagePlus, { className: "size-3.5" }) }),
1121
+ on && /* @__PURE__ */ jsx("span", { className: "absolute right-0.5 top-0.5 rounded-full bg-primary p-0.5 text-primary-foreground", children: /* @__PURE__ */ jsx(Check, { className: "size-2.5" }) })
1122
+ ]
1123
+ },
1124
+ d.external_id
1125
+ );
1126
+ }) })
1127
+ ] }),
1128
+ /* @__PURE__ */ jsx(
1129
+ "textarea",
1130
+ {
1131
+ className: inputClass,
1132
+ rows: 2,
1133
+ placeholder: refIds.length > 0 ? "Describe the edit to apply\u2026" : "Describe the image\u2026",
1134
+ value: prompt,
1135
+ onChange: (e) => setPrompt(e.target.value),
1136
+ "data-testid": tp("prompt")
1137
+ }
1138
+ ),
1139
+ /* @__PURE__ */ jsxs(
1140
+ "button",
1141
+ {
1142
+ type: "button",
1143
+ className: "inline-flex items-center gap-1.5 self-start rounded-md bg-primary px-2.5 py-1.5 text-sm font-medium text-primary-foreground disabled:opacity-50",
1144
+ disabled: !prompt.trim() || gen.isGenerating,
1145
+ onClick: () => void onGenerate(),
1146
+ "data-testid": tp("generate"),
1147
+ children: [
1148
+ gen.isGenerating ? /* @__PURE__ */ jsx(Loader2, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Sparkles, { className: "size-3.5" }),
1149
+ gen.isGenerating ? "Generating\u2026" : refIds.length > 0 ? `Generate from ${refIds.length} reference${refIds.length > 1 ? "s" : ""}` : "Generate"
1150
+ ]
1151
+ }
1152
+ ),
1153
+ gen.error && /* @__PURE__ */ jsx("span", { className: "text-xs text-destructive", role: "alert", children: gen.error })
1154
+ ] }),
1155
+ value && /* @__PURE__ */ jsx(
1156
+ "button",
1157
+ {
1158
+ type: "button",
1159
+ className: "self-start text-xs text-muted-foreground hover:text-foreground",
1160
+ onClick: () => onChange(""),
1161
+ "data-testid": tp("clear"),
1162
+ children: "Clear"
1163
+ }
1164
+ ),
1165
+ /* @__PURE__ */ jsx(
1166
+ ImageCropModal,
1167
+ {
1168
+ open: !!cropSrc,
1169
+ image: cropSrc,
1170
+ title: "Frame your image",
1171
+ aspectOptions: DEFAULT_ASPECT_OPTIONS,
1172
+ cropShape: "rect",
1173
+ output: { maxSize: 1600 },
1174
+ onComplete: onCropComplete,
1175
+ onCancel: () => setCropSrc(null)
1176
+ }
1177
+ )
1178
+ ] });
1179
+ }
1180
+ function createImageField(cfg) {
1181
+ const { workspaceId } = cfg;
1182
+ const folder = cfg.folder ?? "assets";
1183
+ const testIdPrefix = cfg.testIdPrefix ?? "studio";
1184
+ const imageField = (label) => ({
1185
+ type: "custom",
1186
+ label,
1187
+ render: ({ value, onChange, name }) => /* @__PURE__ */ jsx(
1188
+ ImageFieldControl,
1189
+ {
1190
+ value: value ?? "",
1191
+ onChange,
1192
+ name,
1193
+ workspaceId,
1194
+ folder,
1195
+ testIdPrefix
1196
+ }
1197
+ )
1198
+ });
1199
+ return { imageField };
1200
+ }
1201
+ var TEXTURE_STYLE = {
1202
+ backgroundImage: "radial-gradient(currentColor 0.5px, transparent 0.5px)",
1203
+ backgroundSize: "4px 4px",
1204
+ opacity: 0.04
1205
+ };
1206
+ var PADDING = {
1207
+ none: "",
1208
+ sm: "py-8 lg:py-10",
1209
+ md: "py-16 lg:py-20",
1210
+ lg: "py-24 lg:py-28"
1211
+ };
1212
+ var WIDTH = {
1213
+ narrow: "max-w-3xl",
1214
+ default: "max-w-7xl",
1215
+ wide: "max-w-[90rem]",
1216
+ full: "max-w-none"
1217
+ };
1218
+ var INVERTED_VARS = {
1219
+ ["--color-background"]: "var(--color-foreground)",
1220
+ ["--color-foreground"]: "var(--color-background)",
1221
+ ["--color-card"]: "color-mix(in srgb, var(--color-foreground) 92%, #fff)",
1222
+ ["--color-muted"]: "color-mix(in srgb, var(--color-foreground) 85%, #fff)",
1223
+ ["--color-muted-foreground"]: "color-mix(in srgb, var(--color-background) 60%, transparent)",
1224
+ ["--color-border"]: "color-mix(in srgb, var(--color-background) 18%, transparent)",
1225
+ ["--color-accent"]: "color-mix(in srgb, var(--color-foreground) 80%, #fff)"
1226
+ };
1227
+ function Section({
1228
+ children,
1229
+ tone = "default",
1230
+ padding = "md",
1231
+ maxWidth = "default",
1232
+ texture,
1233
+ className = ""
1234
+ }) {
1235
+ const inverted = tone === "ink";
1236
+ const toneClass = inverted ? "bg-background text-foreground" : tone === "muted" ? "border-y border-border bg-card/40" : "";
1237
+ return /* @__PURE__ */ jsxs("section", { className: cn("relative", toneClass), style: inverted ? INVERTED_VARS : void 0, children: [
1238
+ texture && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0", style: TEXTURE_STYLE, "aria-hidden": true }),
1239
+ /* @__PURE__ */ jsx(
1240
+ "div",
1241
+ {
1242
+ className: cn(
1243
+ "relative mx-auto px-5 lg:px-8",
1244
+ WIDTH[maxWidth],
1245
+ PADDING[padding],
1246
+ className
1247
+ ),
1248
+ children
1249
+ }
1250
+ )
1251
+ ] });
1252
+ }
1253
+ var iconMap = {
1254
+ FileText: FileText,
1255
+ Folder,
1256
+ Users,
1257
+ Briefcase,
1258
+ Calendar,
1259
+ Clock,
1260
+ CheckCircle,
1261
+ AlertCircle,
1262
+ Info,
1263
+ TrendingUp,
1264
+ DollarSign,
1265
+ BarChart3,
1266
+ Activity,
1267
+ Bell,
1268
+ Scale,
1269
+ Gavel,
1270
+ Sparkles: Sparkles,
1271
+ // Marketing set
1272
+ Zap,
1273
+ Shield,
1274
+ Rocket: Rocket,
1275
+ Star,
1276
+ Heart,
1277
+ Globe,
1278
+ Lock,
1279
+ Mail,
1280
+ Phone,
1281
+ MapPin,
1282
+ Award,
1283
+ Target,
1284
+ Layers,
1285
+ Cloud,
1286
+ Code,
1287
+ Cpu,
1288
+ MessageSquare
1289
+ };
1290
+ var ICON_OPTIONS = [
1291
+ { label: "None", value: "" },
1292
+ ...Object.keys(iconMap).map((name) => ({ label: name, value: name }))
1293
+ ];
1294
+ function createBlockKit(ai, image, eyebrowClassName) {
1295
+ return {
1296
+ eyebrowClassName,
1297
+ textField: (label, placeholder) => ai ? ai.aiText(label, placeholder) : { type: "text", label, placeholder },
1298
+ textareaField: (label, placeholder, kind) => ai ? ai.aiTextarea(label, placeholder, kind) : { type: "textarea", label, placeholder },
1299
+ imageField: (label) => image ? image(label) : { type: "text", label },
1300
+ boolRadio: (label) => ({
1301
+ type: "radio",
1302
+ label,
1303
+ options: [
1304
+ { label: "No", value: false },
1305
+ { label: "Yes", value: true }
1306
+ ]
1307
+ }),
1308
+ iconField: (label = "Icon") => ({ type: "select", label, options: ICON_OPTIONS }),
1309
+ resolveIcon: (name) => name ? iconMap[name] : void 0,
1310
+ blockTestId: (id) => tid("arbi-block", id)
1311
+ };
1312
+ }
1313
+ var emptySlot = [];
1314
+ var renderSlot = (slot) => {
1315
+ if (typeof slot !== "function") return null;
1316
+ const Slot = slot;
1317
+ return /* @__PURE__ */ jsx(Slot, {});
1318
+ };
1319
+ var toneField = {
1320
+ type: "radio",
1321
+ label: "Tone",
1322
+ options: [
1323
+ { label: "Default", value: "default" },
1324
+ { label: "Muted", value: "muted" },
1325
+ { label: "Ink", value: "ink" }
1326
+ ]
1327
+ };
1328
+ function createWebsiteBlocks(kit) {
1329
+ const { textField, textareaField, imageField, boolRadio, iconField, resolveIcon, blockTestId } = kit;
1330
+ const ctaField = (label) => ({
1331
+ type: "object",
1332
+ label,
1333
+ objectFields: {
1334
+ label: textField("Label"),
1335
+ href: { type: "text", label: "Link" }
1336
+ }
1337
+ });
1338
+ const ColumnsBlock = {
1339
+ label: "Columns",
1340
+ fields: {
1341
+ count: {
1342
+ type: "radio",
1343
+ label: "Columns",
1344
+ options: [
1345
+ { label: "2", value: 2 },
1346
+ { label: "3", value: 3 },
1347
+ { label: "4", value: 4 }
1348
+ ]
1349
+ },
1350
+ gap: {
1351
+ type: "radio",
1352
+ label: "Gap",
1353
+ options: [
1354
+ { label: "S", value: "sm" },
1355
+ { label: "M", value: "md" },
1356
+ { label: "L", value: "lg" }
1357
+ ]
1358
+ },
1359
+ col1: { type: "slot" },
1360
+ col2: { type: "slot" },
1361
+ col3: { type: "slot" },
1362
+ col4: { type: "slot" }
1363
+ },
1364
+ defaultProps: {
1365
+ count: 3,
1366
+ gap: "md",
1367
+ col1: emptySlot,
1368
+ col2: emptySlot,
1369
+ col3: emptySlot,
1370
+ col4: emptySlot
1371
+ },
1372
+ render: ({ id, count, gap, col1, col2, col3, col4 }) => {
1373
+ const n = Number(count) || 3;
1374
+ const columns = [col1, col2, col3, col4].slice(0, n).map(renderSlot);
1375
+ return /* @__PURE__ */ jsx(Columns, { count: n, gap, columns, testId: blockTestId(id) });
1376
+ }
1377
+ };
1378
+ const SpacerBlock = {
1379
+ label: "Spacer",
1380
+ fields: {
1381
+ size: {
1382
+ type: "radio",
1383
+ label: "Size",
1384
+ options: [
1385
+ { label: "S", value: "sm" },
1386
+ { label: "M", value: "md" },
1387
+ { label: "L", value: "lg" },
1388
+ { label: "XL", value: "xl" }
1389
+ ]
1390
+ }
1391
+ },
1392
+ defaultProps: { size: "md" },
1393
+ render: ({ id, size }) => /* @__PURE__ */ jsx(Spacer, { size, testId: blockTestId(id) })
1394
+ };
1395
+ const ContainerBlock = {
1396
+ label: "Container",
1397
+ fields: {
1398
+ width: {
1399
+ type: "radio",
1400
+ label: "Width",
1401
+ options: [
1402
+ { label: "Narrow", value: "narrow" },
1403
+ { label: "Default", value: "default" },
1404
+ { label: "Wide", value: "wide" },
1405
+ { label: "Full", value: "full" }
1406
+ ]
1407
+ },
1408
+ padding: {
1409
+ type: "radio",
1410
+ label: "Padding",
1411
+ options: [
1412
+ { label: "None", value: "none" },
1413
+ { label: "S", value: "sm" },
1414
+ { label: "M", value: "md" },
1415
+ { label: "L", value: "lg" }
1416
+ ]
1417
+ },
1418
+ children: { type: "slot" }
1419
+ },
1420
+ defaultProps: { width: "default", padding: "md", children: emptySlot },
1421
+ render: ({ id, width, padding, children }) => /* @__PURE__ */ jsx(Container, { width, padding, testId: blockTestId(id), children: renderSlot(children) })
1422
+ };
1423
+ const HeroBlock = {
1424
+ label: "Hero",
1425
+ fields: {
1426
+ eyebrow: textField("Eyebrow"),
1427
+ title: textField("Title"),
1428
+ emphasis: { type: "text", label: "Emphasised word" },
1429
+ subtitle: textareaField("Subtitle", void 0, "lead"),
1430
+ primaryCta: ctaField("Primary CTA"),
1431
+ secondaryCta: ctaField("Secondary CTA"),
1432
+ align: {
1433
+ type: "radio",
1434
+ label: "Align",
1435
+ options: [
1436
+ { label: "Left", value: "left" },
1437
+ { label: "Center", value: "center" }
1438
+ ]
1439
+ },
1440
+ tone: toneField,
1441
+ imageUrl: imageField("Side image"),
1442
+ backgroundImage: imageField("Background image"),
1443
+ overlay: boolRadio("Background overlay")
1444
+ },
1445
+ defaultProps: {
1446
+ eyebrow: "Introducing",
1447
+ title: "Build a better website, faster.",
1448
+ emphasis: "",
1449
+ subtitle: "A flexible platform that helps your team ship polished, on-brand pages without waiting on engineering.",
1450
+ primaryCta: { label: "Get started", href: "#" },
1451
+ secondaryCta: { label: "Learn more", href: "#" },
1452
+ align: "left",
1453
+ tone: "default",
1454
+ imageUrl: "",
1455
+ backgroundImage: "",
1456
+ overlay: true
1457
+ },
1458
+ render: ({
1459
+ id,
1460
+ eyebrow,
1461
+ title,
1462
+ emphasis,
1463
+ subtitle,
1464
+ primaryCta,
1465
+ secondaryCta,
1466
+ align,
1467
+ tone,
1468
+ imageUrl,
1469
+ backgroundImage,
1470
+ overlay
1471
+ }) => /* @__PURE__ */ jsx(
1472
+ Hero,
1473
+ {
1474
+ eyebrow,
1475
+ title,
1476
+ emphasis,
1477
+ subtitle,
1478
+ primaryCta,
1479
+ secondaryCta,
1480
+ align,
1481
+ tone,
1482
+ imageUrl,
1483
+ backgroundImage,
1484
+ overlay,
1485
+ eyebrowClassName: kit.eyebrowClassName,
1486
+ testId: blockTestId(id)
1487
+ }
1488
+ )
1489
+ };
1490
+ const FeatureGridBlock = {
1491
+ label: "Feature grid",
1492
+ fields: {
1493
+ columns: {
1494
+ type: "radio",
1495
+ label: "Columns",
1496
+ options: [
1497
+ { label: "2", value: "2" },
1498
+ { label: "3", value: "3" }
1499
+ ]
1500
+ },
1501
+ items: {
1502
+ type: "array",
1503
+ label: "Features",
1504
+ arrayFields: {
1505
+ icon: iconField(),
1506
+ title: textField("Title"),
1507
+ description: textareaField("Description")
1508
+ },
1509
+ defaultItemProps: {
1510
+ icon: "Sparkles",
1511
+ title: "Feature",
1512
+ description: "Describe this feature."
1513
+ },
1514
+ getItemSummary: (item) => item?.title || "Feature"
1515
+ }
1516
+ },
1517
+ defaultProps: {
1518
+ columns: "3",
1519
+ items: [
1520
+ {
1521
+ icon: "Zap",
1522
+ title: "Fast by default",
1523
+ description: "Ship pages in minutes with sensible, on-brand defaults."
1524
+ },
1525
+ {
1526
+ icon: "Shield",
1527
+ title: "Secure & reliable",
1528
+ description: "Enterprise-grade security baked into every layer."
1529
+ },
1530
+ {
1531
+ icon: "Layers",
1532
+ title: "Composable",
1533
+ description: "Mix and match blocks to build any layout you need."
1534
+ }
1535
+ ]
1536
+ },
1537
+ render: ({ id, columns, items }) => {
1538
+ const list = (items ?? []).map(
1539
+ (it) => ({
1540
+ ...it,
1541
+ icon: resolveIcon(it.icon)
1542
+ })
1543
+ );
1544
+ return /* @__PURE__ */ jsx(FeatureGrid, { columns, items: list, testId: blockTestId(id) });
1545
+ }
1546
+ };
1547
+ const StatGroupBlock = {
1548
+ label: "Stat group",
1549
+ fields: {
1550
+ items: {
1551
+ type: "array",
1552
+ label: "Stats",
1553
+ arrayFields: {
1554
+ value: textField("Value"),
1555
+ label: textField("Label")
1556
+ },
1557
+ defaultItemProps: { value: "100%", label: "Metric" },
1558
+ getItemSummary: (item) => item?.label || "Stat"
1559
+ }
1560
+ },
1561
+ defaultProps: {
1562
+ items: [
1563
+ { value: "10k+", label: "Active users" },
1564
+ { value: "99.9%", label: "Uptime" },
1565
+ { value: "4.9/5", label: "Customer rating" },
1566
+ { value: "24/7", label: "Support" }
1567
+ ]
1568
+ },
1569
+ render: ({ id, items }) => /* @__PURE__ */ jsx(StatGroup, { items, testId: blockTestId(id) })
1570
+ };
1571
+ const TestimonialBlock = {
1572
+ label: "Testimonial",
1573
+ fields: {
1574
+ quote: textareaField("Quote"),
1575
+ author: textField("Author"),
1576
+ role: textField("Role"),
1577
+ avatarUrl: { type: "text", label: "Avatar URL" }
1578
+ },
1579
+ defaultProps: {
1580
+ quote: "This is the best tool our team has adopted in years. It just works.",
1581
+ author: "Alex Morgan",
1582
+ role: "Head of Product, Northwind",
1583
+ avatarUrl: ""
1584
+ },
1585
+ render: ({ id, quote, author, role, avatarUrl }) => /* @__PURE__ */ jsx(
1586
+ Testimonial,
1587
+ {
1588
+ quote,
1589
+ author,
1590
+ role,
1591
+ avatarUrl,
1592
+ testId: blockTestId(id)
1593
+ }
1594
+ )
1595
+ };
1596
+ const CTABannerBlock = {
1597
+ label: "CTA banner",
1598
+ fields: {
1599
+ title: textField("Title"),
1600
+ subtitle: textareaField("Subtitle"),
1601
+ cta: ctaField("Button"),
1602
+ tone: toneField,
1603
+ backgroundImage: imageField("Background image"),
1604
+ overlay: boolRadio("Background overlay")
1605
+ },
1606
+ defaultProps: {
1607
+ title: "Ready to get started?",
1608
+ subtitle: "Join thousands of teams already building with us.",
1609
+ cta: { label: "Start free trial", href: "#" },
1610
+ tone: "default",
1611
+ backgroundImage: "",
1612
+ overlay: true
1613
+ },
1614
+ render: ({ id, title, subtitle, cta, tone, backgroundImage, overlay }) => /* @__PURE__ */ jsx(
1615
+ CTABanner,
1616
+ {
1617
+ title,
1618
+ subtitle,
1619
+ cta,
1620
+ tone,
1621
+ backgroundImage,
1622
+ overlay,
1623
+ testId: blockTestId(id)
1624
+ }
1625
+ )
1626
+ };
1627
+ const LogoCloudBlock = {
1628
+ label: "Logo cloud",
1629
+ fields: {
1630
+ items: {
1631
+ type: "array",
1632
+ label: "Logos",
1633
+ arrayFields: { label: textField("Label") },
1634
+ defaultItemProps: { label: "Brand" },
1635
+ getItemSummary: (item) => item?.label || "Logo"
1636
+ }
1637
+ },
1638
+ defaultProps: {
1639
+ items: [
1640
+ { label: "Northwind" },
1641
+ { label: "Acme" },
1642
+ { label: "Globex" },
1643
+ { label: "Initech" },
1644
+ { label: "Umbrella" }
1645
+ ]
1646
+ },
1647
+ render: ({ id, items }) => /* @__PURE__ */ jsx(LogoCloud, { items, testId: blockTestId(id) })
1648
+ };
1649
+ const FAQBlock = {
1650
+ label: "FAQ",
1651
+ fields: {
1652
+ items: {
1653
+ type: "array",
1654
+ label: "Questions",
1655
+ arrayFields: {
1656
+ question: textField("Question"),
1657
+ answer: textareaField("Answer")
1658
+ },
1659
+ defaultItemProps: { question: "A question?", answer: "The answer." },
1660
+ getItemSummary: (item) => item?.question || "Question"
1661
+ }
1662
+ },
1663
+ defaultProps: {
1664
+ items: [
1665
+ {
1666
+ question: "How does the free trial work?",
1667
+ answer: "You get full access for 14 days, no credit card required."
1668
+ },
1669
+ {
1670
+ question: "Can I change plans later?",
1671
+ answer: "Yes \u2014 upgrade or downgrade at any time from your dashboard."
1672
+ },
1673
+ {
1674
+ question: "Do you offer support?",
1675
+ answer: "Every plan includes 24/7 support from our team."
1676
+ }
1677
+ ]
1678
+ },
1679
+ render: ({ id, items }) => /* @__PURE__ */ jsx(FAQ, { items, testId: blockTestId(id) })
1680
+ };
1681
+ const TimelineBlockConfig = {
1682
+ label: "Timeline",
1683
+ fields: {
1684
+ view: {
1685
+ type: "radio",
1686
+ label: "View",
1687
+ options: [
1688
+ { label: "Reader chooses", value: "both" },
1689
+ { label: "Calendar", value: "calendar" },
1690
+ { label: "Gantt", value: "gantt" }
1691
+ ]
1692
+ },
1693
+ height: { type: "number", label: "Height (px)", min: 200, max: 1200 },
1694
+ items: {
1695
+ type: "array",
1696
+ label: "Entries",
1697
+ arrayFields: {
1698
+ label: textField("Label"),
1699
+ description: textareaField("Description"),
1700
+ start: { type: "text", label: "Start (YYYY-MM-DD)" },
1701
+ end: { type: "text", label: "End (YYYY-MM-DD, optional)" }
1702
+ },
1703
+ defaultItemProps: { label: "Milestone", description: "", start: "", end: "" },
1704
+ getItemSummary: (item) => item?.label || "Entry"
1705
+ }
1706
+ },
1707
+ defaultProps: {
1708
+ view: "both",
1709
+ height: 520,
1710
+ items: [
1711
+ {
1712
+ label: "Kick-off",
1713
+ description: "Project starts.",
1714
+ start: "2026-01-12",
1715
+ end: ""
1716
+ },
1717
+ {
1718
+ label: "Build phase",
1719
+ description: "Design and implementation.",
1720
+ start: "2026-01-19",
1721
+ end: "2026-03-13"
1722
+ },
1723
+ {
1724
+ label: "Launch",
1725
+ description: "Public release.",
1726
+ start: "2026-03-30",
1727
+ end: ""
1728
+ }
1729
+ ]
1730
+ },
1731
+ render: ({ id, view, height, items }) => /* @__PURE__ */ jsx("div", { style: { height: height ?? 520 }, children: /* @__PURE__ */ jsx(
1732
+ Timeline,
1733
+ {
1734
+ items: (items ?? []).filter((item) => !!item?.start).map((item, index) => ({
1735
+ id: `${id}-${index}`,
1736
+ label: item.label ?? "",
1737
+ start: item.start,
1738
+ end: item.end || void 0,
1739
+ description: item.description || void 0
1740
+ })),
1741
+ view: view === "both" ? void 0 : view,
1742
+ testId: blockTestId(id)
1743
+ }
1744
+ ) })
1745
+ };
1746
+ const RichTextBlockConfig = {
1747
+ label: "Rich text",
1748
+ fields: {
1749
+ content: textareaField("Content", void 0, "body")
1750
+ },
1751
+ defaultProps: {
1752
+ content: "## About us\n\nWe build tools that help teams do their best work. Our platform combines **power** and _simplicity_ so you can focus on what matters.\n\n- Fast to learn\n- Easy to scale\n- Loved by teams"
1753
+ },
1754
+ render: ({ id, content }) => /* @__PURE__ */ jsx(RichTextBlock, { content, testId: blockTestId(id) })
1755
+ };
1756
+ const CalloutBlock = {
1757
+ label: "Callout",
1758
+ fields: {
1759
+ variant: {
1760
+ type: "radio",
1761
+ label: "Variant",
1762
+ options: [
1763
+ { label: "Info", value: "info" },
1764
+ { label: "Success", value: "success" },
1765
+ { label: "Warning", value: "warning" },
1766
+ { label: "Danger", value: "danger" }
1767
+ ]
1768
+ },
1769
+ title: textField("Title"),
1770
+ body: textareaField("Body")
1771
+ },
1772
+ defaultProps: {
1773
+ variant: "info",
1774
+ title: "Good to know",
1775
+ body: "This is a helpful note that draws attention to important information."
1776
+ },
1777
+ render: ({ id, variant, title, body }) => /* @__PURE__ */ jsx(Callout, { variant, title, body, testId: blockTestId(id) })
1778
+ };
1779
+ const ImageBlock = {
1780
+ label: "Image",
1781
+ fields: {
1782
+ src: imageField("Image"),
1783
+ alt: textField("Alt text"),
1784
+ aspect: {
1785
+ type: "radio",
1786
+ label: "Aspect",
1787
+ options: [
1788
+ { label: "16:9", value: "16:9" },
1789
+ { label: "4:3", value: "4:3" },
1790
+ { label: "1:1", value: "1:1" },
1791
+ { label: "21:9", value: "21:9" }
1792
+ ]
1793
+ },
1794
+ rounded: {
1795
+ type: "radio",
1796
+ label: "Rounded",
1797
+ options: [
1798
+ { label: "None", value: "none" },
1799
+ { label: "M", value: "md" },
1800
+ { label: "L", value: "lg" },
1801
+ { label: "Full", value: "full" }
1802
+ ]
1803
+ },
1804
+ framed: boolRadio("Framed")
1805
+ },
1806
+ defaultProps: { src: "", alt: "Image", aspect: "16:9", rounded: "lg", framed: false },
1807
+ render: ({ id, src, alt, aspect, rounded, framed }) => /* @__PURE__ */ jsx(
1808
+ BlockImage,
1809
+ {
1810
+ src,
1811
+ alt,
1812
+ aspect,
1813
+ rounded,
1814
+ framed,
1815
+ testId: blockTestId(id)
1816
+ }
1817
+ )
1818
+ };
1819
+ const AvatarBlockConfig = {
1820
+ label: "Avatar",
1821
+ fields: {
1822
+ src: { type: "text", label: "Image URL" },
1823
+ name: textField("Name"),
1824
+ size: {
1825
+ type: "radio",
1826
+ label: "Size",
1827
+ options: [
1828
+ { label: "S", value: "sm" },
1829
+ { label: "M", value: "md" },
1830
+ { label: "L", value: "lg" },
1831
+ { label: "XL", value: "xl" }
1832
+ ]
1833
+ }
1834
+ },
1835
+ defaultProps: { src: "", name: "Jane Doe", size: "md" },
1836
+ render: ({ id, src, name, size }) => /* @__PURE__ */ jsx(AvatarBlock, { src, name, size, testId: blockTestId(id) })
1837
+ };
1838
+ const NavbarBlock = {
1839
+ label: "Navbar",
1840
+ fields: {
1841
+ brand: textField("Brand"),
1842
+ links: {
1843
+ type: "array",
1844
+ label: "Links",
1845
+ arrayFields: {
1846
+ label: textField("Label"),
1847
+ href: { type: "text", label: "Link" }
1848
+ },
1849
+ defaultItemProps: { label: "Link", href: "#" },
1850
+ getItemSummary: (item) => item?.label || "Link"
1851
+ },
1852
+ cta: ctaField("CTA")
1853
+ },
1854
+ defaultProps: {
1855
+ brand: "Acme",
1856
+ links: [
1857
+ { label: "Product", href: "#" },
1858
+ { label: "Pricing", href: "#" },
1859
+ { label: "About", href: "#" },
1860
+ { label: "Contact", href: "#" }
1861
+ ],
1862
+ cta: { label: "Sign up", href: "#" }
1863
+ },
1864
+ render: ({ id, brand, links, cta }) => /* @__PURE__ */ jsx(Navbar, { brand, links, cta, testId: blockTestId(id) })
1865
+ };
1866
+ const FooterBlock = {
1867
+ label: "Footer",
1868
+ fields: {
1869
+ columns: {
1870
+ type: "array",
1871
+ label: "Columns",
1872
+ arrayFields: {
1873
+ heading: textField("Heading"),
1874
+ links: {
1875
+ type: "array",
1876
+ label: "Links",
1877
+ arrayFields: {
1878
+ label: textField("Label"),
1879
+ href: { type: "text", label: "Link" }
1880
+ },
1881
+ defaultItemProps: { label: "Link", href: "#" },
1882
+ getItemSummary: (item) => item?.label || "Link"
1883
+ }
1884
+ },
1885
+ defaultItemProps: { heading: "Column", links: [{ label: "Link", href: "#" }] },
1886
+ getItemSummary: (item) => item?.heading || "Column"
1887
+ },
1888
+ copyright: { type: "text", label: "Copyright" }
1889
+ },
1890
+ defaultProps: {
1891
+ columns: [
1892
+ {
1893
+ heading: "Product",
1894
+ links: [
1895
+ { label: "Features", href: "#" },
1896
+ { label: "Pricing", href: "#" }
1897
+ ]
1898
+ },
1899
+ {
1900
+ heading: "Company",
1901
+ links: [
1902
+ { label: "About", href: "#" },
1903
+ { label: "Careers", href: "#" }
1904
+ ]
1905
+ },
1906
+ {
1907
+ heading: "Resources",
1908
+ links: [
1909
+ { label: "Blog", href: "#" },
1910
+ { label: "Docs", href: "#" }
1911
+ ]
1912
+ },
1913
+ {
1914
+ heading: "Legal",
1915
+ links: [
1916
+ { label: "Privacy", href: "#" },
1917
+ { label: "Terms", href: "#" }
1918
+ ]
1919
+ }
1920
+ ],
1921
+ copyright: "\xA9 2026 Acme, Inc. All rights reserved."
1922
+ },
1923
+ render: ({ id, columns, copyright }) => /* @__PURE__ */ jsx(Footer, { columns, copyright, testId: blockTestId(id) })
1924
+ };
1925
+ const ListBlockConfig = {
1926
+ label: "List",
1927
+ fields: {
1928
+ ordered: boolRadio("Ordered"),
1929
+ items: {
1930
+ type: "array",
1931
+ label: "Items",
1932
+ arrayFields: { text: textField("Text") },
1933
+ defaultItemProps: { text: "List item" },
1934
+ getItemSummary: (item) => item?.text || "Item"
1935
+ }
1936
+ },
1937
+ defaultProps: {
1938
+ ordered: false,
1939
+ items: [{ text: "First item" }, { text: "Second item" }, { text: "Third item" }]
1940
+ },
1941
+ render: ({ id, ordered, items }) => /* @__PURE__ */ jsx(ListBlock, { ordered, items, testId: blockTestId(id) })
1942
+ };
1943
+ return {
1944
+ Columns: ColumnsBlock,
1945
+ Spacer: SpacerBlock,
1946
+ Container: ContainerBlock,
1947
+ Hero: HeroBlock,
1948
+ FeatureGrid: FeatureGridBlock,
1949
+ StatGroup: StatGroupBlock,
1950
+ Testimonial: TestimonialBlock,
1951
+ CTABanner: CTABannerBlock,
1952
+ LogoCloud: LogoCloudBlock,
1953
+ FAQ: FAQBlock,
1954
+ Timeline: TimelineBlockConfig,
1955
+ RichText: RichTextBlockConfig,
1956
+ Callout: CalloutBlock,
1957
+ Image: ImageBlock,
1958
+ Avatar: AvatarBlockConfig,
1959
+ Navbar: NavbarBlock,
1960
+ Footer: FooterBlock,
1961
+ List: ListBlockConfig
1962
+ };
1963
+ }
1964
+ var lines = (s) => (s ?? "").split("\n").map((l) => l.trim()).filter(Boolean);
1965
+ function createSiteBlocks(kit) {
1966
+ const { textField, textareaField, imageField, boolRadio, blockTestId } = kit;
1967
+ const ctaField = (label) => ({
1968
+ type: "object",
1969
+ label,
1970
+ objectFields: {
1971
+ label: textField("Label"),
1972
+ href: { type: "text", label: "Link" }
1973
+ }
1974
+ });
1975
+ const columnsField = (values) => ({
1976
+ type: "radio",
1977
+ label: "Columns",
1978
+ options: values.map((v) => ({ label: v, value: v }))
1979
+ });
1980
+ const PricingBlock = {
1981
+ label: "Pricing",
1982
+ fields: {
1983
+ plans: {
1984
+ type: "array",
1985
+ label: "Plans",
1986
+ getItemSummary: (item) => item?.name || "Plan",
1987
+ defaultItemProps: {
1988
+ name: "Plan",
1989
+ price: "\xA30",
1990
+ period: "mo",
1991
+ description: "",
1992
+ features: "Feature one\nFeature two",
1993
+ ctaLabel: "Choose",
1994
+ ctaHref: "#",
1995
+ featured: false
1996
+ },
1997
+ arrayFields: {
1998
+ name: textField("Name"),
1999
+ price: { type: "text", label: "Price" },
2000
+ period: { type: "text", label: "Period" },
2001
+ description: textareaField("Description"),
2002
+ features: { type: "textarea", label: "Features (one per line)" },
2003
+ ctaLabel: { type: "text", label: "Button label" },
2004
+ ctaHref: { type: "text", label: "Button link" },
2005
+ featured: boolRadio("Featured")
2006
+ }
2007
+ }
2008
+ },
2009
+ defaultProps: {
2010
+ plans: [
2011
+ {
2012
+ name: "Starter",
2013
+ price: "\xA30",
2014
+ period: "mo",
2015
+ description: "For getting started.",
2016
+ features: "Up to 3 projects\nCommunity support",
2017
+ ctaLabel: "Get started",
2018
+ ctaHref: "#",
2019
+ featured: false
2020
+ },
2021
+ {
2022
+ name: "Pro",
2023
+ price: "\xA329",
2024
+ period: "mo",
2025
+ description: "For growing teams.",
2026
+ features: "Unlimited projects\nPriority support\nAdvanced analytics",
2027
+ ctaLabel: "Start free trial",
2028
+ ctaHref: "#",
2029
+ featured: true
2030
+ },
2031
+ {
2032
+ name: "Enterprise",
2033
+ price: "Custom",
2034
+ period: "",
2035
+ description: "For large organisations.",
2036
+ features: "SSO & SAML\nDedicated success manager\nSLA",
2037
+ ctaLabel: "Contact sales",
2038
+ ctaHref: "#",
2039
+ featured: false
2040
+ }
2041
+ ]
2042
+ },
2043
+ render: ({ id, plans }) => /* @__PURE__ */ jsx(
2044
+ PricingTable,
2045
+ {
2046
+ plans: (plans ?? []).map(
2047
+ (p) => ({
2048
+ name: p.name,
2049
+ price: p.price,
2050
+ period: p.period,
2051
+ description: p.description,
2052
+ features: lines(p.features),
2053
+ cta: { label: p.ctaLabel, href: p.ctaHref },
2054
+ featured: p.featured
2055
+ })
2056
+ ),
2057
+ testId: blockTestId(id)
2058
+ }
2059
+ )
2060
+ };
2061
+ const TabsBlock = {
2062
+ label: "Tabs",
2063
+ fields: {
2064
+ items: {
2065
+ type: "array",
2066
+ label: "Tabs",
2067
+ getItemSummary: (item) => item?.label || "Tab",
2068
+ defaultItemProps: { label: "Tab", content: "Tab content." },
2069
+ arrayFields: {
2070
+ label: textField("Label"),
2071
+ content: textareaField("Content")
2072
+ }
2073
+ }
2074
+ },
2075
+ defaultProps: {
2076
+ items: [
2077
+ { label: "Overview", content: "A quick overview of this section." },
2078
+ { label: "Details", content: "The finer details go here." },
2079
+ { label: "FAQ", content: "Answers to common questions." }
2080
+ ]
2081
+ },
2082
+ render: ({ id, items }) => /* @__PURE__ */ jsx(Tabs$1, { items, testId: blockTestId(id) })
2083
+ };
2084
+ const AccordionBlock = {
2085
+ label: "Accordion",
2086
+ fields: {
2087
+ allowMultiple: boolRadio("Allow multiple open"),
2088
+ items: {
2089
+ type: "array",
2090
+ label: "Items",
2091
+ getItemSummary: (item) => item?.title || "Item",
2092
+ defaultItemProps: { title: "Section", content: "Section content." },
2093
+ arrayFields: {
2094
+ title: textField("Title"),
2095
+ content: textareaField("Content")
2096
+ }
2097
+ }
2098
+ },
2099
+ defaultProps: {
2100
+ allowMultiple: false,
2101
+ items: [
2102
+ { title: "What is included?", content: "Everything you need to get started." },
2103
+ { title: "How does billing work?", content: "Monthly or annually, cancel anytime." }
2104
+ ]
2105
+ },
2106
+ render: ({ id, allowMultiple, items }) => /* @__PURE__ */ jsx(Accordion, { allowMultiple, items, testId: blockTestId(id) })
2107
+ };
2108
+ const GalleryBlock = {
2109
+ label: "Gallery",
2110
+ fields: {
2111
+ columns: columnsField(["2", "3", "4"]),
2112
+ aspect: {
2113
+ type: "radio",
2114
+ label: "Aspect",
2115
+ options: [
2116
+ { label: "1:1", value: "1:1" },
2117
+ { label: "4:3", value: "4:3" },
2118
+ { label: "16:9", value: "16:9" }
2119
+ ]
2120
+ },
2121
+ images: {
2122
+ type: "array",
2123
+ label: "Images",
2124
+ getItemSummary: (item) => item?.alt || "Image",
2125
+ defaultItemProps: { src: "", alt: "Image", caption: "" },
2126
+ arrayFields: {
2127
+ src: imageField("Image"),
2128
+ alt: { type: "text", label: "Alt text" },
2129
+ caption: { type: "text", label: "Caption" }
2130
+ }
2131
+ }
2132
+ },
2133
+ defaultProps: {
2134
+ columns: "3",
2135
+ aspect: "1:1",
2136
+ images: [
2137
+ { src: "", alt: "Image one", caption: "" },
2138
+ { src: "", alt: "Image two", caption: "" },
2139
+ { src: "", alt: "Image three", caption: "" }
2140
+ ]
2141
+ },
2142
+ render: ({ id, columns, aspect, images }) => /* @__PURE__ */ jsx(Gallery, { columns, aspect, images, testId: blockTestId(id) })
2143
+ };
2144
+ const VideoBlock = {
2145
+ label: "Video",
2146
+ fields: {
2147
+ url: { type: "text", label: "Video URL (YouTube/Vimeo/Loom or file)" },
2148
+ title: { type: "text", label: "Title" },
2149
+ aspect: {
2150
+ type: "radio",
2151
+ label: "Aspect",
2152
+ options: [
2153
+ { label: "16:9", value: "16:9" },
2154
+ { label: "4:3", value: "4:3" },
2155
+ { label: "1:1", value: "1:1" }
2156
+ ]
2157
+ },
2158
+ rounded: boolRadio("Rounded")
2159
+ },
2160
+ defaultProps: { url: "", title: "", aspect: "16:9", rounded: true },
2161
+ render: ({ id, url, title, aspect, rounded }) => /* @__PURE__ */ jsx(
2162
+ VideoEmbed,
2163
+ {
2164
+ url,
2165
+ title,
2166
+ aspect,
2167
+ rounded,
2168
+ testId: blockTestId(id)
2169
+ }
2170
+ )
2171
+ };
2172
+ const ContactFormBlock = {
2173
+ label: "Contact form",
2174
+ fields: {
2175
+ submitLabel: { type: "text", label: "Submit label" },
2176
+ action: { type: "text", label: "POST endpoint (optional)" },
2177
+ successMessage: textareaField("Success message"),
2178
+ fields: {
2179
+ type: "array",
2180
+ label: "Fields",
2181
+ getItemSummary: (item) => item?.label || "Field",
2182
+ defaultItemProps: { name: "field", label: "Field", type: "text", required: false },
2183
+ arrayFields: {
2184
+ name: { type: "text", label: "Name attribute" },
2185
+ label: textField("Label"),
2186
+ type: {
2187
+ type: "select",
2188
+ label: "Type",
2189
+ options: [
2190
+ { label: "Text", value: "text" },
2191
+ { label: "Email", value: "email" },
2192
+ { label: "Phone", value: "tel" },
2193
+ { label: "Multi-line", value: "textarea" }
2194
+ ]
2195
+ },
2196
+ required: boolRadio("Required")
2197
+ }
2198
+ }
2199
+ },
2200
+ defaultProps: {
2201
+ submitLabel: "Send",
2202
+ action: "",
2203
+ successMessage: "Thanks \u2014 we\u2019ll be in touch shortly.",
2204
+ fields: [
2205
+ { name: "name", label: "Name", type: "text", required: true },
2206
+ { name: "email", label: "Email", type: "email", required: true },
2207
+ { name: "message", label: "Message", type: "textarea", required: true }
2208
+ ]
2209
+ },
2210
+ render: ({ id, submitLabel, action, successMessage, fields }) => /* @__PURE__ */ jsx(
2211
+ ContactForm,
2212
+ {
2213
+ submitLabel,
2214
+ action: action || void 0,
2215
+ successMessage,
2216
+ fields,
2217
+ testId: blockTestId(id)
2218
+ }
2219
+ )
2220
+ };
2221
+ const BannerBlock = {
2222
+ label: "Banner",
2223
+ fields: {
2224
+ text: textField("Text"),
2225
+ cta: ctaField("CTA"),
2226
+ tone: {
2227
+ type: "radio",
2228
+ label: "Tone",
2229
+ options: [
2230
+ { label: "Primary", value: "primary" },
2231
+ { label: "Muted", value: "muted" },
2232
+ { label: "Dark", value: "ink" }
2233
+ ]
2234
+ }
2235
+ },
2236
+ defaultProps: {
2237
+ text: "We just shipped something new.",
2238
+ cta: { label: "Read more", href: "#" },
2239
+ tone: "primary"
2240
+ },
2241
+ render: ({ id, text, cta, tone }) => /* @__PURE__ */ jsx(Banner, { text, cta, tone, testId: blockTestId(id) })
2242
+ };
2243
+ const MediaTextBlock = {
2244
+ label: "Media & text",
2245
+ fields: {
2246
+ eyebrow: textField("Eyebrow"),
2247
+ title: textField("Title"),
2248
+ body: textareaField("Body", void 0, "lead"),
2249
+ imageUrl: imageField("Image"),
2250
+ mediaSide: {
2251
+ type: "radio",
2252
+ label: "Media side",
2253
+ options: [
2254
+ { label: "Left", value: "left" },
2255
+ { label: "Right", value: "right" }
2256
+ ]
2257
+ },
2258
+ cta: ctaField("CTA")
2259
+ },
2260
+ defaultProps: {
2261
+ eyebrow: "",
2262
+ title: "A closer look at what we do.",
2263
+ body: "Pair a compelling visual with the copy that explains it \u2014 the classic split section, on any theme.",
2264
+ imageUrl: "",
2265
+ mediaSide: "left",
2266
+ cta: { label: "Learn more", href: "#" }
2267
+ },
2268
+ render: ({ id, eyebrow, title, body, imageUrl, mediaSide, cta }) => /* @__PURE__ */ jsx(
2269
+ MediaText,
2270
+ {
2271
+ eyebrow,
2272
+ title,
2273
+ body,
2274
+ imageUrl,
2275
+ mediaSide,
2276
+ cta,
2277
+ testId: blockTestId(id)
2278
+ }
2279
+ )
2280
+ };
2281
+ const TeamGridBlock = {
2282
+ label: "Team",
2283
+ fields: {
2284
+ columns: columnsField(["2", "3", "4"]),
2285
+ members: {
2286
+ type: "array",
2287
+ label: "Members",
2288
+ getItemSummary: (item) => item?.name || "Member",
2289
+ defaultItemProps: { name: "New member", role: "Role", bio: "", avatarUrl: "" },
2290
+ arrayFields: {
2291
+ name: textField("Name"),
2292
+ role: { type: "text", label: "Role" },
2293
+ bio: textareaField("Bio"),
2294
+ avatarUrl: { type: "text", label: "Avatar URL" }
2295
+ }
2296
+ }
2297
+ },
2298
+ defaultProps: {
2299
+ columns: "3",
2300
+ members: [
2301
+ { name: "Alex Morgan", role: "Founder & CEO", bio: "", avatarUrl: "" },
2302
+ { name: "Sam Rivera", role: "Head of Product", bio: "", avatarUrl: "" },
2303
+ { name: "Jordan Lee", role: "Lead Engineer", bio: "", avatarUrl: "" }
2304
+ ]
2305
+ },
2306
+ render: ({ id, columns, members }) => /* @__PURE__ */ jsx(TeamGrid, { columns, members, testId: blockTestId(id) })
2307
+ };
2308
+ const StepsBlock = {
2309
+ label: "Steps",
2310
+ fields: {
2311
+ variant: {
2312
+ type: "radio",
2313
+ label: "Style",
2314
+ options: [
2315
+ { label: "Numbered", value: "numbered" },
2316
+ { label: "Timeline", value: "timeline" }
2317
+ ]
2318
+ },
2319
+ steps: {
2320
+ type: "array",
2321
+ label: "Steps",
2322
+ getItemSummary: (item) => item?.title || "Step",
2323
+ defaultItemProps: { title: "Step", description: "Describe this step." },
2324
+ arrayFields: {
2325
+ title: textField("Title"),
2326
+ description: textareaField("Description")
2327
+ }
2328
+ }
2329
+ },
2330
+ defaultProps: {
2331
+ variant: "numbered",
2332
+ steps: [
2333
+ { title: "Sign up", description: "Create your account in seconds." },
2334
+ { title: "Configure", description: "Set things up to match your workflow." },
2335
+ { title: "Launch", description: "Go live and start seeing results." }
2336
+ ]
2337
+ },
2338
+ render: ({ id, variant, steps }) => /* @__PURE__ */ jsx(Steps, { variant, steps, testId: blockTestId(id) })
2339
+ };
2340
+ const TestimonialGridBlock = {
2341
+ label: "Testimonial grid",
2342
+ fields: {
2343
+ columns: columnsField(["2", "3"]),
2344
+ items: {
2345
+ type: "array",
2346
+ label: "Testimonials",
2347
+ getItemSummary: (item) => item?.author || "Testimonial",
2348
+ defaultItemProps: { quote: "A great quote.", author: "Author", role: "", avatarUrl: "" },
2349
+ arrayFields: {
2350
+ quote: textareaField("Quote"),
2351
+ author: textField("Author"),
2352
+ role: { type: "text", label: "Role" },
2353
+ avatarUrl: { type: "text", label: "Avatar URL" }
2354
+ }
2355
+ }
2356
+ },
2357
+ defaultProps: {
2358
+ columns: "3",
2359
+ items: [
2360
+ {
2361
+ quote: "This changed how our whole team works.",
2362
+ author: "Alex Morgan",
2363
+ role: "CTO, Northwind",
2364
+ avatarUrl: ""
2365
+ },
2366
+ {
2367
+ quote: "The best tool we adopted this year.",
2368
+ author: "Sam Rivera",
2369
+ role: "Ops Lead, Acme",
2370
+ avatarUrl: ""
2371
+ },
2372
+ {
2373
+ quote: "Simple, fast, and it just works.",
2374
+ author: "Jordan Lee",
2375
+ role: "Founder, Globex",
2376
+ avatarUrl: ""
2377
+ }
2378
+ ]
2379
+ },
2380
+ render: ({ id, columns, items }) => /* @__PURE__ */ jsx(TestimonialGrid, { columns, items, testId: blockTestId(id) })
2381
+ };
2382
+ return {
2383
+ Pricing: PricingBlock,
2384
+ Tabs: TabsBlock,
2385
+ Accordion: AccordionBlock,
2386
+ Gallery: GalleryBlock,
2387
+ Video: VideoBlock,
2388
+ ContactForm: ContactFormBlock,
2389
+ Banner: BannerBlock,
2390
+ MediaText: MediaTextBlock,
2391
+ Team: TeamGridBlock,
2392
+ Steps: StepsBlock,
2393
+ TestimonialGrid: TestimonialGridBlock
2394
+ };
2395
+ }
2396
+ var emptySlot2 = [];
2397
+ var METRIC_COLS = {
2398
+ "2": "sm:grid-cols-2",
2399
+ "3": "sm:grid-cols-2 lg:grid-cols-3",
2400
+ "4": "sm:grid-cols-2 lg:grid-cols-4"
2401
+ };
2402
+ var METRIC_GAP = { sm: "gap-3", md: "gap-4", lg: "gap-6" };
2403
+ function createArbiBlocksConfig(opts = {}) {
2404
+ const kit = createBlockKit(opts.aiFields, opts.imageField, opts.eyebrowClassName);
2405
+ const { textField, textareaField, boolRadio, iconField, resolveIcon, blockTestId } = kit;
2406
+ const SectionHeadingBlock = {
2407
+ label: "Section heading",
2408
+ fields: {
2409
+ title: textField("Title"),
2410
+ eyebrow: textField("Eyebrow"),
2411
+ lead: textareaField("Lead", void 0, "lead"),
2412
+ align: {
2413
+ type: "radio",
2414
+ label: "Align",
2415
+ options: [
2416
+ { label: "Left", value: "left" },
2417
+ { label: "Center", value: "center" }
2418
+ ]
2419
+ },
2420
+ invert: boolRadio("Invert (on dark)")
2421
+ },
2422
+ defaultProps: { title: "Section title", align: "left", invert: false },
2423
+ render: ({ id, title, eyebrow, lead, align, invert }) => /* @__PURE__ */ jsx("div", { "data-testid": blockTestId(id), children: /* @__PURE__ */ jsx(
2424
+ SectionHeading,
2425
+ {
2426
+ title,
2427
+ eyebrow,
2428
+ lead,
2429
+ align,
2430
+ invert,
2431
+ eyebrowClassName: kit.eyebrowClassName
2432
+ }
2433
+ ) })
2434
+ };
2435
+ const PageHeaderBlock = {
2436
+ label: "Page header",
2437
+ fields: {
2438
+ title: textField("Title"),
2439
+ eyebrow: textField("Eyebrow"),
2440
+ description: textareaField("Description", void 0, "description"),
2441
+ // `actions` is a slot so the top band can host real buttons/badges authored
2442
+ // as blocks (drop a Button in), while the interactive body stays bespoke.
2443
+ actions: { type: "slot", allow: ["Button", "Badge"] }
2444
+ },
2445
+ defaultProps: { title: "Page title", actions: emptySlot2 },
2446
+ render: ({ id, title, eyebrow, description, actions: Actions }) => /* @__PURE__ */ jsx(
2447
+ PageHeader,
2448
+ {
2449
+ title,
2450
+ eyebrow,
2451
+ description,
2452
+ actions: /* @__PURE__ */ jsx(Actions, { className: "flex items-center gap-2" }),
2453
+ testId: blockTestId(id)
2454
+ }
2455
+ )
2456
+ };
2457
+ const EmptyStateBlock = {
2458
+ label: "Empty state",
2459
+ fields: {
2460
+ title: textField("Title"),
2461
+ description: textareaField("Description", void 0, "description"),
2462
+ icon: iconField(),
2463
+ // `action` is a slot so an empty state can offer a real call-to-action button.
2464
+ action: { type: "slot", allow: ["Button"] }
2465
+ },
2466
+ defaultProps: { title: "Nothing here yet", action: emptySlot2 },
2467
+ render: ({ id, title, description, icon, action: Action }) => /* @__PURE__ */ jsx(
2468
+ EmptyState,
2469
+ {
2470
+ title,
2471
+ description,
2472
+ icon: resolveIcon(icon),
2473
+ action: /* @__PURE__ */ jsx(Action, {}),
2474
+ testId: blockTestId(id)
2475
+ }
2476
+ )
2477
+ };
2478
+ const MetricCardBlock = {
2479
+ label: "Metric card",
2480
+ fields: {
2481
+ label: { type: "text", label: "Label" },
2482
+ value: { type: "text", label: "Value" },
2483
+ hint: { type: "text", label: "Hint" },
2484
+ accent: {
2485
+ type: "radio",
2486
+ label: "Accent",
2487
+ options: [
2488
+ { label: "Default", value: "default" },
2489
+ { label: "Primary", value: "primary" },
2490
+ { label: "Success", value: "success" },
2491
+ { label: "Muted", value: "muted" }
2492
+ ]
2493
+ },
2494
+ delta: {
2495
+ type: "object",
2496
+ label: "Delta",
2497
+ objectFields: {
2498
+ value: { type: "text", label: "Value" },
2499
+ direction: {
2500
+ type: "radio",
2501
+ label: "Direction",
2502
+ options: [
2503
+ { label: "Up", value: "up" },
2504
+ { label: "Down", value: "down" }
2505
+ ]
2506
+ },
2507
+ good: boolRadio("Good")
2508
+ }
2509
+ },
2510
+ icon: iconField()
2511
+ },
2512
+ defaultProps: { label: "Metric", value: "0", accent: "default" },
2513
+ render: ({ id, label, value, hint, accent, delta, icon }) => /* @__PURE__ */ jsx(
2514
+ MetricCard,
2515
+ {
2516
+ label,
2517
+ value,
2518
+ hint,
2519
+ accent,
2520
+ delta,
2521
+ icon: resolveIcon(icon),
2522
+ testId: blockTestId(id)
2523
+ }
2524
+ )
2525
+ };
2526
+ const MetricRowBlock = {
2527
+ label: "Metric row",
2528
+ fields: {
2529
+ columns: {
2530
+ type: "radio",
2531
+ label: "Columns",
2532
+ options: [
2533
+ { label: "2", value: "2" },
2534
+ { label: "3", value: "3" },
2535
+ { label: "4", value: "4" }
2536
+ ]
2537
+ },
2538
+ gap: {
2539
+ type: "radio",
2540
+ label: "Gap",
2541
+ options: [
2542
+ { label: "S", value: "sm" },
2543
+ { label: "M", value: "md" },
2544
+ { label: "L", value: "lg" }
2545
+ ]
2546
+ },
2547
+ // Static KPI tiles and (when registered) live metric widgets both belong in a band.
2548
+ items: { type: "slot", allow: ["MetricCard", "LiveMetric"] }
2549
+ },
2550
+ defaultProps: { columns: "3", gap: "md", items: emptySlot2 },
2551
+ render: ({ id, columns, gap, items: Items }) => /* @__PURE__ */ jsx("div", { "data-testid": blockTestId(id), children: /* @__PURE__ */ jsx(
2552
+ Items,
2553
+ {
2554
+ className: cn(
2555
+ "grid grid-cols-1",
2556
+ METRIC_COLS[columns] ?? METRIC_COLS["3"],
2557
+ METRIC_GAP[gap] ?? METRIC_GAP.md
2558
+ )
2559
+ }
2560
+ ) })
2561
+ };
2562
+ const DataTableBlockConfig = {
2563
+ label: "Data table",
2564
+ fields: {
2565
+ caption: textField("Caption"),
2566
+ columns: {
2567
+ type: "array",
2568
+ label: "Columns",
2569
+ arrayFields: {
2570
+ header: { type: "text", label: "Header" },
2571
+ field: { type: "text", label: "Field key" },
2572
+ align: {
2573
+ type: "radio",
2574
+ label: "Align",
2575
+ options: [
2576
+ { label: "Left", value: "left" },
2577
+ { label: "Right", value: "right" },
2578
+ { label: "Center", value: "center" }
2579
+ ]
2580
+ }
2581
+ },
2582
+ defaultItemProps: { header: "Column", field: "field", align: "left" },
2583
+ getItemSummary: (item) => item?.header || "Column"
2584
+ },
2585
+ rows: {
2586
+ type: "array",
2587
+ label: "Rows",
2588
+ arrayFields: {
2589
+ cells: {
2590
+ type: "array",
2591
+ label: "Cells",
2592
+ arrayFields: {
2593
+ value: { type: "text", label: "Value" }
2594
+ },
2595
+ defaultItemProps: { value: "" },
2596
+ getItemSummary: (item) => item?.value || "Cell"
2597
+ }
2598
+ },
2599
+ defaultItemProps: { cells: [{ value: "" }, { value: "" }] },
2600
+ getItemSummary: (_item, i) => `Row ${(i ?? 0) + 1}`
2601
+ },
2602
+ emptyText: textField("Empty text")
2603
+ },
2604
+ defaultProps: {
2605
+ columns: [
2606
+ { header: "Item", field: "item", align: "left" },
2607
+ { header: "Status", field: "status", align: "left" }
2608
+ ],
2609
+ rows: [
2610
+ { cells: [{ value: "Acme v. Globex" }, { value: "Active" }] },
2611
+ { cells: [{ value: "Initech Merger" }, { value: "Closed" }] }
2612
+ ],
2613
+ emptyText: "No rows yet"
2614
+ },
2615
+ render: ({ id, columns, rows, caption, emptyText }) => {
2616
+ const cols = columns ?? [];
2617
+ const rawRows = rows ?? [];
2618
+ const recordRows = rawRows.map((row) => {
2619
+ const record = {};
2620
+ cols.forEach((col, index) => {
2621
+ record[col.field || `col-${index}`] = row.cells?.[index]?.value ?? "";
2622
+ });
2623
+ return record;
2624
+ });
2625
+ const normalisedCols = cols.map((col, index) => ({
2626
+ ...col,
2627
+ field: col.field || `col-${index}`
2628
+ }));
2629
+ return /* @__PURE__ */ jsx(
2630
+ DataTableBlock,
2631
+ {
2632
+ columns: normalisedCols,
2633
+ rows: recordRows,
2634
+ caption,
2635
+ emptyText,
2636
+ testId: blockTestId(id)
2637
+ }
2638
+ );
2639
+ }
2640
+ };
2641
+ const SectionBlock = {
2642
+ label: "Section",
2643
+ fields: {
2644
+ children: { type: "slot" },
2645
+ tone: {
2646
+ type: "radio",
2647
+ label: "Tone",
2648
+ options: [
2649
+ { label: "Default", value: "default" },
2650
+ { label: "Muted", value: "muted" },
2651
+ { label: "Dark", value: "ink" }
2652
+ ]
2653
+ },
2654
+ padding: {
2655
+ type: "radio",
2656
+ label: "Padding",
2657
+ options: [
2658
+ { label: "None", value: "none" },
2659
+ { label: "S", value: "sm" },
2660
+ { label: "M", value: "md" },
2661
+ { label: "L", value: "lg" }
2662
+ ]
2663
+ },
2664
+ maxWidth: {
2665
+ type: "radio",
2666
+ label: "Width",
2667
+ options: [
2668
+ { label: "Narrow", value: "narrow" },
2669
+ { label: "Default", value: "default" },
2670
+ { label: "Wide", value: "wide" },
2671
+ { label: "Full", value: "full" }
2672
+ ]
2673
+ },
2674
+ texture: boolRadio("Texture")
2675
+ },
2676
+ defaultProps: {
2677
+ tone: "default",
2678
+ padding: "md",
2679
+ maxWidth: "default",
2680
+ texture: false,
2681
+ children: []
2682
+ },
2683
+ render: ({ id, tone, padding, maxWidth, texture, children: Children }) => /* @__PURE__ */ jsx("div", { "data-testid": blockTestId(id), children: /* @__PURE__ */ jsx(Section, { tone, padding, maxWidth, texture, children: /* @__PURE__ */ jsx(Children, {}) }) })
2684
+ };
2685
+ const CardBlock = {
2686
+ label: "Card",
2687
+ fields: {
2688
+ variant: {
2689
+ type: "select",
2690
+ label: "Variant",
2691
+ options: [
2692
+ { label: "Flat", value: "flat" },
2693
+ { label: "Outline", value: "outline" },
2694
+ { label: "Elevated", value: "elevated" }
2695
+ ]
2696
+ },
2697
+ children: { type: "slot" }
2698
+ },
2699
+ defaultProps: { variant: "flat", children: [] },
2700
+ render: ({ id, variant, children: Children }) => /* @__PURE__ */ jsx(Card, { variant, className: "p-5", "data-testid": blockTestId(id), children: /* @__PURE__ */ jsx(Children, {}) })
2701
+ };
2702
+ const ButtonBlock = {
2703
+ label: "Button",
2704
+ fields: {
2705
+ children: { type: "text", label: "Label" },
2706
+ variant: {
2707
+ type: "select",
2708
+ label: "Variant",
2709
+ options: [
2710
+ { label: "Default", value: "default" },
2711
+ { label: "Accent", value: "accent" },
2712
+ { label: "Destructive", value: "destructive" },
2713
+ { label: "Outline", value: "outline" },
2714
+ { label: "Secondary", value: "secondary" },
2715
+ { label: "Ghost", value: "ghost" },
2716
+ { label: "Link", value: "link" }
2717
+ ]
2718
+ },
2719
+ size: {
2720
+ type: "select",
2721
+ label: "Size",
2722
+ options: [
2723
+ { label: "Default", value: "default" },
2724
+ { label: "Small", value: "sm" },
2725
+ { label: "Large", value: "lg" },
2726
+ { label: "Icon", value: "icon" }
2727
+ ]
2728
+ }
2729
+ },
2730
+ defaultProps: { variant: "default", size: "default", children: "Button" },
2731
+ render: ({ id, children, variant, size }) => /* @__PURE__ */ jsx(Button, { variant, size, "data-testid": blockTestId(id), children })
2732
+ };
2733
+ const BadgeBlock = {
2734
+ label: "Badge",
2735
+ fields: {
2736
+ text: { type: "text", label: "Text" },
2737
+ variant: {
2738
+ type: "select",
2739
+ label: "Variant",
2740
+ options: [
2741
+ { label: "Default", value: "default" },
2742
+ { label: "Accent", value: "accent" },
2743
+ { label: "Secondary", value: "secondary" },
2744
+ { label: "Destructive", value: "destructive" },
2745
+ { label: "Outline", value: "outline" }
2746
+ ]
2747
+ }
2748
+ },
2749
+ defaultProps: { text: "Badge", variant: "default" },
2750
+ render: ({ id, text, variant }) => /* @__PURE__ */ jsx(Badge, { variant, "data-testid": blockTestId(id), children: text })
2751
+ };
2752
+ const SeparatorBlock = {
2753
+ label: "Separator",
2754
+ fields: {},
2755
+ defaultProps: {},
2756
+ render: ({ id }) => /* @__PURE__ */ jsx(Separator, { "data-testid": blockTestId(id) })
2757
+ };
2758
+ const website = createWebsiteBlocks(kit);
2759
+ const site = createSiteBlocks(kit);
2760
+ const widgets = opts.liveWidgets ? createLiveWidgets(kit) : {};
2761
+ const components = {
2762
+ SectionHeading: SectionHeadingBlock,
2763
+ PageHeader: PageHeaderBlock,
2764
+ EmptyState: EmptyStateBlock,
2765
+ MetricCard: MetricCardBlock,
2766
+ MetricRow: MetricRowBlock,
2767
+ DataTable: DataTableBlockConfig,
2768
+ Section: SectionBlock,
2769
+ Card: CardBlock,
2770
+ Button: ButtonBlock,
2771
+ Badge: BadgeBlock,
2772
+ Separator: SeparatorBlock,
2773
+ ...website,
2774
+ ...site,
2775
+ ...widgets
2776
+ };
2777
+ const config = {
2778
+ categories: {
2779
+ Layout: {
2780
+ title: "Layout",
2781
+ components: ["Section", "Container", "Columns", "Card", "Spacer"]
2782
+ },
2783
+ Marketing: {
2784
+ title: "Marketing",
2785
+ components: [
2786
+ "Hero",
2787
+ "FeatureGrid",
2788
+ "StatGroup",
2789
+ "Testimonial",
2790
+ "TestimonialGrid",
2791
+ "CTABanner",
2792
+ "Banner",
2793
+ "LogoCloud",
2794
+ "FAQ",
2795
+ "MediaText",
2796
+ "Steps",
2797
+ "Team"
2798
+ ]
2799
+ },
2800
+ Content: {
2801
+ title: "Content",
2802
+ components: ["SectionHeading", "PageHeader", "EmptyState", "RichText", "Callout", "List"]
2803
+ },
2804
+ Interactive: { title: "Interactive", components: ["Tabs", "Accordion"] },
2805
+ Media: { title: "Media", components: ["Image", "Avatar", "Gallery", "Video"] },
2806
+ Commerce: { title: "Commerce", components: ["Pricing"] },
2807
+ Forms: { title: "Forms", components: ["ContactForm"] },
2808
+ Navigation: { title: "Navigation", components: ["Navbar", "Footer"] },
2809
+ Data: { title: "Data", components: ["MetricRow", "MetricCard", "DataTable", "Timeline"] },
2810
+ ...opts.liveWidgets ? { Widgets: { title: "Widgets", components: ["AgentActivity", "LiveMetric"] } } : {},
2811
+ Primitives: { title: "Primitives", components: ["Button", "Badge", "Separator"] }
2812
+ },
2813
+ components,
2814
+ root: {
2815
+ render: ({ children }) => /* @__PURE__ */ jsx(
2816
+ "div",
2817
+ {
2818
+ className: cn("bg-background text-foreground font-sans", opts.root?.className),
2819
+ "data-testid": opts.root?.testId ?? "arbi-blocks-page-root",
2820
+ children
2821
+ }
2822
+ )
2823
+ }
2824
+ };
2825
+ return config;
2826
+ }
2827
+
2828
+ // src/builder/modules/registry.ts
2829
+ function createModuleRegistry(appNav) {
2830
+ const DEFAULT_MODULES = appNav.flatMap(
2831
+ (section, sIdx) => section.items.map((item, iIdx) => ({
2832
+ id: item.id,
2833
+ label: item.label,
2834
+ section: section.heading,
2835
+ icon: item.icon,
2836
+ to: item.to,
2837
+ arbi: item.arbi ?? false,
2838
+ end: item.end ?? false,
2839
+ // Global order that preserves section grouping then in-section order.
2840
+ defaultOrder: sIdx * 100 + iIdx
2841
+ }))
2842
+ );
2843
+ const SECTION_ORDER = appNav.map((s) => s.heading);
2844
+ const toNavItem = (m) => ({
2845
+ to: m.to,
2846
+ label: m.label,
2847
+ icon: m.icon,
2848
+ id: m.id,
2849
+ arbi: m.arbi || void 0,
2850
+ end: m.end || void 0
2851
+ });
2852
+ return { DEFAULT_MODULES, SECTION_ORDER, toNavItem };
2853
+ }
2854
+ function createModuleStore({
2855
+ registry,
2856
+ storageKey
2857
+ }) {
2858
+ const { DEFAULT_MODULES, SECTION_ORDER, toNavItem } = registry;
2859
+ function isEnabled(overrides, id2) {
2860
+ return overrides[id2]?.enabled ?? true;
2861
+ }
2862
+ const id = (m) => m.id;
2863
+ function orderOf(overrides, m) {
2864
+ return overrides[id(m)]?.order ?? m.defaultOrder;
2865
+ }
2866
+ const useModuleStore = create()(
2867
+ persist(
2868
+ (set, get) => ({
2869
+ overrides: {},
2870
+ toggle: (moduleId) => {
2871
+ const m = DEFAULT_MODULES.find((x) => x.id === moduleId);
2872
+ if (!m) return;
2873
+ const cur = get().overrides;
2874
+ const prev = cur[moduleId] ?? { enabled: true, order: m.defaultOrder };
2875
+ set({
2876
+ overrides: { ...cur, [moduleId]: { ...prev, enabled: !isEnabled(cur, moduleId) } }
2877
+ });
2878
+ },
2879
+ move: (moduleId, dir) => {
2880
+ const cur = get().overrides;
2881
+ const target = DEFAULT_MODULES.find((x) => x.id === moduleId);
2882
+ if (!target) return;
2883
+ const siblings = DEFAULT_MODULES.filter((x) => x.section === target.section).sort(
2884
+ (a2, b2) => orderOf(cur, a2) - orderOf(cur, b2)
2885
+ );
2886
+ const idx = siblings.findIndex((x) => x.id === moduleId);
2887
+ const swapIdx = dir === "up" ? idx - 1 : idx + 1;
2888
+ if (swapIdx < 0 || swapIdx >= siblings.length) return;
2889
+ const a = siblings[idx];
2890
+ const b = siblings[swapIdx];
2891
+ const next = { ...cur };
2892
+ next[a.id] = { enabled: isEnabled(cur, a.id), order: orderOf(cur, b) };
2893
+ next[b.id] = { enabled: isEnabled(cur, b.id), order: orderOf(cur, a) };
2894
+ set({ overrides: next });
2895
+ },
2896
+ reset: () => set({ overrides: {} })
2897
+ }),
2898
+ { name: storageKey }
2899
+ )
2900
+ );
2901
+ function useResolvedModules() {
2902
+ const overrides = useModuleStore((s) => s.overrides);
2903
+ return useMemo(() => {
2904
+ return SECTION_ORDER.map((section) => ({
2905
+ section,
2906
+ modules: DEFAULT_MODULES.filter((m) => m.section === section).map((m) => ({ ...m, enabled: isEnabled(overrides, m.id), order: orderOf(overrides, m) })).sort((a, b) => a.order - b.order)
2907
+ }));
2908
+ }, [overrides]);
2909
+ }
2910
+ function useEnabledNav() {
2911
+ const grouped = useResolvedModules();
2912
+ return useMemo(
2913
+ () => grouped.map((g) => ({
2914
+ heading: g.section,
2915
+ items: g.modules.filter((m) => m.enabled).map(toNavItem)
2916
+ })).filter((s) => s.items.length > 0),
2917
+ [grouped]
2918
+ );
2919
+ }
2920
+ return { useModuleStore, useResolvedModules, useEnabledNav };
2921
+ }
2922
+ var toggle = (list, id) => list.includes(id) ? list.filter((x) => x !== id) : [...list, id];
2923
+ function createAgentSelectionStore({
2924
+ storageKey
2925
+ }) {
2926
+ return create()(
2927
+ persist(
2928
+ (set) => ({
2929
+ configIds: [],
2930
+ agentIds: [],
2931
+ toggleConfig: (id) => set((s) => ({ configIds: toggle(s.configIds, id) })),
2932
+ toggleAgent: (id) => set((s) => ({ agentIds: toggle(s.agentIds, id) })),
2933
+ clear: () => set({ configIds: [], agentIds: [] })
2934
+ }),
2935
+ { name: storageKey }
2936
+ )
2937
+ );
2938
+ }
2939
+
2940
+ // src/builder/export.ts
2941
+ function createVerticalExport(cfg) {
2942
+ const { vertical, themeStore, moduleStore, agentStore, registry } = cfg;
2943
+ const { DEFAULT_MODULES, SECTION_ORDER } = registry;
2944
+ function buildVerticalConfig() {
2945
+ const { colors, fonts, radius } = themeStore.getState();
2946
+ const { overrides } = moduleStore.useModuleStore.getState();
2947
+ const { configIds, agentIds } = agentStore.getState();
2948
+ const modules = DEFAULT_MODULES.map((m) => ({
2949
+ id: m.id,
2950
+ label: m.label,
2951
+ section: m.section,
2952
+ to: m.to,
2953
+ arbi: m.arbi,
2954
+ enabled: overrides[m.id]?.enabled ?? true,
2955
+ order: overrides[m.id]?.order ?? m.defaultOrder
2956
+ })).sort(
2957
+ (a, b) => SECTION_ORDER.indexOf(a.section) - SECTION_ORDER.indexOf(b.section) || a.order - b.order
2958
+ );
2959
+ return {
2960
+ version: 1,
2961
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2962
+ vertical,
2963
+ theme: { colors, fonts, radius },
2964
+ modules,
2965
+ agents: { configIds, agentIds }
2966
+ };
2967
+ }
2968
+ function serialize(config = buildVerticalConfig()) {
2969
+ return JSON.stringify(config, null, 2);
2970
+ }
2971
+ function download(config = buildVerticalConfig()) {
2972
+ const blob = new Blob([serialize(config)], { type: "application/json" });
2973
+ const url = URL.createObjectURL(blob);
2974
+ const a = document.createElement("a");
2975
+ a.href = url;
2976
+ a.download = `${config.vertical.key}-vertical.json`;
2977
+ document.body.appendChild(a);
2978
+ a.click();
2979
+ a.remove();
2980
+ URL.revokeObjectURL(url);
2981
+ }
2982
+ return { buildVerticalConfig, serialize, download };
2983
+ }
2984
+ function ModuleManager({ store, testIdPrefix = "builder" }) {
2985
+ const grouped = store.useResolvedModules();
2986
+ const toggle2 = store.useModuleStore((s) => s.toggle);
2987
+ const move = store.useModuleStore((s) => s.move);
2988
+ const reset = store.useModuleStore((s) => s.reset);
2989
+ const enabledCount = grouped.reduce((n, g) => n + g.modules.filter((m) => m.enabled).length, 0);
2990
+ const total = grouped.reduce((n, g) => n + g.modules.length, 0);
2991
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-5", "data-testid": `${testIdPrefix}-modules`, children: [
2992
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
2993
+ /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", "data-testid": `${testIdPrefix}-modules-count`, children: [
2994
+ enabledCount,
2995
+ " of ",
2996
+ total,
2997
+ " modules enabled"
2998
+ ] }),
2999
+ /* @__PURE__ */ jsxs(
3000
+ Button,
3001
+ {
3002
+ size: "sm",
3003
+ variant: "ghost",
3004
+ onClick: reset,
3005
+ "data-testid": `${testIdPrefix}-modules-reset`,
3006
+ children: [
3007
+ /* @__PURE__ */ jsx(RotateCcw, { className: "size-3.5" }),
3008
+ " Reset"
3009
+ ]
3010
+ }
3011
+ )
3012
+ ] }),
3013
+ grouped.map((group) => /* @__PURE__ */ jsxs(
3014
+ Card,
3015
+ {
3016
+ variant: "elevated",
3017
+ className: "rounded-lg",
3018
+ "data-testid": tid(`${testIdPrefix}-modules-section`, group.section),
3019
+ children: [
3020
+ /* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: group.section }) }),
3021
+ /* @__PURE__ */ jsx(CardContent, { className: "space-y-1.5", children: group.modules.map((m, idx) => {
3022
+ const Icon = m.icon;
3023
+ return /* @__PURE__ */ jsxs(
3024
+ "div",
3025
+ {
3026
+ className: "flex items-center gap-3 rounded-md border border-border/60 bg-card px-3 py-2",
3027
+ "data-testid": tid(`${testIdPrefix}-module`, m.id),
3028
+ children: [
3029
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
3030
+ /* @__PURE__ */ jsx(
3031
+ Button,
3032
+ {
3033
+ size: "icon",
3034
+ variant: "ghost",
3035
+ className: "size-6",
3036
+ disabled: idx === 0,
3037
+ onClick: () => move(m.id, "up"),
3038
+ "aria-label": `Move ${m.label} up`,
3039
+ "data-testid": tid(`${testIdPrefix}-module-up`, m.id),
3040
+ children: /* @__PURE__ */ jsx(ChevronUp, { className: "size-3.5" })
3041
+ }
3042
+ ),
3043
+ /* @__PURE__ */ jsx(
3044
+ Button,
3045
+ {
3046
+ size: "icon",
3047
+ variant: "ghost",
3048
+ className: "size-6",
3049
+ disabled: idx === group.modules.length - 1,
3050
+ onClick: () => move(m.id, "down"),
3051
+ "aria-label": `Move ${m.label} down`,
3052
+ "data-testid": tid(`${testIdPrefix}-module-down`, m.id),
3053
+ children: /* @__PURE__ */ jsx(ChevronDown, { className: "size-3.5" })
3054
+ }
3055
+ )
3056
+ ] }),
3057
+ /* @__PURE__ */ jsx(Icon, { className: "size-4 shrink-0 text-muted-foreground" }),
3058
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
3059
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: m.label }),
3060
+ /* @__PURE__ */ jsx("p", { className: "truncate font-mono text-xs text-muted-foreground", children: m.to })
3061
+ ] }),
3062
+ m.arbi && /* @__PURE__ */ jsxs(Badge, { variant: "accent", className: "text-xs", children: [
3063
+ /* @__PURE__ */ jsx(Sparkles, { className: "size-3" }),
3064
+ " ARBI"
3065
+ ] }),
3066
+ /* @__PURE__ */ jsx(
3067
+ Switch,
3068
+ {
3069
+ checked: m.enabled,
3070
+ onCheckedChange: () => toggle2(m.id),
3071
+ "aria-label": `Enable ${m.label}`,
3072
+ "data-testid": tid(`${testIdPrefix}-module-toggle`, m.id)
3073
+ }
3074
+ )
3075
+ ]
3076
+ },
3077
+ m.id
3078
+ );
3079
+ }) })
3080
+ ]
3081
+ },
3082
+ group.section
3083
+ ))
3084
+ ] });
3085
+ }
3086
+ var TIER_VARIANT = {
3087
+ Premium: "default",
3088
+ Wise: "accent",
3089
+ Fast: "secondary",
3090
+ auto: "secondary"
3091
+ };
3092
+ function ConfigModelBadge({ configId, testIdPrefix }) {
3093
+ const arbi = useArbi();
3094
+ const { data, isLoading, isError } = useQuery({
3095
+ queryKey: ["builder", "config-model", configId],
3096
+ queryFn: async () => (await arbi.agentConfig.get(configId)).Agents.AGENT_MODEL_NAME,
3097
+ staleTime: 6e4
3098
+ });
3099
+ const tier = isLoading ? "\u2026" : isError || !data ? "\u2014" : data;
3100
+ return /* @__PURE__ */ jsxs(
3101
+ Badge,
3102
+ {
3103
+ variant: TIER_VARIANT[tier] ?? "secondary",
3104
+ "data-testid": tid(`${testIdPrefix}-agents-config-tier`, configId),
3105
+ children: [
3106
+ /* @__PURE__ */ jsx(Cpu, { className: "size-3" }),
3107
+ " ",
3108
+ tier
3109
+ ]
3110
+ }
3111
+ );
3112
+ }
3113
+ function AgentsPanel({ store, testIdPrefix = "builder" }) {
3114
+ const { data: configs, isLoading: configsLoading } = useConfigs();
3115
+ const { data: agents = [], isLoading: agentsLoading } = useAgents();
3116
+ const configIds = store((s) => s.configIds);
3117
+ const agentIds = store((s) => s.agentIds);
3118
+ const toggleConfig = store((s) => s.toggleConfig);
3119
+ const toggleAgent = store((s) => s.toggleAgent);
3120
+ const versions = configs?.versions ?? [];
3121
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6", "data-testid": `${testIdPrefix}-agents`, children: [
3122
+ /* @__PURE__ */ jsxs(
3123
+ Card,
3124
+ {
3125
+ variant: "elevated",
3126
+ className: "rounded-lg",
3127
+ "data-testid": `${testIdPrefix}-agents-configs`,
3128
+ children: [
3129
+ /* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2 text-base", children: [
3130
+ /* @__PURE__ */ jsx(BrainCircuit, { className: "size-4 text-primary" }),
3131
+ " Behaviour configurations",
3132
+ /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "ml-1", children: versions.length })
3133
+ ] }) }),
3134
+ /* @__PURE__ */ jsxs(CardContent, { className: "space-y-1.5", children: [
3135
+ configsLoading && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Loading configurations\u2026" }),
3136
+ !configsLoading && versions.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "No configurations found on this workspace." }),
3137
+ versions.map((v) => /* @__PURE__ */ jsxs(
3138
+ "label",
3139
+ {
3140
+ className: "flex cursor-pointer items-center gap-3 rounded-md border border-border/60 bg-card px-3 py-2",
3141
+ "data-testid": tid(`${testIdPrefix}-agents-config`, v.external_id),
3142
+ children: [
3143
+ /* @__PURE__ */ jsx(
3144
+ Checkbox,
3145
+ {
3146
+ checked: configIds.includes(v.external_id),
3147
+ onCheckedChange: () => toggleConfig(v.external_id),
3148
+ "data-testid": tid(`${testIdPrefix}-agents-config-toggle`, v.external_id)
3149
+ }
3150
+ ),
3151
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
3152
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: v.title ?? "Untitled behaviour" }),
3153
+ /* @__PURE__ */ jsx("p", { className: "truncate font-mono text-xs text-muted-foreground", children: v.external_id })
3154
+ ] }),
3155
+ /* @__PURE__ */ jsx(ConfigModelBadge, { configId: v.external_id, testIdPrefix })
3156
+ ]
3157
+ },
3158
+ v.external_id
3159
+ ))
3160
+ ] })
3161
+ ]
3162
+ }
3163
+ ),
3164
+ /* @__PURE__ */ jsxs(Card, { variant: "elevated", className: "rounded-lg", "data-testid": `${testIdPrefix}-agents-agents`, children: [
3165
+ /* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2 text-base", children: [
3166
+ /* @__PURE__ */ jsx(Bot, { className: "size-4 text-primary" }),
3167
+ " Agents",
3168
+ /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "ml-1", children: agents.length })
3169
+ ] }) }),
3170
+ /* @__PURE__ */ jsxs(CardContent, { className: "space-y-1.5", children: [
3171
+ agentsLoading && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Loading agents\u2026" }),
3172
+ !agentsLoading && agents.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "No agents provisioned." }),
3173
+ agents.map((a) => /* @__PURE__ */ jsxs(
3174
+ "label",
3175
+ {
3176
+ className: "flex cursor-pointer items-center gap-3 rounded-md border border-border/60 bg-card px-3 py-2",
3177
+ "data-testid": tid(`${testIdPrefix}-agents-agent`, a.external_id),
3178
+ children: [
3179
+ /* @__PURE__ */ jsx(
3180
+ Checkbox,
3181
+ {
3182
+ checked: agentIds.includes(a.external_id),
3183
+ onCheckedChange: () => toggleAgent(a.external_id),
3184
+ "data-testid": tid(`${testIdPrefix}-agents-agent-toggle`, a.external_id)
3185
+ }
3186
+ ),
3187
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
3188
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: (a.given_name ?? "Agent").replace(/-/g, " ") }),
3189
+ /* @__PURE__ */ jsx("p", { className: "truncate font-mono text-xs text-muted-foreground", children: a.external_id })
3190
+ ] })
3191
+ ]
3192
+ },
3193
+ a.external_id
3194
+ ))
3195
+ ] })
3196
+ ] })
3197
+ ] });
3198
+ }
3199
+ function ExportPanel({
3200
+ themeBundle,
3201
+ moduleStore,
3202
+ agentStore,
3203
+ exporter,
3204
+ testIdPrefix
3205
+ }) {
3206
+ const theme = themeBundle.useStore();
3207
+ const modules = moduleStore.useModuleStore();
3208
+ const agents = agentStore();
3209
+ const json = useMemo(() => exporter.serialize(), [theme, modules, agents, exporter]);
3210
+ return /* @__PURE__ */ jsxs(Card, { variant: "elevated", className: "rounded-xl", "data-testid": `${testIdPrefix}-export-panel`, children: [
3211
+ /* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center justify-between text-base", children: [
3212
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
3213
+ /* @__PURE__ */ jsx(FileJson, { className: "size-4 text-primary" }),
3214
+ " Vertical config"
3215
+ ] }),
3216
+ /* @__PURE__ */ jsxs(
3217
+ Button,
3218
+ {
3219
+ size: "sm",
3220
+ variant: "accent",
3221
+ onClick: () => exporter.download(),
3222
+ "data-testid": `${testIdPrefix}-export`,
3223
+ children: [
3224
+ /* @__PURE__ */ jsx(Download, { className: "size-4" }),
3225
+ " Download JSON"
3226
+ ]
3227
+ }
3228
+ )
3229
+ ] }) }),
3230
+ /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx(
3231
+ "pre",
3232
+ {
3233
+ className: "scrollbar-slim max-h-[520px] overflow-auto rounded-md bg-foreground p-4 font-mono text-xs leading-relaxed text-background",
3234
+ "data-testid": `${testIdPrefix}-export-preview`,
3235
+ children: json
3236
+ }
3237
+ ) })
3238
+ ] });
3239
+ }
3240
+ function VerticalBuilder({
3241
+ themeBundle,
3242
+ moduleStore,
3243
+ agentStore,
3244
+ exporter,
3245
+ themePreview,
3246
+ header,
3247
+ testIdPrefix = "builder"
3248
+ }) {
3249
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6", "data-testid": `page-${testIdPrefix}`, children: [
3250
+ header,
3251
+ /* @__PURE__ */ jsxs(Tabs, { defaultValue: "theme", children: [
3252
+ /* @__PURE__ */ jsxs(TabsList, { "data-testid": `${testIdPrefix}-tabs`, children: [
3253
+ /* @__PURE__ */ jsxs(TabsTrigger, { value: "theme", "data-testid": `${testIdPrefix}-tab-theme`, children: [
3254
+ /* @__PURE__ */ jsx(Palette, { className: "size-4" }),
3255
+ " Theme"
3256
+ ] }),
3257
+ /* @__PURE__ */ jsxs(TabsTrigger, { value: "modules", "data-testid": `${testIdPrefix}-tab-modules`, children: [
3258
+ /* @__PURE__ */ jsx(LayoutGrid, { className: "size-4" }),
3259
+ " Modules"
3260
+ ] }),
3261
+ /* @__PURE__ */ jsxs(TabsTrigger, { value: "agents", "data-testid": `${testIdPrefix}-tab-agents`, children: [
3262
+ /* @__PURE__ */ jsx(Bot, { className: "size-4" }),
3263
+ " Agents"
3264
+ ] }),
3265
+ /* @__PURE__ */ jsxs(TabsTrigger, { value: "export", "data-testid": `${testIdPrefix}-tab-export`, children: [
3266
+ /* @__PURE__ */ jsx(FileJson, { className: "size-4" }),
3267
+ " Export"
3268
+ ] })
3269
+ ] }),
3270
+ /* @__PURE__ */ jsx(TabsContent, { value: "theme", children: /* @__PURE__ */ jsx(
3271
+ ThemeEditor,
3272
+ {
3273
+ bundle: themeBundle,
3274
+ variant: "panel",
3275
+ testIdPrefix: `${testIdPrefix}-theme`,
3276
+ preview: themePreview
3277
+ }
3278
+ ) }),
3279
+ /* @__PURE__ */ jsx(TabsContent, { value: "modules", children: /* @__PURE__ */ jsx(ModuleManager, { store: moduleStore, testIdPrefix }) }),
3280
+ /* @__PURE__ */ jsx(TabsContent, { value: "agents", children: /* @__PURE__ */ jsx(AgentsPanel, { store: agentStore, testIdPrefix }) }),
3281
+ /* @__PURE__ */ jsx(TabsContent, { value: "export", children: /* @__PURE__ */ jsx(
3282
+ ExportPanel,
3283
+ {
3284
+ themeBundle,
3285
+ moduleStore,
3286
+ agentStore,
3287
+ exporter,
3288
+ testIdPrefix
3289
+ }
3290
+ ) })
3291
+ ] })
3292
+ ] });
3293
+ }
3294
+
3295
+ export { AgentsPanel, AssetThumb, LiveDataProvider, ModuleManager, PageRenderer, Section, StudioEditor, StudioPersistenceError, VerticalBuilder, WidgetPlaceholder, createAgentSelectionStore, createAiFields, createArbiBlocksConfig, createArbiMaterializer, createBlockKit, createImageField, createLiveWidgets, createModuleRegistry, createModuleStore, createSiteBlocks, createStudioPersistence, createVerticalExport, createWebsiteBlocks, iconMap, materializePageData, useAssetImage, useImageGen, useLiveDataActive };
3296
+ //# sourceMappingURL=index.js.map
3297
+ //# sourceMappingURL=index.js.map