@salla.sa/twilight-theme-engine 1.0.124 → 1.0.126

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 (231) hide show
  1. package/dist/{AddToCartForm-7C5NJOT3.js → AddToCartForm-BY24SGTR.js} +3 -3
  2. package/dist/{AddToCartForm-7C5NJOT3.js.map → AddToCartForm-BY24SGTR.js.map} +1 -1
  3. package/dist/{BlogAuthorPage-T5AQINIT.js → BlogAuthorPage-UAQ6266M.js} +17 -17
  4. package/dist/{BlogAuthorPage-T5AQINIT.js.map → BlogAuthorPage-UAQ6266M.js.map} +1 -1
  5. package/dist/{BlogCategoryPage-NNDOLC3G.js → BlogCategoryPage-UX5OZZRZ.js} +17 -17
  6. package/dist/{BlogCategoryPage-NNDOLC3G.js.map → BlogCategoryPage-UX5OZZRZ.js.map} +1 -1
  7. package/dist/{BlogPage-F52ASL2W.js → BlogPage-AAITQ3UZ.js} +15 -15
  8. package/dist/{BlogPage-F52ASL2W.js.map → BlogPage-AAITQ3UZ.js.map} +1 -1
  9. package/dist/{BlogSinglePage-33OTX2SH.js → BlogSinglePage-R7VG3WNP.js} +15 -15
  10. package/dist/{BlogSinglePage-33OTX2SH.js.map → BlogSinglePage-R7VG3WNP.js.map} +1 -1
  11. package/dist/{BlogTagPage-DOTS5CLM.js → BlogTagPage-YCJL3FNS.js} +17 -17
  12. package/dist/{BlogTagPage-DOTS5CLM.js.map → BlogTagPage-YCJL3FNS.js.map} +1 -1
  13. package/dist/{BrandsPage-R5DRBHNZ.js → BrandsPage-4M4J7YH5.js} +16 -16
  14. package/dist/{BrandsPage-R5DRBHNZ.js.map → BrandsPage-4M4J7YH5.js.map} +1 -1
  15. package/dist/{Breadcrumb-4YXIEAFY.js → Breadcrumb-WB5NXLLA.js} +15 -15
  16. package/dist/{Breadcrumb-4YXIEAFY.js.map → Breadcrumb-WB5NXLLA.js.map} +1 -1
  17. package/dist/{CartItem-KMQJU6BO.js → CartItem-UWCMEGW7.js} +15 -15
  18. package/dist/{CartItem-KMQJU6BO.js.map → CartItem-UWCMEGW7.js.map} +1 -1
  19. package/dist/{CartSummary-EW6Q6JS7.js → CartSummary-TK55II7A.js} +15 -15
  20. package/dist/{CartSummary-EW6Q6JS7.js.map → CartSummary-TK55II7A.js.map} +1 -1
  21. package/dist/{Copyright-AVJL3KJR.js → Copyright-PHLEOCO4.js} +3 -3
  22. package/dist/{Copyright-AVJL3KJR.js.map → Copyright-PHLEOCO4.js.map} +1 -1
  23. package/dist/{CustomerLayout-5343GESW.js → CustomerLayout-BCTFI56B.js} +15 -15
  24. package/dist/{CustomerLayout-5343GESW.js.map → CustomerLayout-BCTFI56B.js.map} +1 -1
  25. package/dist/{Footer-ODWJ3HQH.js → Footer-AXIXSATN.js} +4 -4
  26. package/dist/{Footer-ODWJ3HQH.js.map → Footer-AXIXSATN.js.map} +1 -1
  27. package/dist/{Header-VQJILH5R.js → Header-5GD53HC5.js} +8 -8
  28. package/dist/{Header-VQJILH5R.js.map → Header-5GD53HC5.js.map} +1 -1
  29. package/dist/{LiveEditorBar-WSASAFCL.js → LiveEditorBar-IQLFFBHE.js} +38 -11
  30. package/dist/LiveEditorBar-IQLFFBHE.js.map +1 -0
  31. package/dist/{LoyaltyPage-HDEWDV6M.js → LoyaltyPage-MBYLZF7B.js} +15 -15
  32. package/dist/{LoyaltyPage-HDEWDV6M.js.map → LoyaltyPage-MBYLZF7B.js.map} +1 -1
  33. package/dist/{LoyaltyPointsPage-5ZML2PHB.js → LoyaltyPointsPage-SFAL3TOO.js} +6 -6
  34. package/dist/{LoyaltyPointsPage-5ZML2PHB.js.map → LoyaltyPointsPage-SFAL3TOO.js.map} +1 -1
  35. package/dist/{MainMenu-YTGZSTRC.js → MainMenu-VEZTUNCZ.js} +5 -5
  36. package/dist/{MainMenu-YTGZSTRC.js.map → MainMenu-VEZTUNCZ.js.map} +1 -1
  37. package/dist/{MasterLayout-ITSFTBZD.js → MasterLayout-TFHNWIRE.js} +15 -15
  38. package/dist/{MasterLayout-ITSFTBZD.js.map → MasterLayout-TFHNWIRE.js.map} +1 -1
  39. package/dist/{MobileMenu-FJTSMNGS.js → MobileMenu-JWD22FVU.js} +16 -16
  40. package/dist/{MobileMenu-FJTSMNGS.js.map → MobileMenu-JWD22FVU.js.map} +1 -1
  41. package/dist/{NotificationsPage-EVQ7KTKD.js → NotificationsPage-QVE7AOCV.js} +15 -15
  42. package/dist/{NotificationsPage-EVQ7KTKD.js.map → NotificationsPage-QVE7AOCV.js.map} +1 -1
  43. package/dist/{OrderSinglePage-IJQ2BW6N.js → OrderSinglePage-QSNZVQ22.js} +16 -16
  44. package/dist/{OrderSinglePage-IJQ2BW6N.js.map → OrderSinglePage-QSNZVQ22.js.map} +1 -1
  45. package/dist/{OrdersPage-5PW3D7NP.js → OrdersPage-WGIHZJNJ.js} +15 -15
  46. package/dist/{OrdersPage-5PW3D7NP.js.map → OrdersPage-WGIHZJNJ.js.map} +1 -1
  47. package/dist/{PageSingle-DQA45NP5.js → PageSingle-3HXGOYLZ.js} +3 -3
  48. package/dist/{PageSingle-DQA45NP5.js.map → PageSingle-3HXGOYLZ.js.map} +1 -1
  49. package/dist/{ProductDetails-IAJZJOPE.js → ProductDetails-ALVRCBP4.js} +4 -4
  50. package/dist/{ProductDetails-IAJZJOPE.js.map → ProductDetails-ALVRCBP4.js.map} +1 -1
  51. package/dist/{ProfilePage-7SEMN3OY.js → ProfilePage-AVJYAQOG.js} +15 -15
  52. package/dist/{ProfilePage-7SEMN3OY.js.map → ProfilePage-AVJYAQOG.js.map} +1 -1
  53. package/dist/{ReviewsPage-HEC24XWB.js → ReviewsPage-ULGQMLGH.js} +3 -3
  54. package/dist/{ReviewsPage-HEC24XWB.js.map → ReviewsPage-ULGQMLGH.js.map} +1 -1
  55. package/dist/{SeoCartWidget-VZ2VAKQ6.js → SeoCartWidget-5WY6AV2F.js} +15 -15
  56. package/dist/{SeoCartWidget-VZ2VAKQ6.js.map → SeoCartWidget-5WY6AV2F.js.map} +1 -1
  57. package/dist/{SettingsPage-7N2V7IXZ.js → SettingsPage-ZHTSKKBD.js} +15 -15
  58. package/dist/{SettingsPage-7N2V7IXZ.js.map → SettingsPage-ZHTSKKBD.js.map} +1 -1
  59. package/dist/{StoreClosedNotification-XMX4T26T.js → StoreClosedNotification-5V7QGKMC.js} +16 -16
  60. package/dist/{StoreClosedNotification-XMX4T26T.js.map → StoreClosedNotification-5V7QGKMC.js.map} +1 -1
  61. package/dist/{TestimonialsPage-TD7G4REM.js → TestimonialsPage-7YS7QRTY.js} +3 -3
  62. package/dist/{TestimonialsPage-TD7G4REM.js.map → TestimonialsPage-7YS7QRTY.js.map} +1 -1
  63. package/dist/{ThankYouPage-23Y5VNJL.js → ThankYouPage-PLCUI7LC.js} +15 -15
  64. package/dist/{ThankYouPage-23Y5VNJL.js.map → ThankYouPage-PLCUI7LC.js.map} +1 -1
  65. package/dist/{WalletPage-QOEE4C73.js → WalletPage-C6ZFJTSA.js} +16 -16
  66. package/dist/{WalletPage-QOEE4C73.js.map → WalletPage-C6ZFJTSA.js.map} +1 -1
  67. package/dist/{WishlistPage-YTN4SYZA.js → WishlistPage-3CIW7AIZ.js} +17 -17
  68. package/dist/{WishlistPage-YTN4SYZA.js.map → WishlistPage-3CIW7AIZ.js.map} +1 -1
  69. package/dist/api/advertisement.js +2 -2
  70. package/dist/api/apps.js +2 -2
  71. package/dist/api/blog.js +3 -3
  72. package/dist/api/brands.js +3 -3
  73. package/dist/api/cart.js +3 -3
  74. package/dist/api/category.js +3 -3
  75. package/dist/api/client.js +1 -1
  76. package/dist/api/home.js +2 -2
  77. package/dist/api/loyalty.js +3 -3
  78. package/dist/api/menu.js +2 -2
  79. package/dist/api/notification.js +2 -2
  80. package/dist/api/order.js +3 -3
  81. package/dist/api/page.js +3 -3
  82. package/dist/api/product.js +3 -3
  83. package/dist/api/profile.js +1 -1
  84. package/dist/api/store.js +2 -2
  85. package/dist/api/translations.js +2 -2
  86. package/dist/api/user.js +2 -2
  87. package/dist/api/wallet.js +2 -2
  88. package/dist/api/wishlist.js +4 -4
  89. package/dist/{chunk-IFPTZL77.js → chunk-47BPYZJF.js} +3 -3
  90. package/dist/{chunk-IFPTZL77.js.map → chunk-47BPYZJF.js.map} +1 -1
  91. package/dist/{chunk-N4M5Y3CH.js → chunk-4C76FZV4.js} +4 -4
  92. package/dist/{chunk-N4M5Y3CH.js.map → chunk-4C76FZV4.js.map} +1 -1
  93. package/dist/{chunk-ZHW5KZJM.js → chunk-4VYKXIYA.js} +12 -8
  94. package/dist/chunk-4VYKXIYA.js.map +1 -0
  95. package/dist/{chunk-E5JIVIBY.js → chunk-4ZYJDSYQ.js} +3 -3
  96. package/dist/{chunk-E5JIVIBY.js.map → chunk-4ZYJDSYQ.js.map} +1 -1
  97. package/dist/{chunk-XO4S6BKT.js → chunk-53IXPTVY.js} +4 -4
  98. package/dist/{chunk-XO4S6BKT.js.map → chunk-53IXPTVY.js.map} +1 -1
  99. package/dist/{chunk-WNV73ZFT.js → chunk-5LP2K2J7.js} +3 -3
  100. package/dist/{chunk-WNV73ZFT.js.map → chunk-5LP2K2J7.js.map} +1 -1
  101. package/dist/{chunk-AS4A6I5D.js → chunk-5SJRY6P3.js} +4 -4
  102. package/dist/{chunk-AS4A6I5D.js.map → chunk-5SJRY6P3.js.map} +1 -1
  103. package/dist/{chunk-I44YRS6T.js → chunk-6PK4GITC.js} +3 -3
  104. package/dist/{chunk-I44YRS6T.js.map → chunk-6PK4GITC.js.map} +1 -1
  105. package/dist/{chunk-RMKB3MJS.js → chunk-75STIRBZ.js} +4 -4
  106. package/dist/{chunk-RMKB3MJS.js.map → chunk-75STIRBZ.js.map} +1 -1
  107. package/dist/{chunk-6MF7FWER.js → chunk-A6AAG7U6.js} +4 -4
  108. package/dist/{chunk-6MF7FWER.js.map → chunk-A6AAG7U6.js.map} +1 -1
  109. package/dist/{chunk-Z3MSALIT.js → chunk-A6IKHCIA.js} +4 -4
  110. package/dist/{chunk-Z3MSALIT.js.map → chunk-A6IKHCIA.js.map} +1 -1
  111. package/dist/{chunk-XAG4TLX4.js → chunk-ANLG2WBG.js} +4 -4
  112. package/dist/{chunk-XAG4TLX4.js.map → chunk-ANLG2WBG.js.map} +1 -1
  113. package/dist/{chunk-NPSABKKR.js → chunk-BWTUGXHI.js} +5 -5
  114. package/dist/{chunk-NPSABKKR.js.map → chunk-BWTUGXHI.js.map} +1 -1
  115. package/dist/{chunk-DRV4QRIS.js → chunk-CKH5HDHS.js} +4 -4
  116. package/dist/{chunk-DRV4QRIS.js.map → chunk-CKH5HDHS.js.map} +1 -1
  117. package/dist/{chunk-5EKTGKJY.js → chunk-FN2GQ2B5.js} +3 -3
  118. package/dist/{chunk-5EKTGKJY.js.map → chunk-FN2GQ2B5.js.map} +1 -1
  119. package/dist/{chunk-7NOGCBED.js → chunk-GHLPK63Q.js} +7 -7
  120. package/dist/{chunk-7NOGCBED.js.map → chunk-GHLPK63Q.js.map} +1 -1
  121. package/dist/{chunk-J52AQPKU.js → chunk-GLE6TNJ3.js} +3 -3
  122. package/dist/{chunk-J52AQPKU.js.map → chunk-GLE6TNJ3.js.map} +1 -1
  123. package/dist/{chunk-PNAWYJP2.js → chunk-HFCZIPYO.js} +3 -3
  124. package/dist/{chunk-PNAWYJP2.js.map → chunk-HFCZIPYO.js.map} +1 -1
  125. package/dist/{chunk-PCGKPANV.js → chunk-IGGIH6GL.js} +3 -3
  126. package/dist/{chunk-PCGKPANV.js.map → chunk-IGGIH6GL.js.map} +1 -1
  127. package/dist/{chunk-2QJXTJCF.js → chunk-IM5NMPNU.js} +7 -7
  128. package/dist/{chunk-2QJXTJCF.js.map → chunk-IM5NMPNU.js.map} +1 -1
  129. package/dist/{chunk-VEY7PJK7.js → chunk-JAKLD546.js} +3 -3
  130. package/dist/{chunk-VEY7PJK7.js.map → chunk-JAKLD546.js.map} +1 -1
  131. package/dist/{chunk-TLH4C6H4.js → chunk-JL7MKSCD.js} +10 -10
  132. package/dist/{chunk-TLH4C6H4.js.map → chunk-JL7MKSCD.js.map} +1 -1
  133. package/dist/{chunk-R2LXUFTW.js → chunk-KDV6WZYJ.js} +3 -3
  134. package/dist/{chunk-R2LXUFTW.js.map → chunk-KDV6WZYJ.js.map} +1 -1
  135. package/dist/{chunk-CDLT7UJB.js → chunk-KEOCLT62.js} +3 -3
  136. package/dist/{chunk-CDLT7UJB.js.map → chunk-KEOCLT62.js.map} +1 -1
  137. package/dist/{chunk-N7EFUGP4.js → chunk-KKOFHYTI.js} +3 -3
  138. package/dist/{chunk-N7EFUGP4.js.map → chunk-KKOFHYTI.js.map} +1 -1
  139. package/dist/{chunk-4HV2MRZD.js → chunk-LWPIGNCW.js} +4 -4
  140. package/dist/{chunk-4HV2MRZD.js.map → chunk-LWPIGNCW.js.map} +1 -1
  141. package/dist/{chunk-EFMAJAIS.js → chunk-MHLKLADI.js} +3 -3
  142. package/dist/{chunk-EFMAJAIS.js.map → chunk-MHLKLADI.js.map} +1 -1
  143. package/dist/{chunk-PDIFXQDX.js → chunk-MVNWWZWQ.js} +23 -4
  144. package/dist/chunk-MVNWWZWQ.js.map +1 -0
  145. package/dist/{chunk-XCIMWJ7P.js → chunk-NHJ5U6WK.js} +3 -3
  146. package/dist/{chunk-XCIMWJ7P.js.map → chunk-NHJ5U6WK.js.map} +1 -1
  147. package/dist/{chunk-IIVUBE4K.js → chunk-R2FXTCRK.js} +23 -22
  148. package/dist/chunk-R2FXTCRK.js.map +1 -0
  149. package/dist/{chunk-ICKIPKCC.js → chunk-RE4QQM4Q.js} +3 -3
  150. package/dist/{chunk-ICKIPKCC.js.map → chunk-RE4QQM4Q.js.map} +1 -1
  151. package/dist/{chunk-QUDLGIR6.js → chunk-SGET4UYI.js} +3 -3
  152. package/dist/{chunk-QUDLGIR6.js.map → chunk-SGET4UYI.js.map} +1 -1
  153. package/dist/{chunk-MHBBN7O6.js → chunk-TCRZSLXH.js} +6 -6
  154. package/dist/{chunk-MHBBN7O6.js.map → chunk-TCRZSLXH.js.map} +1 -1
  155. package/dist/{chunk-JN4EYPHY.js → chunk-TNUK62YF.js} +4 -4
  156. package/dist/{chunk-JN4EYPHY.js.map → chunk-TNUK62YF.js.map} +1 -1
  157. package/dist/chunk-UE65NYZF.js +14 -0
  158. package/dist/{chunk-QTTABDVW.js.map → chunk-UE65NYZF.js.map} +1 -1
  159. package/dist/{chunk-COF5OFQX.js → chunk-UHCNMJDI.js} +7 -7
  160. package/dist/{chunk-COF5OFQX.js.map → chunk-UHCNMJDI.js.map} +1 -1
  161. package/dist/{chunk-3EYNFA67.js → chunk-WBDU6KDP.js} +4 -4
  162. package/dist/{chunk-3EYNFA67.js.map → chunk-WBDU6KDP.js.map} +1 -1
  163. package/dist/{chunk-74EGP2KE.js → chunk-WFSKX5AI.js} +3 -3
  164. package/dist/{chunk-74EGP2KE.js.map → chunk-WFSKX5AI.js.map} +1 -1
  165. package/dist/{chunk-K5TWJICH.js → chunk-XRDQAJW5.js} +3 -3
  166. package/dist/{chunk-K5TWJICH.js.map → chunk-XRDQAJW5.js.map} +1 -1
  167. package/dist/{chunk-4SKMLRLX.js → chunk-Z76YTG4W.js} +3 -3
  168. package/dist/{chunk-4SKMLRLX.js.map → chunk-Z76YTG4W.js.map} +1 -1
  169. package/dist/components/cart/index.js +1 -1
  170. package/dist/components/common/index.js +1 -1
  171. package/dist/components/drawer/index.js +13 -13
  172. package/dist/components/home/HomePageRenderer.d.ts.map +1 -1
  173. package/dist/components/home/index.js +17 -17
  174. package/dist/components/layout/index.js +4 -4
  175. package/dist/components/live-editor/LiveEditorBar.d.ts +5 -1
  176. package/dist/components/live-editor/LiveEditorBar.d.ts.map +1 -1
  177. package/dist/components/live-editor/frame.d.ts +7 -0
  178. package/dist/components/live-editor/frame.d.ts.map +1 -1
  179. package/dist/components/live-editor/index.d.ts.map +1 -1
  180. package/dist/components/product/index.js +14 -14
  181. package/dist/components/store/index.js +14 -14
  182. package/dist/dev/editor/ThemeEditor.d.ts.map +1 -1
  183. package/dist/dev/editor/index.js +108 -26
  184. package/dist/dev/editor/index.js.map +1 -1
  185. package/dist/dev/editor/live-actions.d.ts +19 -0
  186. package/dist/dev/editor/live-actions.d.ts.map +1 -0
  187. package/dist/dev/index.js +1 -1
  188. package/dist/{document-class-QZGAXN2W.js → document-class-P2H6HKXA.js} +11 -11
  189. package/dist/{document-class-QZGAXN2W.js.map → document-class-P2H6HKXA.js.map} +1 -1
  190. package/dist/hooks/index.js +13 -13
  191. package/dist/hooks/useGtm.js +13 -13
  192. package/dist/hooks/useMoney.js +13 -13
  193. package/dist/hooks/useStore.js +13 -13
  194. package/dist/hooks/useUser.js +3 -3
  195. package/dist/index.js +13 -13
  196. package/dist/{navigation-3SFDZIDH.js → navigation-4STP3ZMK.js} +4 -4
  197. package/dist/{navigation-3SFDZIDH.js.map → navigation-4STP3ZMK.js.map} +1 -1
  198. package/dist/{navigation-WAAWAQV2.js → navigation-6Y2SKWH3.js} +5 -5
  199. package/dist/{navigation-WAAWAQV2.js.map → navigation-6Y2SKWH3.js.map} +1 -1
  200. package/dist/{not-found-DJOWWOUH.js → not-found-LLCXGKVS.js} +16 -16
  201. package/dist/{not-found-DJOWWOUH.js.map → not-found-LLCXGKVS.js.map} +1 -1
  202. package/dist/providers/index.js +13 -13
  203. package/dist/routes/account/orders.js +4 -4
  204. package/dist/routes/account.js +13 -13
  205. package/dist/routes/blog.js +8 -8
  206. package/dist/routes/brands.js +4 -4
  207. package/dist/routes/cart.js +21 -21
  208. package/dist/routes/home.js +18 -18
  209. package/dist/routes/index.js +3 -3
  210. package/dist/routes/loyalty.js +5 -5
  211. package/dist/routes/page.js +4 -4
  212. package/dist/routes/product-listing.js +19 -19
  213. package/dist/routes/product.js +17 -17
  214. package/dist/routes/seo-redirects.js +3 -3
  215. package/dist/routes/testimonials.js +2 -2
  216. package/dist/routes/thank-you.js +4 -4
  217. package/dist/skeleton.js +18 -18
  218. package/dist/{store-lookup-UONSSGJ3.js → store-lookup-XKL42Y4H.js} +8 -8
  219. package/dist/{store-lookup-UONSSGJ3.js.map → store-lookup-XKL42Y4H.js.map} +1 -1
  220. package/dist/tanstack.js +11 -11
  221. package/dist/utils/index.js +2 -2
  222. package/package.json +1 -1
  223. package/tailwind/components/home/HomePageRenderer.tsx.txt +13 -6
  224. package/tailwind/components/live-editor/LiveEditorBar.tsx.txt +51 -8
  225. package/tailwind/components/live-editor/frame.ts.txt +34 -1
  226. package/tailwind/components/live-editor/index.tsx.txt +9 -7
  227. package/dist/LiveEditorBar-WSASAFCL.js.map +0 -1
  228. package/dist/chunk-IIVUBE4K.js.map +0 -1
  229. package/dist/chunk-PDIFXQDX.js.map +0 -1
  230. package/dist/chunk-QTTABDVW.js +0 -14
  231. package/dist/chunk-ZHW5KZJM.js.map +0 -1
