@salla.sa/twilight-theme-engine 1.0.123 → 1.0.125

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-VFCTMUDL.js → AddToCartForm-Q6Y7BTDC.js} +3 -3
  2. package/dist/{AddToCartForm-VFCTMUDL.js.map → AddToCartForm-Q6Y7BTDC.js.map} +1 -1
  3. package/dist/{BlogAuthorPage-QVHRFMBQ.js → BlogAuthorPage-N34SKQIU.js} +17 -17
  4. package/dist/{BlogAuthorPage-QVHRFMBQ.js.map → BlogAuthorPage-N34SKQIU.js.map} +1 -1
  5. package/dist/{BlogCategoryPage-BP5WIW7X.js → BlogCategoryPage-FIQZZVFS.js} +17 -17
  6. package/dist/{BlogCategoryPage-BP5WIW7X.js.map → BlogCategoryPage-FIQZZVFS.js.map} +1 -1
  7. package/dist/{BlogPage-YUZCR2YV.js → BlogPage-HKUIDCPI.js} +15 -15
  8. package/dist/{BlogPage-YUZCR2YV.js.map → BlogPage-HKUIDCPI.js.map} +1 -1
  9. package/dist/{BlogSinglePage-BJHC7DVC.js → BlogSinglePage-YGMH5NZ2.js} +15 -15
  10. package/dist/{BlogSinglePage-BJHC7DVC.js.map → BlogSinglePage-YGMH5NZ2.js.map} +1 -1
  11. package/dist/{BlogTagPage-ND5KMCZZ.js → BlogTagPage-64BDH4C7.js} +17 -17
  12. package/dist/{BlogTagPage-ND5KMCZZ.js.map → BlogTagPage-64BDH4C7.js.map} +1 -1
  13. package/dist/{BrandsPage-FGJBQBFL.js → BrandsPage-TCYFRA6G.js} +16 -16
  14. package/dist/{BrandsPage-FGJBQBFL.js.map → BrandsPage-TCYFRA6G.js.map} +1 -1
  15. package/dist/{Breadcrumb-2LPY6KHS.js → Breadcrumb-25EMZG3Z.js} +15 -15
  16. package/dist/{Breadcrumb-2LPY6KHS.js.map → Breadcrumb-25EMZG3Z.js.map} +1 -1
  17. package/dist/{CartItem-UQF332H4.js → CartItem-QIGMCSE2.js} +15 -15
  18. package/dist/{CartItem-UQF332H4.js.map → CartItem-QIGMCSE2.js.map} +1 -1
  19. package/dist/{CartSummary-FV3I7CJV.js → CartSummary-QWTWFV6R.js} +15 -15
  20. package/dist/{CartSummary-FV3I7CJV.js.map → CartSummary-QWTWFV6R.js.map} +1 -1
  21. package/dist/{Copyright-AZHDEIJX.js → Copyright-XUD6PWUP.js} +3 -3
  22. package/dist/{Copyright-AZHDEIJX.js.map → Copyright-XUD6PWUP.js.map} +1 -1
  23. package/dist/{CustomerLayout-DYA77OLE.js → CustomerLayout-NIENY4TF.js} +15 -15
  24. package/dist/{CustomerLayout-DYA77OLE.js.map → CustomerLayout-NIENY4TF.js.map} +1 -1
  25. package/dist/{Footer-BCFWCWWI.js → Footer-3CF5V7OE.js} +4 -4
  26. package/dist/{Footer-BCFWCWWI.js.map → Footer-3CF5V7OE.js.map} +1 -1
  27. package/dist/{Header-HALNPMED.js → Header-O4Z6HYNA.js} +8 -8
  28. package/dist/{Header-HALNPMED.js.map → Header-O4Z6HYNA.js.map} +1 -1
  29. package/dist/{LiveEditorBar-6SKEWJOX.js → LiveEditorBar-IQLFFBHE.js} +46 -12
  30. package/dist/LiveEditorBar-IQLFFBHE.js.map +1 -0
  31. package/dist/{LoyaltyPage-LUQLKTDF.js → LoyaltyPage-DATO5W6R.js} +15 -15
  32. package/dist/{LoyaltyPage-LUQLKTDF.js.map → LoyaltyPage-DATO5W6R.js.map} +1 -1
  33. package/dist/{LoyaltyPointsPage-LJLCJBLR.js → LoyaltyPointsPage-YQAJ6PNQ.js} +6 -6
  34. package/dist/{LoyaltyPointsPage-LJLCJBLR.js.map → LoyaltyPointsPage-YQAJ6PNQ.js.map} +1 -1
  35. package/dist/{MainMenu-WPVP2XF6.js → MainMenu-NUJSMS3W.js} +5 -5
  36. package/dist/{MainMenu-WPVP2XF6.js.map → MainMenu-NUJSMS3W.js.map} +1 -1
  37. package/dist/{MasterLayout-UPFHTRV6.js → MasterLayout-6SJCANZN.js} +15 -15
  38. package/dist/{MasterLayout-UPFHTRV6.js.map → MasterLayout-6SJCANZN.js.map} +1 -1
  39. package/dist/{MobileMenu-KMTRWYHV.js → MobileMenu-N7P6Z6TM.js} +16 -16
  40. package/dist/{MobileMenu-KMTRWYHV.js.map → MobileMenu-N7P6Z6TM.js.map} +1 -1
  41. package/dist/{NotificationsPage-5GDS22E6.js → NotificationsPage-N4DAQ7RS.js} +15 -15
  42. package/dist/{NotificationsPage-5GDS22E6.js.map → NotificationsPage-N4DAQ7RS.js.map} +1 -1
  43. package/dist/{OrderSinglePage-EXVTMRJI.js → OrderSinglePage-NMWLWFX4.js} +16 -16
  44. package/dist/{OrderSinglePage-EXVTMRJI.js.map → OrderSinglePage-NMWLWFX4.js.map} +1 -1
  45. package/dist/{OrdersPage-JNIDVHV3.js → OrdersPage-WGA6SAU7.js} +15 -15
  46. package/dist/{OrdersPage-JNIDVHV3.js.map → OrdersPage-WGA6SAU7.js.map} +1 -1
  47. package/dist/{PageSingle-KX5XSBWJ.js → PageSingle-MZQWB3OW.js} +3 -3
  48. package/dist/{PageSingle-KX5XSBWJ.js.map → PageSingle-MZQWB3OW.js.map} +1 -1
  49. package/dist/{ProductDetails-7EI5PTFY.js → ProductDetails-HYVU4GWC.js} +4 -4
  50. package/dist/{ProductDetails-7EI5PTFY.js.map → ProductDetails-HYVU4GWC.js.map} +1 -1
  51. package/dist/{ProfilePage-6FSWT3RC.js → ProfilePage-M5RBRGMH.js} +15 -15
  52. package/dist/{ProfilePage-6FSWT3RC.js.map → ProfilePage-M5RBRGMH.js.map} +1 -1
  53. package/dist/{ReviewsPage-2JSZHLPR.js → ReviewsPage-PEQC4IDJ.js} +3 -3
  54. package/dist/{ReviewsPage-2JSZHLPR.js.map → ReviewsPage-PEQC4IDJ.js.map} +1 -1
  55. package/dist/{SeoCartWidget-2MI5P2RT.js → SeoCartWidget-P2MT5BRA.js} +15 -15
  56. package/dist/{SeoCartWidget-2MI5P2RT.js.map → SeoCartWidget-P2MT5BRA.js.map} +1 -1
  57. package/dist/{SettingsPage-BX3SZNO5.js → SettingsPage-OMPK72Z7.js} +15 -15
  58. package/dist/{SettingsPage-BX3SZNO5.js.map → SettingsPage-OMPK72Z7.js.map} +1 -1
  59. package/dist/{StoreClosedNotification-E7WEFZGO.js → StoreClosedNotification-NWEQHHAR.js} +16 -16
  60. package/dist/{StoreClosedNotification-E7WEFZGO.js.map → StoreClosedNotification-NWEQHHAR.js.map} +1 -1
  61. package/dist/{TestimonialsPage-5OEZMBRF.js → TestimonialsPage-4AUF6ZWG.js} +3 -3
  62. package/dist/{TestimonialsPage-5OEZMBRF.js.map → TestimonialsPage-4AUF6ZWG.js.map} +1 -1
  63. package/dist/{ThankYouPage-3XJUFGSE.js → ThankYouPage-R3EWHCT7.js} +15 -15
  64. package/dist/{ThankYouPage-3XJUFGSE.js.map → ThankYouPage-R3EWHCT7.js.map} +1 -1
  65. package/dist/{WalletPage-LUSSC5TL.js → WalletPage-KHQGRDQB.js} +16 -16
  66. package/dist/{WalletPage-LUSSC5TL.js.map → WalletPage-KHQGRDQB.js.map} +1 -1
  67. package/dist/{WishlistPage-JK2JLLMR.js → WishlistPage-YJXOMBQ4.js} +17 -17
  68. package/dist/{WishlistPage-JK2JLLMR.js.map → WishlistPage-YJXOMBQ4.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-N67C5QRH.js → chunk-2CJGX6QU.js} +3 -3
  90. package/dist/{chunk-N67C5QRH.js.map → chunk-2CJGX6QU.js.map} +1 -1
  91. package/dist/{chunk-3L4TXBS7.js → chunk-2YCR7EEN.js} +3 -3
  92. package/dist/{chunk-3L4TXBS7.js.map → chunk-2YCR7EEN.js.map} +1 -1
  93. package/dist/{chunk-IXZPQR4M.js → chunk-5D26ZL3R.js} +5 -5
  94. package/dist/{chunk-IXZPQR4M.js.map → chunk-5D26ZL3R.js.map} +1 -1
  95. package/dist/{chunk-WAZYTMTB.js → chunk-653SIKQV.js} +3 -3
  96. package/dist/{chunk-WAZYTMTB.js.map → chunk-653SIKQV.js.map} +1 -1
  97. package/dist/{chunk-SLXTJLF2.js → chunk-B3KRYCIN.js} +4 -4
  98. package/dist/{chunk-SLXTJLF2.js.map → chunk-B3KRYCIN.js.map} +1 -1
  99. package/dist/{chunk-EEZNB6HS.js → chunk-DHBDQF2L.js} +4 -4
  100. package/dist/{chunk-EEZNB6HS.js.map → chunk-DHBDQF2L.js.map} +1 -1
  101. package/dist/{chunk-2CORNDFS.js → chunk-DMOBC6K3.js} +3 -3
  102. package/dist/{chunk-2CORNDFS.js.map → chunk-DMOBC6K3.js.map} +1 -1
  103. package/dist/{chunk-HGEKTJVV.js → chunk-E6J4TSI4.js} +4 -4
  104. package/dist/{chunk-HGEKTJVV.js.map → chunk-E6J4TSI4.js.map} +1 -1
  105. package/dist/{chunk-C4GXSAYT.js → chunk-EADPFZYQ.js} +4 -4
  106. package/dist/{chunk-C4GXSAYT.js.map → chunk-EADPFZYQ.js.map} +1 -1
  107. package/dist/chunk-FMDGEAFD.js +14 -0
  108. package/dist/{chunk-DEY7MDGF.js.map → chunk-FMDGEAFD.js.map} +1 -1
  109. package/dist/{chunk-UBNOFV7C.js → chunk-HEGWAECO.js} +3 -3
  110. package/dist/{chunk-UBNOFV7C.js.map → chunk-HEGWAECO.js.map} +1 -1
  111. package/dist/{chunk-3BIT754E.js → chunk-I6SWCTCV.js} +4 -4
  112. package/dist/{chunk-3BIT754E.js.map → chunk-I6SWCTCV.js.map} +1 -1
  113. package/dist/{chunk-4KRALUHW.js → chunk-IVW2XKC3.js} +3 -3
  114. package/dist/{chunk-4KRALUHW.js.map → chunk-IVW2XKC3.js.map} +1 -1
  115. package/dist/{chunk-AFERG34I.js → chunk-IZTIOJNJ.js} +7 -7
  116. package/dist/{chunk-AFERG34I.js.map → chunk-IZTIOJNJ.js.map} +1 -1
  117. package/dist/{chunk-ESZBWB7X.js → chunk-JWBHXCLD.js} +3 -3
  118. package/dist/{chunk-ESZBWB7X.js.map → chunk-JWBHXCLD.js.map} +1 -1
  119. package/dist/{chunk-EPPCSJGJ.js → chunk-K4HJD556.js} +3 -3
  120. package/dist/{chunk-EPPCSJGJ.js.map → chunk-K4HJD556.js.map} +1 -1
  121. package/dist/{chunk-FLTPDDL3.js → chunk-LCJSZXXV.js} +7 -7
  122. package/dist/{chunk-FLTPDDL3.js.map → chunk-LCJSZXXV.js.map} +1 -1
  123. package/dist/{chunk-YPEWTS4L.js → chunk-MHS2SUIJ.js} +4 -4
  124. package/dist/{chunk-YPEWTS4L.js.map → chunk-MHS2SUIJ.js.map} +1 -1
  125. package/dist/{chunk-4X4FBQMO.js → chunk-MIKL53WC.js} +3 -3
  126. package/dist/{chunk-4X4FBQMO.js.map → chunk-MIKL53WC.js.map} +1 -1
  127. package/dist/{chunk-PDIFXQDX.js → chunk-MVNWWZWQ.js} +23 -4
  128. package/dist/chunk-MVNWWZWQ.js.map +1 -0
  129. package/dist/{chunk-3BCIBYJC.js → chunk-N5PGVFSO.js} +3 -3
  130. package/dist/{chunk-3BCIBYJC.js.map → chunk-N5PGVFSO.js.map} +1 -1
  131. package/dist/{chunk-ISTESEV6.js → chunk-N6X7ESEY.js} +7 -7
  132. package/dist/{chunk-ISTESEV6.js.map → chunk-N6X7ESEY.js.map} +1 -1
  133. package/dist/{chunk-MQLMYMEQ.js → chunk-NWYOIN5R.js} +3 -3
  134. package/dist/{chunk-MQLMYMEQ.js.map → chunk-NWYOIN5R.js.map} +1 -1
  135. package/dist/{chunk-74PTCV46.js → chunk-OBSTSW3E.js} +23 -22
  136. package/dist/chunk-OBSTSW3E.js.map +1 -0
  137. package/dist/{chunk-ITXVKNO7.js → chunk-OXUPXXE5.js} +4 -4
  138. package/dist/{chunk-ITXVKNO7.js.map → chunk-OXUPXXE5.js.map} +1 -1
  139. package/dist/{chunk-G2BBB5AI.js → chunk-P4M57HMD.js} +3 -3
  140. package/dist/{chunk-G2BBB5AI.js.map → chunk-P4M57HMD.js.map} +1 -1
  141. package/dist/{chunk-XK5CEGKA.js → chunk-PD7ZQY6T.js} +4 -4
  142. package/dist/{chunk-XK5CEGKA.js.map → chunk-PD7ZQY6T.js.map} +1 -1
  143. package/dist/{chunk-S5FRM4TG.js → chunk-PPTOCIKV.js} +6 -6
  144. package/dist/{chunk-S5FRM4TG.js.map → chunk-PPTOCIKV.js.map} +1 -1
  145. package/dist/{chunk-Y2OC2467.js → chunk-QFEJAVGD.js} +10 -10
  146. package/dist/{chunk-Y2OC2467.js.map → chunk-QFEJAVGD.js.map} +1 -1
  147. package/dist/{chunk-FXYR563Z.js → chunk-RVKKQGHR.js} +3 -3
  148. package/dist/{chunk-FXYR563Z.js.map → chunk-RVKKQGHR.js.map} +1 -1
  149. package/dist/{chunk-Z43BQ7ZH.js → chunk-SRMZHB5G.js} +4 -4
  150. package/dist/{chunk-Z43BQ7ZH.js.map → chunk-SRMZHB5G.js.map} +1 -1
  151. package/dist/{chunk-DPLMZQ2Q.js → chunk-ST6FMVXY.js} +3 -3
  152. package/dist/{chunk-DPLMZQ2Q.js.map → chunk-ST6FMVXY.js.map} +1 -1
  153. package/dist/{chunk-2Q6M3CE5.js → chunk-SVB3WGEG.js} +3 -3
  154. package/dist/{chunk-2Q6M3CE5.js.map → chunk-SVB3WGEG.js.map} +1 -1
  155. package/dist/{chunk-IMCS5L4A.js → chunk-UZXRQZV2.js} +3 -3
  156. package/dist/{chunk-IMCS5L4A.js.map → chunk-UZXRQZV2.js.map} +1 -1
  157. package/dist/{chunk-MDJK7WHN.js → chunk-V3E2UIZB.js} +4 -4
  158. package/dist/{chunk-MDJK7WHN.js.map → chunk-V3E2UIZB.js.map} +1 -1
  159. package/dist/{chunk-36FXIFZ6.js → chunk-VAX5TXTJ.js} +4 -4
  160. package/dist/{chunk-36FXIFZ6.js.map → chunk-VAX5TXTJ.js.map} +1 -1
  161. package/dist/{chunk-4JNLIYCE.js → chunk-XDZHVKBK.js} +3 -3
  162. package/dist/{chunk-4JNLIYCE.js.map → chunk-XDZHVKBK.js.map} +1 -1
  163. package/dist/{chunk-XJJMGLHB.js → chunk-YRWGUJOR.js} +12 -8
  164. package/dist/chunk-YRWGUJOR.js.map +1 -0
  165. package/dist/{chunk-ZTY326ZR.js → chunk-YYNH6PGX.js} +3 -3
  166. package/dist/{chunk-ZTY326ZR.js.map → chunk-YYNH6PGX.js.map} +1 -1
  167. package/dist/{chunk-PGXS2QT5.js → chunk-Z4BZW7QS.js} +3 -3
  168. package/dist/{chunk-PGXS2QT5.js.map → chunk-Z4BZW7QS.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-LQ6CAJYV.js → document-class-RKLOCL35.js} +11 -11
  189. package/dist/{document-class-LQ6CAJYV.js.map → document-class-RKLOCL35.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-YFRK3OW2.js → navigation-DIKCKMZ6.js} +5 -5
  197. package/dist/{navigation-YFRK3OW2.js.map → navigation-DIKCKMZ6.js.map} +1 -1
  198. package/dist/{navigation-MEQGPFG2.js → navigation-HHJTUPN2.js} +4 -4
  199. package/dist/{navigation-MEQGPFG2.js.map → navigation-HHJTUPN2.js.map} +1 -1
  200. package/dist/{not-found-62H3K6KC.js → not-found-4KKQNZCQ.js} +16 -16
  201. package/dist/{not-found-62H3K6KC.js.map → not-found-4KKQNZCQ.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-HQYYF3ZS.js → store-lookup-EN7CBHC3.js} +8 -8
  219. package/dist/{store-lookup-HQYYF3ZS.js.map → store-lookup-EN7CBHC3.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 +11 -6
  224. package/tailwind/components/live-editor/LiveEditorBar.tsx.txt +64 -9
  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-6SKEWJOX.js.map +0 -1
  228. package/dist/chunk-74PTCV46.js.map +0 -1
  229. package/dist/chunk-DEY7MDGF.js +0 -14
  230. package/dist/chunk-PDIFXQDX.js.map +0 -1
  231. package/dist/chunk-XJJMGLHB.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-Y2OC2467.js';
2
- import { requestHostOf } from './chunk-3L4TXBS7.js';
3
- export { TanStackLinkAdapter, TanStackNavigationProvider, createTanStackLinkAdapter } from './chunk-3L4TXBS7.js';
1
+ export { tanstackDocumentAdapter as attrs, createRouter, createTwilightRootRoute, useTanstackDocumentAttrs as useAttrs } from './chunk-QFEJAVGD.js';
2
+ import { requestHostOf } from './chunk-2YCR7EEN.js';
3
+ export { TanStackLinkAdapter, TanStackNavigationProvider, createTanStackLinkAdapter } from './chunk-2YCR7EEN.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-3BCIBYJC.js';
8
- import { readNativeAppHeaders } from './chunk-4KRALUHW.js';
9
- import './chunk-ESZBWB7X.js';
10
- import './chunk-SLXTJLF2.js';
7
+ import './chunk-N5PGVFSO.js';
8
+ import { readNativeAppHeaders } from './chunk-IVW2XKC3.js';
9
+ import './chunk-JWBHXCLD.js';
10
+ import './chunk-B3KRYCIN.js';
11
11
  import { rootHead, maintenanceEvidence, MAINTENANCE_EVIDENCE_HEADER, maintenanceEvidenceHeader } from './chunk-KQEBYXYB.js';
12
12
  import './chunk-JYV25VNZ.js';
13
- import './chunk-PGXS2QT5.js';
13
+ import './chunk-Z4BZW7QS.js';
14
14
  import './chunk-5FJFN3IW.js';
15
15
  import './chunk-GD26FI4J.js';
16
16
  import './chunk-OM2GRPDH.js';
17
- import './chunk-UBNOFV7C.js';
17
+ import './chunk-HEGWAECO.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-4JNLIYCE.js';
22
- import './chunk-IMCS5L4A.js';
21
+ import './chunk-XDZHVKBK.js';
22
+ import './chunk-UZXRQZV2.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-3BCIBYJC.js';
3
+ export { dispatchRouteChanged } from '../chunk-N5PGVFSO.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-IMCS5L4A.js';
11
+ import '../chunk-UZXRQZV2.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.123",
3
+ "version": "1.0.125",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -205,6 +205,16 @@ 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: its row id, the dev editor's
210
+ * layout id (less the remount suffix), or the uuid of one not saved yet.
211
+ */
212
+ function liveBlockIdOf(data: HomeComponentData): string | undefined {
213
+ const devId = typeof data.__dev_id === 'string' ? data.__dev_id.replace(/~\d+$/, '') : undefined;
214
+ const id = data.id ?? devId ?? data.key;
215
+ return typeof id === 'number' || (typeof id === 'string' && id) ? String(id) : undefined;
216
+ }
217
+
208
218
  /**
209
219
  * Memoized component renderer with scroll-based lazy loading
210
220
  * Components only mount when scrolled into view - preventing API calls until needed
@@ -253,12 +263,7 @@ export const HomeComponentRenderer = memo(function HomeComponentRenderer({
253
263
  ? config.id(data)
254
264
  : config.id
255
265
  : 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;
266
+ const liveBlockId = liveBlockIdOf(data);
262
267
  const height = config?.height || '400px';
263
268
  // A theme's config replaces the engine's, but keeps its mount timing unless the theme sets one.
264
269
  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"
@@ -331,13 +357,34 @@ function Toolbar({ target, labels, busy, barRef, onAct }: ToolbarProps) {
331
357
  );
332
358
  }
333
359
 
360
+ /**
361
+ * The block an action names. A uuid is shared by every copy of one component,
362
+ * so it comes with which copy, in page order; a row id is unique on its own.
363
+ */
364
+ function blockRefOf(el: HTMLElement, id: string): { id: string; copy?: number } {
365
+ if (/^\d+$/.test(id)) return { id };
366
+ const copies = Array.from(document.querySelectorAll(`[${BLOCK_ATTRIBUTE}]`)).filter(
367
+ (block) => block.getAttribute(BLOCK_ATTRIBUTE) === id
368
+ );
369
+ return { id, copy: copies.indexOf(el) };
370
+ }
371
+
334
372
  /**
335
373
  * The live editor's action bar: hovering a home block outlines it and shows
336
374
  * edit, hide, move, duplicate, add and delete; hovering the header or footer
337
375
  * shows an edit button. Double-clicking either edits it. Each action is a
338
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.
339
380
  */
340
- 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 {
341
388
  const [hovered, setHovered] = useState<HTMLElement | null>(null);
342
389
  const [editing, setEditing] = useState<HTMLElement | null>(null);
343
390
  const [busy, setBusy] = useState<BlockAction | null>(null);
@@ -357,11 +404,12 @@ export function LiveEditorBar({ origin }: { origin: string }): ReactElement | nu
357
404
  }
358
405
  const id = el.getAttribute(BLOCK_ATTRIBUTE);
359
406
  if (!id) return;
360
- postToEditor(origin, `preview::${action}.component`, { id });
407
+ postToEditor(origin, `preview::${action}.component`, blockRefOf(el, id));
361
408
  if (action === 'edit') setEditing(el);
362
- else if (action === 'hide' || action === 'duplicate' || action === 'delete') setBusy(action);
409
+ else if (!dev && (action === 'hide' || action === 'duplicate' || action === 'delete'))
410
+ setBusy(action);
363
411
  },
364
- [origin]
412
+ [origin, dev]
365
413
  );
366
414
 
367
415
  useEffect(() => {
@@ -409,7 +457,14 @@ export function LiveEditorBar({ origin }: { origin: string }): ReactElement | nu
409
457
  {selected && selected !== target && <Outline el={selected} />}
410
458
  {target && <Outline el={target} label={targetArea ? labels[targetArea] : undefined} />}
411
459
  {target && (
412
- <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
+ />
413
468
  )}
414
469
  </>,
415
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-6SKEWJOX.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 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`, { 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"]}