package/dist/tanstack.js CHANGED
@@ -1,25 +1,25 @@
1
- export { tanstackDocumentAdapter as attrs, createRouter, createTwilightRootRoute, useTanstackDocumentAttrs as useAttrs } from './chunk-TLH4C6H4.js';
2
- import { requestHostOf } from './chunk-IFPTZL77.js';
3
- export { TanStackLinkAdapter, TanStackNavigationProvider, createTanStackLinkAdapter } from './chunk-IFPTZL77.js';
1
+ export { tanstackDocumentAdapter as attrs, createRouter, createTwilightRootRoute, useTanstackDocumentAttrs as useAttrs } from './chunk-JL7MKSCD.js';
2
+ import { requestHostOf } from './chunk-47BPYZJF.js';
3
+ export { TanStackLinkAdapter, TanStackNavigationProvider, createTanStackLinkAdapter } from './chunk-47BPYZJF.js';
4
4
  import './chunk-Z4Y55HWX.js';
5
5
  export { tanstackHeadAdapter as head, withHead } from './chunk-EHTLXK5F.js';
6
6
  import { CRITICAL_STYLE_HREF } from './chunk-4FJGZQD3.js';
7
- import './chunk-PCGKPANV.js';
8
- import { readNativeAppHeaders } from './chunk-CDLT7UJB.js';
9
- import './chunk-R2LXUFTW.js';
10
- import './chunk-JN4EYPHY.js';
7
+ import './chunk-IGGIH6GL.js';
8
+ import { readNativeAppHeaders } from './chunk-KEOCLT62.js';
9
+ import './chunk-KDV6WZYJ.js';
10
+ import './chunk-TNUK62YF.js';
11
11
  import { rootHead, maintenanceEvidence, MAINTENANCE_EVIDENCE_HEADER, maintenanceEvidenceHeader } from './chunk-KQEBYXYB.js';
12
12
  import './chunk-JYV25VNZ.js';
13
- import './chunk-ICKIPKCC.js';
13
+ import './chunk-RE4QQM4Q.js';
14
14
  import './chunk-5FJFN3IW.js';
15
15
  import './chunk-GD26FI4J.js';
16
16
  import './chunk-OM2GRPDH.js';
17
- import './chunk-J52AQPKU.js';
17
+ import './chunk-GLE6TNJ3.js';
18
18
  import './chunk-OUW6WESU.js';
19
19
  import './chunk-JWXJPJBO.js';
20
20
  export { mergeDocumentDescriptors as mergeAttrs } from './chunk-CW625T2E.js';
21
- import './chunk-5EKTGKJY.js';
22
- import './chunk-WNV73ZFT.js';
21
+ import './chunk-FN2GQ2B5.js';
22
+ import './chunk-5LP2K2J7.js';
23
23
  import './chunk-TYNZ5B2B.js';
24
24
  import './chunk-BYMOPN2S.js';
25
25
  import './chunk-2TLIULJZ.js';
@@ -1,6 +1,6 @@
1
1
  export { stripUnsafeHtml } from '../chunk-RUHSV4Z6.js';
2
2
  export { formatSallaPlural } from '../chunk-BT6HEEAP.js';
3
- export { dispatchRouteChanged } from '../chunk-PCGKPANV.js';
3
+ export { dispatchRouteChanged } from '../chunk-IGGIH6GL.js';
4
4
  export { sortOptions } from '../chunk-3LZJXKVO.js';
5
5
  export { color } from '../chunk-VVZNW5CB.js';
6
6
  export { applyAppScopeMocks, registerThemeHookSlots } from '../chunk-5FJFN3IW.js';
@@ -8,7 +8,7 @@ export { mergeHead, resolveHead, setHeadAdapter } from '../chunk-GD26FI4J.js';
8
8
  export { buildBaseHead, buildHreflangAlternates, localeToOgLocale } from '../chunk-OM2GRPDH.js';
9
9
  export { asset, cdn, isPlaceholder } from '../chunk-ZCH7W7DZ.js';
10
10
  export { mergeDocumentDescriptors, mergeElementAttrs, normalizeClasses, syncAttrsToElement, toReactProps } from '../chunk-CW625T2E.js';
11
- import '../chunk-WNV73ZFT.js';
11
+ import '../chunk-5LP2K2J7.js';
12
12
  export { getSallaSDK, getStoreIdentifier, onSallaEvent, whenSallaLoaded, whenSallaReady } from '../chunk-GYXUYHKO.js';
13
13
  export { AUTH_TOKEN_COOKIE_NAME, clearAuthTokenCookie, extractTokenFromCookies } from '../chunk-FHU2MXZA.js';
14
14
  import '../chunk-7VNVCHID.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salla.sa/twilight-theme-engine",
3
- "version": "1.0.124",
3
+ "version": "1.0.126",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -205,6 +205,18 @@ function resolveComponentConfig(path: string): HomeComponentConfig | undefined {
205
205
  return themeComponentConfig[path] ?? COMPONENT_CONFIG[path];
206
206
  }
207
207
 
208
+ /**
209
+ * What the theme editor addresses a block by: the dev editor's layout id (less
210
+ * the remount suffix), its row id, or the uuid of one not saved yet. The layout
211
+ * id comes first: a customized layout keeps the store blocks' row ids among
212
+ * their fields, and duplicates share them.
213
+ */
214
+ function liveBlockIdOf(data: HomeComponentData): string | undefined {
215
+ const devId = typeof data.__dev_id === 'string' ? data.__dev_id.replace(/~\d+$/, '') : undefined;
216
+ const id = devId ?? data.id ?? data.key;
217
+ return typeof id === 'number' || (typeof id === 'string' && id) ? String(id) : undefined;
218
+ }
219
+
208
220
  /**
209
221
  * Memoized component renderer with scroll-based lazy loading
210
222
  * Components only mount when scrolled into view - preventing API calls until needed
@@ -253,12 +265,7 @@ export const HomeComponentRenderer = memo(function HomeComponentRenderer({
253
265
  ? config.id(data)
254
266
  : config.id
255
267
  : undefined;
256
- // What the theme editor addresses the block by: its row id, or the uuid of one not saved yet.
257
- const blockId = data.id ?? data.key;
258
- const liveBlockId =
259
- typeof blockId === 'number' || (typeof blockId === 'string' && blockId)
260
- ? String(blockId)
261
- : undefined;
268
+ const liveBlockId = liveBlockIdOf(data);
262
269
  const height = config?.height || '400px';
263
270
  // A theme's config replaces the engine's, but keeps its mount timing unless the theme sets one.
264
271
  const renderOnMount = config?.renderOnMount ?? COMPONENT_CONFIG[data.path]?.renderOnMount;
@@ -55,6 +55,8 @@ const BLOCK_ACTIONS: BlockAction[] = [
55
55
  'delete',
56
56
  ];
57
57
 
58
+ const DEV_ACTIONS = BLOCK_ACTIONS.filter((action) => action !== 'hide');
59
+
58
60
  /** Lucide-style 24px stroke icons. */
59
61
  const ICONS: Record<BlockAction, ReactElement> = {
60
62
  edit: (
@@ -172,6 +174,29 @@ function connected(el: HTMLElement | null): HTMLElement | null {
172
174
  }
173
175
 
174
176
  /** Re-renders on scroll, resize and layout shifts, so the overlay follows what it outlines. */
177
+ interface Box {
178
+ top: number;
179
+ left: number;
180
+ width: number;
181
+ height: number;
182
+ }
183
+
184
+ /**
185
+ * Where `el` is on screen. One with no box of its own (`display: contents`, as
186
+ * Raed's header) measures 0×0 at the corner, so it's measured by its children.
187
+ */
188
+ function boxOf(el: Element): Box {
189
+ const rect = el.getBoundingClientRect();
190
+ if (rect.width || rect.height) return rect;
191
+ const boxes = Array.from(el.children, boxOf).filter((box) => box.width || box.height);
192
+ if (!boxes.length) return rect;
193
+ const top = Math.min(...boxes.map((box) => box.top));
194
+ const left = Math.min(...boxes.map((box) => box.left));
195
+ const right = Math.max(...boxes.map((box) => box.left + box.width));
196
+ const bottom = Math.max(...boxes.map((box) => box.top + box.height));
197
+ return { top, left, width: right - left, height: bottom - top };
198
+ }
199
+
175
200
  function useLayoutTick(active: boolean): void {
176
201
  const [, setTick] = useState(0);
177
202
  useEffect(() => {
@@ -253,7 +278,7 @@ function useHeldLinkClicks(barRef: RefObject<HTMLDivElement | null>): void {
253
278
  }
254
279
 
255
280
  function Outline({ el, label }: { el: HTMLElement; label?: string }) {
256
- const rect = el.getBoundingClientRect();
281
+ const rect = boxOf(el);
257
282
  return (
258
283
  <div
259
284
  data-testid="live-editor-outline"
@@ -269,12 +294,13 @@ interface ToolbarProps {
269
294
  labels: Labels;
270
295
  busy: BlockAction | null;
271
296
  barRef: RefObject<HTMLDivElement | null>;
297
+ actions: BlockAction[];
272
298
  onAct: (el: HTMLElement, action: BlockAction) => void;
273
299
  }
274
300
 
275
301
  /** One edit button for the header or footer; the full set for a block. */
276
- function Toolbar({ target, labels, busy, barRef, onAct }: ToolbarProps) {
277
- const rect = target.getBoundingClientRect();
302
+ function Toolbar({ target, labels, busy, barRef, actions, onAct }: ToolbarProps) {
303
+ const rect = boxOf(target);
278
304
  const style: CSSProperties = {
279
305
  ...BAR,
280
306
  top: Math.max(rect.top + 8, 8),
@@ -312,7 +338,7 @@ function Toolbar({ target, labels, busy, barRef, onAct }: ToolbarProps) {
312
338
  aria-label={labels.edit}
313
339
  style={style}
314
340
  >
315
- {BLOCK_ACTIONS.map((action) => (
341
+ {actions.map((action) => (
316
342
  <button
317
343
  key={action}
318
344
  type="button"
@@ -348,8 +374,17 @@ function blockRefOf(el: HTMLElement, id: string): { id: string; copy?: number }
348
374
  * edit, hide, move, duplicate, add and delete; hovering the header or footer
349
375
  * shows an edit button. Double-clicking either edits it. Each action is a
350
376
  * message to the editor, which saves the change and reloads the preview.
377
+ *
378
+ * In the engine's dev editor (`dev`) edits reach the page live, so nothing
379
+ * waits for a reload, and there is no hide: its layout has no hidden blocks.
351
380
  */
352
- export function LiveEditorBar({ origin }: { origin: string }): ReactElement | null {
381
+ export function LiveEditorBar({
382
+ origin,
383
+ dev = false,
384
+ }: {
385
+ origin: string;
386
+ dev?: boolean;
387
+ }): ReactElement | null {
353
388
  const [hovered, setHovered] = useState<HTMLElement | null>(null);
354
389
  const [editing, setEditing] = useState<HTMLElement | null>(null);
355
390
  const [busy, setBusy] = useState<BlockAction | null>(null);
@@ -371,9 +406,10 @@ export function LiveEditorBar({ origin }: { origin: string }): ReactElement | nu
371
406
  if (!id) return;
372
407
  postToEditor(origin, `preview::${action}.component`, blockRefOf(el, id));
373
408
  if (action === 'edit') setEditing(el);
374
- else if (action === 'hide' || action === 'duplicate' || action === 'delete') setBusy(action);
409
+ else if (!dev && (action === 'hide' || action === 'duplicate' || action === 'delete'))
410
+ setBusy(action);
375
411
  },
376
- [origin]
412
+ [origin, dev]
377
413
  );
378
414
 
379
415
  useEffect(() => {
@@ -421,7 +457,14 @@ export function LiveEditorBar({ origin }: { origin: string }): ReactElement | nu
421
457
  {selected && selected !== target && <Outline el={selected} />}
422
458
  {target && <Outline el={target} label={targetArea ? labels[targetArea] : undefined} />}
423
459
  {target && (
424
- <Toolbar target={target} labels={labels} busy={busy} barRef={barRef} onAct={act} />
460
+ <Toolbar
461
+ target={target}
462
+ labels={labels}
463
+ busy={busy}
464
+ barRef={barRef}
465
+ actions={dev ? DEV_ACTIONS : BLOCK_ACTIONS}
466
+ onAct={act}
467
+ />
425
468
  )}
426
469
  </>,
427
470
  host
@@ -5,6 +5,8 @@
5
5
  * protocol the twig themes' `live-themes-editor.js` speaks.
6
6
  */
7
7
 
8
+ import { isEditorFrame } from '../../dev/schema-types';
9
+
8
10
  /** Where the theme editor runs. Messages go to the one that framed the page, never to `*`. */
9
11
  export const EDITOR_ORIGINS = [
10
12
  'https://s.salla.sa',
@@ -47,6 +49,21 @@ function parentOrigin(): string | null {
47
49
  }
48
50
  }
49
51
 
52
+ /**
53
+ * On a dev server, a page on another localhost port (an editor run locally, or
54
+ * a test harness) can frame the store too. Not its own origin: that is the
55
+ * engine's dev editor, which has its own controls.
56
+ */
57
+ function isLocalEditor(origin: string): boolean {
58
+ if (origin === window.location.origin) return false;
59
+ try {
60
+ const { protocol, hostname } = new URL(origin);
61
+ return protocol === 'http:' && (hostname === 'localhost' || hostname === '127.0.0.1');
62
+ } catch {
63
+ return false;
64
+ }
65
+ }
66
+
50
67
  /** The theme editor's origin when it frames this page, otherwise `null`. Browser only. */
51
68
  export function editorOrigin(): string | null {
52
69
  if (typeof window === 'undefined') return null;
@@ -56,7 +73,23 @@ export function editorOrigin(): string | null {
56
73
  } catch {
57
74
  return null;
58
75
  }
59
- return origin && (EDITOR_ORIGINS as readonly string[]).includes(origin) ? origin : null;
76
+ if (!origin) return null;
77
+ if ((EDITOR_ORIGINS as readonly string[]).includes(origin)) return origin;
78
+ return import.meta.env.DEV && isLocalEditor(origin) ? origin : null;
79
+ }
80
+
81
+ export interface EditorFrame {
82
+ origin: string;
83
+ /** The engine's own dev editor (`/__twilight/editor`): edits apply live, and blocks can't be hidden. */
84
+ dev: boolean;
85
+ }
86
+
87
+ /** The editor framing this page, the merchant's or (on a dev server) the engine's own. Browser only. */
88
+ export function editorFrame(): EditorFrame | null {
89
+ if (typeof window === 'undefined') return null;
90
+ if (import.meta.env.DEV && isEditorFrame()) return { origin: window.location.origin, dev: true };
91
+ const origin = editorOrigin();
92
+ return origin ? { origin, dev: false } : null;
60
93
  }
61
94
 
62
95
  export function postToEditor(
@@ -1,5 +1,5 @@
1
1
  import { lazy, Suspense, useEffect, useState, type ReactElement } from 'react';
2
- import { editorOrigin, FRAME_LOADED_ACTION, postToEditor } from './frame';
2
+ import { editorFrame, FRAME_LOADED_ACTION, postToEditor, type EditorFrame } from './frame';
3
3
 
4
4
  /** Off the shell chunk: only a store framed by the theme editor ever loads it. */
5
5
  const LazyLiveEditorBar = lazy(() =>
@@ -13,13 +13,15 @@ const LazyLiveEditorBar = lazy(() =>
13
13
  * nothing on the server, so it changes no markup.
14
14
  */
15
15
  export function LiveEditor(): ReactElement | null {
16
- const [origin, setOrigin] = useState<string | null>(null);
16
+ const [frame, setFrame] = useState<EditorFrame | null>(null);
17
17
 
18
18
  useEffect(() => {
19
- const found = editorOrigin();
19
+ const found = editorFrame();
20
20
  if (!found) return;
21
- setOrigin(found);
22
- const announce = () => postToEditor(found, FRAME_LOADED_ACTION);
21
+ setFrame(found);
22
+ // The merchant's editor checks the preview's health with it; the dev editor doesn't.
23
+ if (found.dev) return;
24
+ const announce = () => postToEditor(found.origin, FRAME_LOADED_ACTION);
23
25
  if (document.readyState === 'complete') {
24
26
  announce();
25
27
  return;
@@ -28,10 +30,10 @@ export function LiveEditor(): ReactElement | null {
28
30
  return () => window.removeEventListener('load', announce);
29
31
  }, []);
30
32
 
31
- if (!origin) return null;
33
+ if (!frame) return null;
32
34
  return (
33
35
  <Suspense fallback={null}>
34
- <LazyLiveEditorBar origin={origin} />
36
+ <LazyLiveEditorBar origin={frame.origin} dev={frame.dev} />
35
37
  </Suspense>
36
38
  );
37
39
  }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/live-editor/LiveEditorBar.tsx"],"names":[],"mappings":"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","file":"LiveEditorBar-WSASAFCL.js","sourcesContent":["import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactElement,\n type RefObject,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { useTranslation } from '../../providers/I18nProvider';\nimport { BLOCK_ATTRIBUTE, areaOf, postToEditor, targetOf, type BlockAction } from './frame';\n\nconst ACCENT = '#5dd5c4';\nconst LAYER = 2147483000;\n/** The editor reloads the preview after hide, duplicate and delete; stop waiting if it doesn't. */\nconst BUSY_TIMEOUT_MS = 10_000;\n/** How long a link's click waits for a second one, so a double-click edits instead of navigating. */\nconst DOUBLE_CLICK_MS = 300;\n\n/** Defaults for the `blocks.live_editor.*` keys, in the store's language. */\nconst DEFAULT_LABELS = {\n ar: {\n edit: 'تعديل',\n hide: 'إخفاء',\n moveup: 'تحريك لأعلى',\n movedown: 'تحريك لأسفل',\n duplicate: 'تكرار',\n add: 'إضافة عنصر',\n delete: 'حذف',\n header: 'رأس الصفحة',\n footer: 'ذيل الصفحة',\n },\n en: {\n edit: 'Edit',\n hide: 'Hide',\n moveup: 'Move up',\n movedown: 'Move down',\n duplicate: 'Duplicate',\n add: 'Add element',\n delete: 'Delete',\n header: 'Header',\n footer: 'Footer',\n },\n} as const;\n\n/** Twig's order, read from the start of the line: edit first, delete last. */\nconst BLOCK_ACTIONS: BlockAction[] = [\n 'edit',\n 'hide',\n 'moveup',\n 'movedown',\n 'duplicate',\n 'add',\n 'delete',\n];\n\n/** Lucide-style 24px stroke icons. */\nconst ICONS: Record<BlockAction, ReactElement> = {\n edit: (\n <>\n <path d=\"M12 20h9\" />\n <path d=\"M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z\" />\n </>\n ),\n hide: (\n <>\n <path d=\"M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 10 8 10 8a13.16 13.16 0 0 1-1.67 2.68\" />\n <path d=\"M6.61 6.61A13.53 13.53 0 0 0 2 12s3 8 10 8a9.74 9.74 0 0 0 5.39-1.61\" />\n <path d=\"M9.88 9.88a3 3 0 1 0 4.24 4.24\" />\n <path d=\"m2 2 20 20\" />\n </>\n ),\n moveup: <path d=\"m18 15-6-6-6 6\" />,\n movedown: <path d=\"m6 9 6 6 6-6\" />,\n duplicate: (\n <>\n <rect x=\"8\" y=\"8\" width=\"14\" height=\"14\" rx=\"2\" />\n <path d=\"M4 16a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2\" />\n </>\n ),\n add: <path d=\"M12 5v14M5 12h14\" />,\n delete: (\n <>\n <path d=\"M3 6h18\" />\n <path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6\" />\n <path d=\"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\" />\n </>\n ),\n};\n\nconst OUTLINE: CSSProperties = {\n position: 'fixed',\n zIndex: LAYER,\n pointerEvents: 'none',\n boxSizing: 'border-box',\n border: `2px solid ${ACCENT}`,\n};\n\nconst CHIP: CSSProperties = {\n position: 'absolute',\n top: 0,\n insetInlineStart: 0,\n padding: '4px 12px',\n background: ACCENT,\n color: '#0f172a',\n font: '500 14px/1.4 system-ui, sans-serif',\n whiteSpace: 'nowrap',\n};\n\nconst BAR: CSSProperties = {\n position: 'fixed',\n zIndex: LAYER + 1,\n display: 'flex',\n gap: 4,\n padding: 6,\n background: '#fff',\n borderRadius: 8,\n boxShadow: '0 4px 16px rgba(15, 23, 42, 0.18)',\n transform: 'translateX(-50%)',\n};\n\nfunction buttonStyle(action: BlockAction | 'area', disabled: boolean): CSSProperties {\n return {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 6,\n minWidth: 36,\n height: 36,\n padding: '0 8px',\n border: 0,\n borderRadius: 6,\n background: 'transparent',\n color: action === 'delete' ? '#e11d48' : action === 'edit' ? '#0d9488' : '#475569',\n font: '500 14px/1 system-ui, sans-serif',\n cursor: disabled ? 'default' : 'pointer',\n opacity: disabled ? 0.35 : 1,\n };\n}\n\nfunction Icon({ children }: { children: ReactElement }) {\n return (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n {children}\n </svg>\n );\n}\n\n/** The blocks `el` can trade places with: its siblings in the same list. */\nfunction blockSiblings(el: HTMLElement): HTMLElement[] {\n const parent = el.parentElement;\n if (!parent) return [el];\n return Array.from(parent.children).filter(\n (child): child is HTMLElement =>\n child instanceof HTMLElement && child.hasAttribute(BLOCK_ATTRIBUTE)\n );\n}\n\nfunction connected(el: HTMLElement | null): HTMLElement | null {\n return el?.isConnected ? el : null;\n}\n\n/** Re-renders on scroll, resize and layout shifts, so the overlay follows what it outlines. */\nfunction useLayoutTick(active: boolean): void {\n const [, setTick] = useState(0);\n useEffect(() => {\n if (!active) return;\n let frame = 0;\n const schedule = () => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(() => setTick((n) => n + 1));\n };\n window.addEventListener('scroll', schedule, { capture: true, passive: true });\n window.addEventListener('resize', schedule);\n const observer = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(schedule) : null;\n observer?.observe(document.body);\n return () => {\n cancelAnimationFrame(frame);\n window.removeEventListener('scroll', schedule, { capture: true });\n window.removeEventListener('resize', schedule);\n observer?.disconnect();\n };\n }, [active]);\n}\n\ntype LabelKey = keyof typeof DEFAULT_LABELS.en;\ntype Labels = Record<LabelKey, string>;\n\nfunction useLabels(): Labels {\n const { t, i18n } = useTranslation();\n const defaults = i18n?.language?.startsWith('ar') ? DEFAULT_LABELS.ar : DEFAULT_LABELS.en;\n return Object.fromEntries(\n (Object.keys(defaults) as LabelKey[]).map((key) => [\n key,\n t(`blocks.live_editor.${key}`, { defaultValue: defaults[key] }),\n ])\n ) as Labels;\n}\n\n/**\n * Holds a link's click inside a block, the header or the footer for\n * {@link DOUBLE_CLICK_MS}: a second click on the same link makes it a\n * double-click, which edits the area, and the link never navigates; otherwise\n * the click is replayed. A click on another link drops the first one.\n */\nfunction useHeldLinkClicks(barRef: RefObject<HTMLDivElement | null>): void {\n useEffect(() => {\n let held: { link: HTMLAnchorElement; timer: number } | null = null;\n let replaying = false;\n\n const onClick = (event: MouseEvent) => {\n if (replaying || event.button !== 0) return;\n if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;\n if (barRef.current?.contains(event.target as Node)) return;\n const link = event.target instanceof Element ? event.target.closest('a[href]') : null;\n if (!(link instanceof HTMLAnchorElement) || !targetOf(link)) return;\n\n // Before the router's own handler, which sits below the document.\n event.preventDefault();\n event.stopPropagation();\n const previous = held;\n if (previous) {\n window.clearTimeout(previous.timer);\n held = null;\n if (previous.link === link) return;\n }\n const timer = window.setTimeout(() => {\n held = null;\n replaying = true;\n link.click();\n replaying = false;\n }, DOUBLE_CLICK_MS);\n held = { link, timer };\n };\n\n document.addEventListener('click', onClick, true);\n return () => {\n document.removeEventListener('click', onClick, true);\n if (held) window.clearTimeout(held.timer);\n };\n }, [barRef]);\n}\n\nfunction Outline({ el, label }: { el: HTMLElement; label?: string }) {\n const rect = el.getBoundingClientRect();\n return (\n <div\n data-testid=\"live-editor-outline\"\n style={{ ...OUTLINE, top: rect.top, left: rect.left, width: rect.width, height: rect.height }}\n >\n {label && <span style={CHIP}>{label}</span>}\n </div>\n );\n}\n\ninterface ToolbarProps {\n target: HTMLElement;\n labels: Labels;\n busy: BlockAction | null;\n barRef: RefObject<HTMLDivElement | null>;\n onAct: (el: HTMLElement, action: BlockAction) => void;\n}\n\n/** One edit button for the header or footer; the full set for a block. */\nfunction Toolbar({ target, labels, busy, barRef, onAct }: ToolbarProps) {\n const rect = target.getBoundingClientRect();\n const style: CSSProperties = {\n ...BAR,\n top: Math.max(rect.top + 8, 8),\n left: rect.left + rect.width / 2,\n };\n\n if (areaOf(target)) {\n return (\n <div ref={barRef} data-testid=\"live-editor-bar\" style={style}>\n <button\n type=\"button\"\n data-testid=\"live-editor-edit\"\n style={buttonStyle('area', false)}\n onClick={() => onAct(target, 'edit')}\n >\n <Icon>{ICONS.edit}</Icon>\n {labels.edit}\n </button>\n </div>\n );\n }\n\n const siblings = blockSiblings(target);\n const index = siblings.indexOf(target);\n const isDisabled = (action: BlockAction) =>\n busy !== null ||\n (action === 'moveup' && index <= 0) ||\n (action === 'movedown' && index >= siblings.length - 1);\n\n return (\n <div\n ref={barRef}\n data-testid=\"live-editor-bar\"\n role=\"toolbar\"\n aria-label={labels.edit}\n style={style}\n >\n {BLOCK_ACTIONS.map((action) => (\n <button\n key={action}\n type=\"button\"\n data-testid={`live-editor-${action}`}\n title={labels[action]}\n aria-label={labels[action]}\n aria-busy={busy === action || undefined}\n disabled={isDisabled(action)}\n style={buttonStyle(action, isDisabled(action))}\n onClick={() => onAct(target, action)}\n >\n <Icon>{ICONS[action]}</Icon>\n </button>\n ))}\n </div>\n );\n}\n\n/**\n * The block an action names. A uuid is shared by every copy of one component,\n * so it comes with which copy, in page order; a row id is unique on its own.\n */\nfunction blockRefOf(el: HTMLElement, id: string): { id: string; copy?: number } {\n if (/^\\d+$/.test(id)) return { id };\n const copies = Array.from(document.querySelectorAll(`[${BLOCK_ATTRIBUTE}]`)).filter(\n (block) => block.getAttribute(BLOCK_ATTRIBUTE) === id\n );\n return { id, copy: copies.indexOf(el) };\n}\n\n/**\n * The live editor's action bar: hovering a home block outlines it and shows\n * edit, hide, move, duplicate, add and delete; hovering the header or footer\n * shows an edit button. Double-clicking either edits it. Each action is a\n * message to the editor, which saves the change and reloads the preview.\n */\nexport function LiveEditorBar({ origin }: { origin: string }): ReactElement | null {\n const [hovered, setHovered] = useState<HTMLElement | null>(null);\n const [editing, setEditing] = useState<HTMLElement | null>(null);\n const [busy, setBusy] = useState<BlockAction | null>(null);\n const barRef = useRef<HTMLDivElement>(null);\n // Taken after mount, so nothing reads the DOM while rendering.\n const [host, setHost] = useState<HTMLElement | null>(null);\n const labels = useLabels();\n useHeldLinkClicks(barRef);\n\n const act = useCallback(\n (el: HTMLElement, action: BlockAction) => {\n const area = areaOf(el);\n if (area) {\n postToEditor(origin, `preview::edit.${area}`, { id: `store-${area}` });\n setEditing(el);\n return;\n }\n const id = el.getAttribute(BLOCK_ATTRIBUTE);\n if (!id) return;\n postToEditor(origin, `preview::${action}.component`, blockRefOf(el, id));\n if (action === 'edit') setEditing(el);\n else if (action === 'hide' || action === 'duplicate' || action === 'delete') setBusy(action);\n },\n [origin]\n );\n\n useEffect(() => {\n const onOver = (event: MouseEvent) => {\n // The bar sits over the block it acts on: reaching it keeps that block.\n if (barRef.current?.contains(event.target as Node)) return;\n setHovered(targetOf(event.target));\n };\n const onOut = (event: MouseEvent) => {\n if (!event.relatedTarget) setHovered(null);\n };\n const onDoubleClick = (event: MouseEvent) => {\n if (barRef.current?.contains(event.target as Node)) return;\n const el = targetOf(event.target);\n if (el) act(el, 'edit');\n };\n document.addEventListener('mouseover', onOver);\n document.addEventListener('mouseout', onOut);\n document.addEventListener('dblclick', onDoubleClick);\n return () => {\n document.removeEventListener('mouseover', onOver);\n document.removeEventListener('mouseout', onOut);\n document.removeEventListener('dblclick', onDoubleClick);\n };\n }, [act]);\n\n useEffect(() => setHost(document.body), []);\n\n useEffect(() => {\n if (!busy) return;\n const timer = window.setTimeout(() => setBusy(null), BUSY_TIMEOUT_MS);\n return () => window.clearTimeout(timer);\n }, [busy]);\n\n const target = connected(hovered);\n const selected = connected(editing);\n useLayoutTick(Boolean(target || selected));\n\n if (!host || (!target && !selected)) return null;\n\n const targetArea = target ? areaOf(target) : null;\n\n return createPortal(\n <>\n {selected && selected !== target && <Outline el={selected} />}\n {target && <Outline el={target} label={targetArea ? labels[targetArea] : undefined} />}\n {target && (\n <Toolbar target={target} labels={labels} busy={busy} barRef={barRef} onAct={act} />\n )}\n </>,\n host\n );\n}\n"]}