@sonordev/site-kit 6.0.1 → 6.1.0

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/README.md +11 -0
  2. package/agent-manifest.json +8 -1
  3. package/dist/{AnalyticsProvider-YU6QANW5.mjs → AnalyticsProvider-PO6SSJG2.mjs} +6 -6
  4. package/dist/{AnalyticsProvider-YU6QANW5.mjs.map → AnalyticsProvider-PO6SSJG2.mjs.map} +1 -1
  5. package/dist/{AnalyticsProvider-4RLYTVWO.js → AnalyticsProvider-V6G45QG7.js} +10 -10
  6. package/dist/{AnalyticsProvider-4RLYTVWO.js.map → AnalyticsProvider-V6G45QG7.js.map} +1 -1
  7. package/dist/{ArticleViewTracker-LI44SA4T.mjs → ArticleViewTracker-CML36HJZ.mjs} +5 -5
  8. package/dist/{ArticleViewTracker-LI44SA4T.mjs.map → ArticleViewTracker-CML36HJZ.mjs.map} +1 -1
  9. package/dist/{ArticleViewTracker-OQB2VT73.js → ArticleViewTracker-FE2YWHX4.js} +6 -6
  10. package/dist/{ArticleViewTracker-OQB2VT73.js.map → ArticleViewTracker-FE2YWHX4.js.map} +1 -1
  11. package/dist/ChatWidget-GVNDKGUU.mjs +16 -0
  12. package/dist/{ChatWidget-56SW5ITH.mjs.map → ChatWidget-GVNDKGUU.mjs.map} +1 -1
  13. package/dist/ChatWidget-YL34KOZM.js +25 -0
  14. package/dist/{ChatWidget-HR3OC3WA.js.map → ChatWidget-YL34KOZM.js.map} +1 -1
  15. package/dist/EngageWidget-465O5XUV.mjs +12 -0
  16. package/dist/{EngageWidget-L4X35N2B.mjs.map → EngageWidget-465O5XUV.mjs.map} +1 -1
  17. package/dist/EngageWidget-IQRKRYPG.js +21 -0
  18. package/dist/{EngageWidget-HXGI3ZKG.js.map → EngageWidget-IQRKRYPG.js.map} +1 -1
  19. package/dist/{FileField-N2P6E2PN.js → FileField-F3PFKMIF.js} +6 -6
  20. package/dist/{FileField-N2P6E2PN.js.map → FileField-F3PFKMIF.js.map} +1 -1
  21. package/dist/{FileField-GSTLU42O.mjs → FileField-FWKSBFRX.mjs} +5 -5
  22. package/dist/{FileField-GSTLU42O.mjs.map → FileField-FWKSBFRX.mjs.map} +1 -1
  23. package/dist/{FormSpotlight-QLXOS5LN.js → FormSpotlight-DJQ3P3EX.js} +9 -9
  24. package/dist/{FormSpotlight-QLXOS5LN.js.map → FormSpotlight-DJQ3P3EX.js.map} +1 -1
  25. package/dist/{FormSpotlight-UFATQTN3.mjs → FormSpotlight-K4LP2DMP.mjs} +3 -3
  26. package/dist/{FormSpotlight-UFATQTN3.mjs.map → FormSpotlight-K4LP2DMP.mjs.map} +1 -1
  27. package/dist/{FormStage-4HYDMCYU.js → FormStage-QMGZ3MOT.js} +9 -9
  28. package/dist/{FormStage-4HYDMCYU.js.map → FormStage-QMGZ3MOT.js.map} +1 -1
  29. package/dist/{FormStage-SYOKD5TJ.mjs → FormStage-YKVTHPKV.mjs} +3 -3
  30. package/dist/{FormStage-SYOKD5TJ.mjs.map → FormStage-YKVTHPKV.mjs.map} +1 -1
  31. package/dist/ManagedForm-MLPETHSQ.js +25 -0
  32. package/dist/{ManagedForm-TUSGW5EH.js.map → ManagedForm-MLPETHSQ.js.map} +1 -1
  33. package/dist/ManagedForm-P3L34ALG.mjs +16 -0
  34. package/dist/{ManagedForm-3TDSIRCM.mjs.map → ManagedForm-P3L34ALG.mjs.map} +1 -1
  35. package/dist/{ManagedNewsletterForm-CNGCBA2N.mjs → ManagedNewsletterForm-C5YQH7MP.mjs} +7 -7
  36. package/dist/{ManagedNewsletterForm-CNGCBA2N.mjs.map → ManagedNewsletterForm-C5YQH7MP.mjs.map} +1 -1
  37. package/dist/{ManagedNewsletterForm-XJEPVJRM.js → ManagedNewsletterForm-R4LLZBRQ.js} +8 -8
  38. package/dist/{ManagedNewsletterForm-XJEPVJRM.js.map → ManagedNewsletterForm-R4LLZBRQ.js.map} +1 -1
  39. package/dist/{SignalCore-ARRVP6F7.mjs → SignalCore-BNYNS6P7.mjs} +5 -5
  40. package/dist/{SignalCore-ARRVP6F7.mjs.map → SignalCore-BNYNS6P7.mjs.map} +1 -1
  41. package/dist/{SignalCore-AXK6RCR4.js → SignalCore-CLELIKFI.js} +11 -11
  42. package/dist/{SignalCore-AXK6RCR4.js.map → SignalCore-CLELIKFI.js.map} +1 -1
  43. package/dist/SitemapSync-P7XWL6VV.js +18 -0
  44. package/dist/{SitemapSync-GAGREAX6.js.map → SitemapSync-P7XWL6VV.js.map} +1 -1
  45. package/dist/SitemapSync-SIKJCK7N.mjs +9 -0
  46. package/dist/{SitemapSync-42VKJUOU.mjs.map → SitemapSync-SIKJCK7N.mjs.map} +1 -1
  47. package/dist/analytics/index.js +9 -9
  48. package/dist/analytics/index.mjs +4 -4
  49. package/dist/articles/index.js +2 -2
  50. package/dist/articles/index.mjs +2 -2
  51. package/dist/articles/server-ui.js +1 -1
  52. package/dist/articles/server-ui.mjs +1 -1
  53. package/dist/{chunk-UILVPI2R.js → chunk-36NE4PSS.js} +3 -3
  54. package/dist/{chunk-UILVPI2R.js.map → chunk-36NE4PSS.js.map} +1 -1
  55. package/dist/{chunk-L63DFM4R.mjs → chunk-3DXJHMQM.mjs} +3 -3
  56. package/dist/{chunk-L63DFM4R.mjs.map → chunk-3DXJHMQM.mjs.map} +1 -1
  57. package/dist/{chunk-NYNL6UQV.mjs → chunk-4EJI4IOX.mjs} +63 -50
  58. package/dist/chunk-4EJI4IOX.mjs.map +1 -0
  59. package/dist/{chunk-6URNNS3V.mjs → chunk-6OSHL7H5.mjs} +4 -4
  60. package/dist/{chunk-6URNNS3V.mjs.map → chunk-6OSHL7H5.mjs.map} +1 -1
  61. package/dist/chunk-ACKFL6WA.mjs +6 -0
  62. package/dist/{chunk-7MAHP735.mjs.map → chunk-ACKFL6WA.mjs.map} +1 -1
  63. package/dist/{chunk-4NBJDP6Y.js → chunk-AQUWWATH.js} +4 -4
  64. package/dist/{chunk-4NBJDP6Y.js.map → chunk-AQUWWATH.js.map} +1 -1
  65. package/dist/{chunk-QXPGIOU7.mjs → chunk-BDSNEB3O.mjs} +3 -3
  66. package/dist/{chunk-QXPGIOU7.mjs.map → chunk-BDSNEB3O.mjs.map} +1 -1
  67. package/dist/{chunk-ZRWIMNQF.js → chunk-BGOPF6J2.js} +3 -3
  68. package/dist/{chunk-ZRWIMNQF.js.map → chunk-BGOPF6J2.js.map} +1 -1
  69. package/dist/chunk-BZR6RUIN.mjs +128 -0
  70. package/dist/chunk-BZR6RUIN.mjs.map +1 -0
  71. package/dist/{chunk-XLFTZV34.mjs → chunk-CJX2MC62.mjs} +3 -3
  72. package/dist/{chunk-XLFTZV34.mjs.map → chunk-CJX2MC62.mjs.map} +1 -1
  73. package/dist/{chunk-AQMQOH7V.js → chunk-DQXEYUTD.js} +3 -3
  74. package/dist/{chunk-AQMQOH7V.js.map → chunk-DQXEYUTD.js.map} +1 -1
  75. package/dist/{chunk-PKZMNDZJ.js → chunk-DXBSBWSS.js} +5 -5
  76. package/dist/{chunk-PKZMNDZJ.js.map → chunk-DXBSBWSS.js.map} +1 -1
  77. package/dist/{chunk-NVWUXCUQ.mjs → chunk-ESAPT5JT.mjs} +3 -3
  78. package/dist/{chunk-NVWUXCUQ.mjs.map → chunk-ESAPT5JT.mjs.map} +1 -1
  79. package/dist/{chunk-QP56JXMH.mjs → chunk-F6BYWNHU.mjs} +3 -3
  80. package/dist/{chunk-QP56JXMH.mjs.map → chunk-F6BYWNHU.mjs.map} +1 -1
  81. package/dist/{chunk-NNQG25M2.mjs → chunk-FQAZBJJO.mjs} +3 -3
  82. package/dist/{chunk-NNQG25M2.mjs.map → chunk-FQAZBJJO.mjs.map} +1 -1
  83. package/dist/{chunk-RX4TVHSN.mjs → chunk-H5FCJBAN.mjs} +3 -3
  84. package/dist/{chunk-RX4TVHSN.mjs.map → chunk-H5FCJBAN.mjs.map} +1 -1
  85. package/dist/{chunk-6ZOMIPU5.js → chunk-I4RU3RCS.js} +8 -8
  86. package/dist/{chunk-6ZOMIPU5.js.map → chunk-I4RU3RCS.js.map} +1 -1
  87. package/dist/{chunk-GCZHKHEH.js → chunk-IDJY2JWU.js} +5 -5
  88. package/dist/{chunk-GCZHKHEH.js.map → chunk-IDJY2JWU.js.map} +1 -1
  89. package/dist/{chunk-6YEPFH3Q.js → chunk-ITVQV7DK.js} +10 -10
  90. package/dist/{chunk-6YEPFH3Q.js.map → chunk-ITVQV7DK.js.map} +1 -1
  91. package/dist/chunk-IWSN4DGG.js +132 -0
  92. package/dist/chunk-IWSN4DGG.js.map +1 -0
  93. package/dist/{chunk-P2HOVWJW.js → chunk-K4LLYQFZ.js} +14 -14
  94. package/dist/{chunk-P2HOVWJW.js.map → chunk-K4LLYQFZ.js.map} +1 -1
  95. package/dist/{chunk-XWKY46NU.js → chunk-KDNB6BYH.js} +11 -11
  96. package/dist/{chunk-XWKY46NU.js.map → chunk-KDNB6BYH.js.map} +1 -1
  97. package/dist/{chunk-U6F6AN23.mjs → chunk-KKTSAT7I.mjs} +4 -4
  98. package/dist/{chunk-U6F6AN23.mjs.map → chunk-KKTSAT7I.mjs.map} +1 -1
  99. package/dist/{chunk-BMH2D2EP.mjs → chunk-L3GAEBJU.mjs} +3 -3
  100. package/dist/{chunk-BMH2D2EP.mjs.map → chunk-L3GAEBJU.mjs.map} +1 -1
  101. package/dist/{chunk-TC3JSQUS.js → chunk-LF44SJPP.js} +4 -4
  102. package/dist/{chunk-TC3JSQUS.js.map → chunk-LF44SJPP.js.map} +1 -1
  103. package/dist/{chunk-QD4BNYMM.js → chunk-MZK5APTV.js} +18 -18
  104. package/dist/{chunk-QD4BNYMM.js.map → chunk-MZK5APTV.js.map} +1 -1
  105. package/dist/chunk-NBOBOYMT.js +52 -0
  106. package/dist/chunk-NBOBOYMT.js.map +1 -0
  107. package/dist/{chunk-ZYJYBE5G.js → chunk-NDVKIDMN.js} +4 -4
  108. package/dist/{chunk-ZYJYBE5G.js.map → chunk-NDVKIDMN.js.map} +1 -1
  109. package/dist/{chunk-IDOHDP7D.mjs → chunk-NMKUYGKK.mjs} +4 -4
  110. package/dist/{chunk-IDOHDP7D.mjs.map → chunk-NMKUYGKK.mjs.map} +1 -1
  111. package/dist/chunk-OBKS3CPP.mjs +50 -0
  112. package/dist/chunk-OBKS3CPP.mjs.map +1 -0
  113. package/dist/{chunk-WLCPBZOS.mjs → chunk-OF4V7CSP.mjs} +3 -3
  114. package/dist/{chunk-WLCPBZOS.mjs.map → chunk-OF4V7CSP.mjs.map} +1 -1
  115. package/dist/{chunk-LCYODFNY.mjs → chunk-OQWAJBYA.mjs} +3 -3
  116. package/dist/{chunk-LCYODFNY.mjs.map → chunk-OQWAJBYA.mjs.map} +1 -1
  117. package/dist/chunk-OXA4ZSX2.js +8 -0
  118. package/dist/{chunk-F3EAAAZ3.js.map → chunk-OXA4ZSX2.js.map} +1 -1
  119. package/dist/{chunk-QJ3SMLND.mjs → chunk-QC6VQ2IW.mjs} +3 -3
  120. package/dist/{chunk-QJ3SMLND.mjs.map → chunk-QC6VQ2IW.mjs.map} +1 -1
  121. package/dist/{chunk-UCBBZXJI.js → chunk-QF3XTEXV.js} +4 -4
  122. package/dist/{chunk-UCBBZXJI.js.map → chunk-QF3XTEXV.js.map} +1 -1
  123. package/dist/{chunk-SU42KCQD.mjs → chunk-RGCUND5Q.mjs} +4 -4
  124. package/dist/{chunk-SU42KCQD.mjs.map → chunk-RGCUND5Q.mjs.map} +1 -1
  125. package/dist/{chunk-BWSYPQ5E.js → chunk-RVFOUPK4.js} +6 -6
  126. package/dist/{chunk-BWSYPQ5E.js.map → chunk-RVFOUPK4.js.map} +1 -1
  127. package/dist/{chunk-ULSFOC3J.js → chunk-SDGS2R5O.js} +12 -12
  128. package/dist/{chunk-ULSFOC3J.js.map → chunk-SDGS2R5O.js.map} +1 -1
  129. package/dist/{chunk-BIU234TJ.mjs → chunk-TA3TQTDA.mjs} +8 -8
  130. package/dist/{chunk-BIU234TJ.mjs.map → chunk-TA3TQTDA.mjs.map} +1 -1
  131. package/dist/{chunk-R6UE6MC5.js → chunk-TMNFPVK5.js} +7 -7
  132. package/dist/{chunk-R6UE6MC5.js.map → chunk-TMNFPVK5.js.map} +1 -1
  133. package/dist/{chunk-4VCR6TXK.js → chunk-VK74LC4H.js} +5 -5
  134. package/dist/{chunk-4VCR6TXK.js.map → chunk-VK74LC4H.js.map} +1 -1
  135. package/dist/{chunk-GJCQVVO3.mjs → chunk-XGELGOF6.mjs} +7 -7
  136. package/dist/{chunk-GJCQVVO3.mjs.map → chunk-XGELGOF6.mjs.map} +1 -1
  137. package/dist/{chunk-LZPICBND.js → chunk-XKJLJLQ4.js} +74 -61
  138. package/dist/chunk-XKJLJLQ4.js.map +1 -0
  139. package/dist/{chunk-YTJKVWHY.mjs → chunk-XOYQKFKI.mjs} +4 -4
  140. package/dist/{chunk-YTJKVWHY.mjs.map → chunk-XOYQKFKI.mjs.map} +1 -1
  141. package/dist/{chunk-NVOYCK2S.js → chunk-Y53BIB7A.js} +14 -14
  142. package/dist/{chunk-NVOYCK2S.js.map → chunk-Y53BIB7A.js.map} +1 -1
  143. package/dist/{chunk-GKPVGVHY.mjs → chunk-YKNXBWPZ.mjs} +3 -3
  144. package/dist/{chunk-GKPVGVHY.mjs.map → chunk-YKNXBWPZ.mjs.map} +1 -1
  145. package/dist/{chunk-KUHMHKG5.mjs → chunk-YP5WAFTI.mjs} +4 -4
  146. package/dist/{chunk-KUHMHKG5.mjs.map → chunk-YP5WAFTI.mjs.map} +1 -1
  147. package/dist/{chunk-FKFVN3GT.js → chunk-YS7Y2PQG.js} +8 -8
  148. package/dist/{chunk-FKFVN3GT.js.map → chunk-YS7Y2PQG.js.map} +1 -1
  149. package/dist/{chunk-5ABTBPPK.js → chunk-ZQJGCQHC.js} +15 -15
  150. package/dist/{chunk-5ABTBPPK.js.map → chunk-ZQJGCQHC.js.map} +1 -1
  151. package/dist/{chunk-LFASEDBO.mjs → chunk-ZQSJK65K.mjs} +3 -3
  152. package/dist/{chunk-LFASEDBO.mjs.map → chunk-ZQSJK65K.mjs.map} +1 -1
  153. package/dist/cli/index.js +3 -3
  154. package/dist/cli/index.mjs +1 -1
  155. package/dist/client/index.js +4 -4
  156. package/dist/client/index.mjs +3 -3
  157. package/dist/commerce/index.js +57 -57
  158. package/dist/commerce/index.mjs +5 -5
  159. package/dist/cta-bar/CtaBar.d.ts +71 -0
  160. package/dist/cta-bar/CtaBar.d.ts.map +1 -0
  161. package/dist/cta-bar/CtaBarBehavior.d.ts +46 -0
  162. package/dist/cta-bar/CtaBarBehavior.d.ts.map +1 -0
  163. package/dist/cta-bar/client.d.ts +46 -0
  164. package/dist/cta-bar/client.d.ts.map +1 -0
  165. package/dist/cta-bar/client.js +23 -0
  166. package/dist/cta-bar/client.js.map +1 -0
  167. package/dist/cta-bar/client.mjs +6 -0
  168. package/dist/cta-bar/client.mjs.map +1 -0
  169. package/dist/cta-bar/index.d.ts +12 -0
  170. package/dist/cta-bar/index.d.ts.map +1 -0
  171. package/dist/cta-bar/index.js +230 -0
  172. package/dist/cta-bar/index.js.map +1 -0
  173. package/dist/cta-bar/index.mjs +224 -0
  174. package/dist/cta-bar/index.mjs.map +1 -0
  175. package/dist/cta-bar/styles.d.ts +46 -0
  176. package/dist/cta-bar/styles.d.ts.map +1 -0
  177. package/dist/engage/ChatWidget.d.ts.map +1 -1
  178. package/dist/engage/EchoUiActions.d.ts.map +1 -1
  179. package/dist/engage/index.js +14 -13
  180. package/dist/engage/index.js.map +1 -1
  181. package/dist/engage/index.mjs +8 -7
  182. package/dist/engage/index.mjs.map +1 -1
  183. package/dist/fleet/index.js +8 -8
  184. package/dist/fleet/index.mjs +4 -4
  185. package/dist/forms/index.js +22 -22
  186. package/dist/forms/index.mjs +9 -9
  187. package/dist/forms/server.js +10 -10
  188. package/dist/forms/server.mjs +8 -8
  189. package/dist/forms/static.js +7 -7
  190. package/dist/forms/static.mjs +6 -6
  191. package/dist/images/index.js +11 -11
  192. package/dist/images/index.mjs +4 -4
  193. package/dist/index.js +63 -63
  194. package/dist/index.mjs +9 -9
  195. package/dist/layout/client.js +8 -8
  196. package/dist/layout/client.mjs +7 -7
  197. package/dist/layout/index.js +10 -10
  198. package/dist/layout/index.mjs +8 -8
  199. package/dist/maps/index.js +5 -5
  200. package/dist/maps/index.mjs +3 -3
  201. package/dist/search/index.js +4 -4
  202. package/dist/search/index.mjs +3 -3
  203. package/dist/seo/client.js +5 -5
  204. package/dist/seo/client.mjs +4 -4
  205. package/dist/seo/index.js +5 -5
  206. package/dist/seo/index.mjs +4 -4
  207. package/dist/server/index.js +3 -3
  208. package/dist/server/index.mjs +2 -2
  209. package/dist/shared/build-entries.d.ts +4 -1
  210. package/dist/shared/build-entries.d.ts.map +1 -1
  211. package/dist/shared/glass.d.ts +51 -0
  212. package/dist/shared/glass.d.ts.map +1 -0
  213. package/dist/shared/version.d.ts +1 -1
  214. package/dist/signal/index.js +11 -11
  215. package/dist/signal/index.mjs +2 -2
  216. package/dist/sync/index.js +17 -17
  217. package/dist/sync/index.mjs +4 -4
  218. package/package.json +6 -1
  219. package/skills/site-kit/SKILL.md +6 -0
  220. package/dist/ChatWidget-56SW5ITH.mjs +0 -15
  221. package/dist/ChatWidget-HR3OC3WA.js +0 -24
  222. package/dist/EngageWidget-HXGI3ZKG.js +0 -21
  223. package/dist/EngageWidget-L4X35N2B.mjs +0 -12
  224. package/dist/ManagedForm-3TDSIRCM.mjs +0 -16
  225. package/dist/ManagedForm-TUSGW5EH.js +0 -25
  226. package/dist/SitemapSync-42VKJUOU.mjs +0 -9
  227. package/dist/SitemapSync-GAGREAX6.js +0 -18
  228. package/dist/chunk-7MAHP735.mjs +0 -6
  229. package/dist/chunk-F3EAAAZ3.js +0 -8
  230. package/dist/chunk-LZPICBND.js.map +0 -1
  231. package/dist/chunk-NYNL6UQV.mjs.map +0 -1
package/README.md CHANGED
@@ -142,6 +142,14 @@ Three tiers as three subpaths — a site only installs and ships what it imports
142
142
  | **Motion / GSAP** | `@sonordev/site-kit/motion/gsap` | Lazy-loaded GSAP timelines (`useGsap`) — optional peer `gsap` | [README](src/motion/README.md) |
143
143
  | **Motion / three** | `@sonordev/site-kit/motion/three` | WebGL stages on the engine (`useThreeStage`) — optional peer `three` | [README](src/motion/README.md) |
144
144
 
145
+ ### Liquid Glass chrome
146
+
147
+ | Module | Import | Purpose | Docs |
148
+ |--------|--------|---------|------|
149
+ | **CTA bar** | `@sonordev/site-kit/cta-bar` | `<CtaBar>` + `<CtaBarAction>`: the floating glass mobile CTA bar. Hides over its form and while typing, compacts on scroll, lifts the Echo launcher, emits `cta_click`. Server component. | [README](src/cta-bar/README.md) |
150
+
151
+ Echo's launcher and chat window use the same glass recipe; see [Engage](src/engage/README.md#liquid-glass-610).
152
+
145
153
  ---
146
154
 
147
155
  ## How It Works
@@ -229,6 +237,9 @@ import { Reveal, Parallax, ScrollScene, registerScene } from '@sonordev/site-kit
229
237
  import { useGsap } from '@sonordev/site-kit/motion/gsap'
230
238
  import { useThreeStage, canRunWebGL } from '@sonordev/site-kit/motion/three'
231
239
 
240
+ // Liquid Glass mobile CTA bar (server component; pass Next Link via `as`)
241
+ import { CtaBar, CtaBarAction } from '@sonordev/site-kit/cta-bar'
242
+
232
243
  // Styles (optional)
233
244
  import '@sonordev/site-kit/brand.css'
234
245
  import '@sonordev/site-kit/forms/styles.css'
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "package": "@sonordev/site-kit",
4
- "version": "6.0.1",
4
+ "version": "6.1.0",
5
5
  "description": "Machine-readable manifest for coding agents integrating a site with Sonor. See AGENTS.md (shipped) and docs/SITE-KIT-AGENT-NATIVE.md.",
6
6
  "env": {
7
7
  "required": [
@@ -416,6 +416,13 @@
416
416
  "purpose": "Tier 2: three.js stages on the scroll engine (useThreeStage, canRunWebGL, loadTexture). Optional peer `three` — flagship builds only (~180KB gz).",
417
417
  "caution": "Mount the scene as a dynamic(ssr:false) CHILDLESS sibling of the server-rendered stage, never wrapping it. Copy lives in the DOM, never in the canvas. canRunWebGL() false = keep the server-rendered still."
418
418
  },
419
+ {
420
+ "name": "cta-bar",
421
+ "import": "@sonordev/site-kit/cta-bar",
422
+ "side": "server",
423
+ "purpose": "Liquid Glass mobile CTA bar: <CtaBar> + <CtaBarAction>. Server component with a childless behaviour island; hides over targets and while typing, compacts on scroll, lifts the Echo launcher, emits cta_click.",
424
+ "caution": "Render once at the layout root, outside <main> (it is an <aside> landmark) and never inside a backdrop-filtered header. Pass Next Link via as={Link}. Replaces every hand-rolled sticky mobile CTA bar; delete the site copy and its body padding when adopting."
425
+ },
419
426
  {
420
427
  "name": "client",
421
428
  "import": "@sonordev/site-kit/client",
@@ -1,12 +1,12 @@
1
- export { AnalyticsProvider, useAnalytics, useAnalyticsOptional, useTrackEvent } from './chunk-QXPGIOU7.mjs';
1
+ export { AnalyticsProvider, useAnalytics, useAnalyticsOptional, useTrackEvent } from './chunk-BDSNEB3O.mjs';
2
2
  import './chunk-SHLWA6JY.mjs';
3
3
  import './chunk-ZZQAGCSI.mjs';
4
4
  import './chunk-5SANRSX3.mjs';
5
5
  import './chunk-6EDVCUAG.mjs';
6
6
  import './chunk-A6UYV475.mjs';
7
- import './chunk-SU42KCQD.mjs';
8
- import './chunk-L63DFM4R.mjs';
9
- import './chunk-7MAHP735.mjs';
7
+ import './chunk-RGCUND5Q.mjs';
8
+ import './chunk-3DXJHMQM.mjs';
9
+ import './chunk-ACKFL6WA.mjs';
10
10
  import './chunk-BEXTDHO4.mjs';
11
- //# sourceMappingURL=AnalyticsProvider-YU6QANW5.mjs.map
12
- //# sourceMappingURL=AnalyticsProvider-YU6QANW5.mjs.map
11
+ //# sourceMappingURL=AnalyticsProvider-PO6SSJG2.mjs.map
12
+ //# sourceMappingURL=AnalyticsProvider-PO6SSJG2.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"AnalyticsProvider-YU6QANW5.mjs"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"AnalyticsProvider-PO6SSJG2.mjs"}
@@ -1,33 +1,33 @@
1
1
  'use strict';
2
2
 
3
- var chunkPKZMNDZJ_js = require('./chunk-PKZMNDZJ.js');
3
+ var chunkDXBSBWSS_js = require('./chunk-DXBSBWSS.js');
4
4
  require('./chunk-3DWIYCQX.js');
5
5
  require('./chunk-RC4WWQXR.js');
6
6
  require('./chunk-P4Y75QSU.js');
7
7
  require('./chunk-VJMPIRSD.js');
8
8
  require('./chunk-5RNAUYWE.js');
9
- require('./chunk-5ABTBPPK.js');
10
- require('./chunk-TC3JSQUS.js');
11
- require('./chunk-F3EAAAZ3.js');
9
+ require('./chunk-ZQJGCQHC.js');
10
+ require('./chunk-LF44SJPP.js');
11
+ require('./chunk-OXA4ZSX2.js');
12
12
  require('./chunk-SNAQBZPT.js');
13
13
 
14
14
 
15
15
 
16
16
  Object.defineProperty(exports, "AnalyticsProvider", {
17
17
  enumerable: true,
18
- get: function () { return chunkPKZMNDZJ_js.AnalyticsProvider; }
18
+ get: function () { return chunkDXBSBWSS_js.AnalyticsProvider; }
19
19
  });
20
20
  Object.defineProperty(exports, "useAnalytics", {
21
21
  enumerable: true,
22
- get: function () { return chunkPKZMNDZJ_js.useAnalytics; }
22
+ get: function () { return chunkDXBSBWSS_js.useAnalytics; }
23
23
  });
24
24
  Object.defineProperty(exports, "useAnalyticsOptional", {
25
25
  enumerable: true,
26
- get: function () { return chunkPKZMNDZJ_js.useAnalyticsOptional; }
26
+ get: function () { return chunkDXBSBWSS_js.useAnalyticsOptional; }
27
27
  });
28
28
  Object.defineProperty(exports, "useTrackEvent", {
29
29
  enumerable: true,
30
- get: function () { return chunkPKZMNDZJ_js.useTrackEvent; }
30
+ get: function () { return chunkDXBSBWSS_js.useTrackEvent; }
31
31
  });
32
- //# sourceMappingURL=AnalyticsProvider-4RLYTVWO.js.map
33
- //# sourceMappingURL=AnalyticsProvider-4RLYTVWO.js.map
32
+ //# sourceMappingURL=AnalyticsProvider-V6G45QG7.js.map
33
+ //# sourceMappingURL=AnalyticsProvider-V6G45QG7.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"AnalyticsProvider-4RLYTVWO.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"AnalyticsProvider-V6G45QG7.js"}
@@ -3,9 +3,9 @@ import { useDeferredActivation } from './chunk-ZVBLXXNJ.mjs';
3
3
  import { withSiteBody } from './chunk-AS2HUIDW.mjs';
4
4
  import './chunk-KZVSB5WE.mjs';
5
5
  import './chunk-ZN4HBK5C.mjs';
6
- import { sonorFetch } from './chunk-SU42KCQD.mjs';
7
- import './chunk-L63DFM4R.mjs';
8
- import './chunk-7MAHP735.mjs';
6
+ import { sonorFetch } from './chunk-RGCUND5Q.mjs';
7
+ import './chunk-3DXJHMQM.mjs';
8
+ import './chunk-ACKFL6WA.mjs';
9
9
  import './chunk-BEXTDHO4.mjs';
10
10
  import { useEffect } from 'react';
11
11
 
@@ -47,5 +47,5 @@ function ArticleViewTracker({ slug, site }) {
47
47
  }
48
48
 
49
49
  export { ArticleViewTracker, trackArticleView };
50
- //# sourceMappingURL=ArticleViewTracker-LI44SA4T.mjs.map
51
- //# sourceMappingURL=ArticleViewTracker-LI44SA4T.mjs.map
50
+ //# sourceMappingURL=ArticleViewTracker-CML36HJZ.mjs.map
51
+ //# sourceMappingURL=ArticleViewTracker-CML36HJZ.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/articles/ArticleViewTracker.tsx"],"names":[],"mappings":";;;;;;;;;;AAoBA,IAAM,kBAAA,GAAqB,2BAAA;AAgBpB,SAAS,gBAAA,CAAiB,MAAc,IAAA,EAAqB;AAClE,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,MAAA,KAAW,WAAA,EAAa;AAI5C,EAAA,MAAM,CAAA,GAAI,MAAA;AAKV,EAAA,MAAM,UAAU,CAAA,CAAE,oBAAA;AAClB,EAAA,IAAI,CAAC,OAAA,IAAW,CAAC,CAAA,CAAE,oBAAA,EAAsB;AAKzC,EAAA,MAAM,GAAA,GAAM,CAAA,EAAG,kBAAkB,CAAA,EAAG,IAAI,CAAA,CAAA;AACxC,EAAA,IAAI;AACF,IAAA,IAAI,cAAA,CAAe,OAAA,CAAQ,GAAG,CAAA,EAAG;AAAA,EACnC,CAAA,CAAA,MAAQ;AAAA,EAER;AAIA,EAAA,KAAK,UAAA;AAAA,IACH,GAAG,OAAO,CAAA,qBAAA,CAAA;AAAA,IACV;AAAA,MACE,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,MAC9C,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,YAAA,CAAa,EAAE,MAAK,EAAG,IAAA,IAAQ,CAAA,CAAE,iBAAiB,CAAC;AAAA,KAC1E;AAAA,IACA,EAAE,SAAS,CAAA;AAAE,GACf,CACG,IAAA,CAAK,CAAC,GAAA,KAAQ;AACb,IAAA,IAAI,IAAI,EAAA,EAAI;AACV,MAAA,IAAI;AACF,QAAA,cAAA,CAAe,OAAA,CAAQ,KAAK,GAAG,CAAA;AAAA,MACjC,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF,CAAC,CAAA,CACA,KAAA,CAAM,MAAM;AAAA,EAEb,CAAC,CAAA;AACL;AAEO,SAAS,kBAAA,CAAmB,EAAE,IAAA,EAAM,IAAA,EAAK,EAA4B;AAC1E,EAAA,MAAM,KAAA,GAAQ,sBAAsB,IAAI,CAAA;AAExC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,KAAA,EAAO,gBAAA,CAAiB,IAAA,EAAM,IAAI,CAAA;AAAA,EACxC,CAAA,EAAG,CAAC,KAAA,EAAO,IAAA,EAAM,IAAI,CAAC,CAAA;AAEtB,EAAA,OAAO,IAAA;AACT","file":"ArticleViewTracker-LI44SA4T.mjs","sourcesContent":["'use client'\n\n/**\n * @sonordev/site-kit article — reader-side view tracking\n *\n * Counts actual readers by POSTing to /public/articles/view once per post per\n * browser session. This replaces the server-side increment that rode\n * GET posts/:slug — under ISR that GET fires on REVALIDATION, not on reads,\n * so view_count was measuring cache churn instead of people.\n *\n * Childless by design (renders null, never wraps children) and gated behind\n * the same idle deferral SiteKitLayout uses, so it stays out of the critical\n * hydration window.\n */\n\nimport { useEffect } from 'react'\nimport { useDeferredActivation } from '../shared/defer'\nimport { sonorFetch } from '../shared/sonorFetch'\nimport { withSiteBody } from '../sites/site-param'\n\nconst SESSION_KEY_PREFIX = '__sonor_article_viewed__:'\n\nexport interface ArticleViewTrackerProps {\n slug: string\n /**\n * Sub-site host the view counts toward. Omit to use the host SiteKitLayout\n * published (`__SITE_KIT_SITE__`, the same one analytics tags page views\n * with), then NEXT_PUBLIC_SITE_URL, then `window.location.host`.\n */\n site?: string\n}\n\n/**\n * The effect body, exported for tests (the package's vitest runs in node, so\n * the component's mount path can't be exercised directly).\n */\nexport function trackArticleView(slug: string, site?: string): void {\n if (!slug || typeof window === 'undefined') return\n\n // SiteKitLayout globals; the credential is the minted st1. token, never\n // the raw key. Without SiteKitLayout the globals are absent — no-op.\n const w = window as Window & {\n __SITE_KIT_API_URL__?: string\n __SITE_KIT_API_KEY__?: string\n __SITE_KIT_SITE__?: string\n }\n const baseUrl = w.__SITE_KIT_API_URL__\n if (!baseUrl || !w.__SITE_KIT_API_KEY__) return\n\n // One view per post per session. sessionStorage can throw (private\n // windows, storage-blocked browsers) — treat a throw as \"not yet viewed\"\n // and skip writing rather than skip counting.\n const key = `${SESSION_KEY_PREFIX}${slug}`\n try {\n if (sessionStorage.getItem(key)) return\n } catch {\n /* unreadable storage — count the view, skip dedupe */\n }\n\n // retries: 0 — the increment is not idempotent, so a retried request\n // would double-count.\n void sonorFetch(\n `${baseUrl}/public/articles/view`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(withSiteBody({ slug }, site || w.__SITE_KIT_SITE__)),\n },\n { retries: 0 }\n )\n .then((res) => {\n if (res.ok) {\n try {\n sessionStorage.setItem(key, '1')\n } catch {\n /* storage write blocked — worst case is a re-count next mount */\n }\n }\n })\n .catch(() => {\n /* view tracking is best-effort; never surface errors */\n })\n}\n\nexport function ArticleViewTracker({ slug, site }: ArticleViewTrackerProps) {\n const ready = useDeferredActivation(true)\n\n useEffect(() => {\n if (ready) trackArticleView(slug, site)\n }, [ready, slug, site])\n\n return null\n}\n"]}
1
+ {"version":3,"sources":["../src/articles/ArticleViewTracker.tsx"],"names":[],"mappings":";;;;;;;;;;AAoBA,IAAM,kBAAA,GAAqB,2BAAA;AAgBpB,SAAS,gBAAA,CAAiB,MAAc,IAAA,EAAqB;AAClE,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,MAAA,KAAW,WAAA,EAAa;AAI5C,EAAA,MAAM,CAAA,GAAI,MAAA;AAKV,EAAA,MAAM,UAAU,CAAA,CAAE,oBAAA;AAClB,EAAA,IAAI,CAAC,OAAA,IAAW,CAAC,CAAA,CAAE,oBAAA,EAAsB;AAKzC,EAAA,MAAM,GAAA,GAAM,CAAA,EAAG,kBAAkB,CAAA,EAAG,IAAI,CAAA,CAAA;AACxC,EAAA,IAAI;AACF,IAAA,IAAI,cAAA,CAAe,OAAA,CAAQ,GAAG,CAAA,EAAG;AAAA,EACnC,CAAA,CAAA,MAAQ;AAAA,EAER;AAIA,EAAA,KAAK,UAAA;AAAA,IACH,GAAG,OAAO,CAAA,qBAAA,CAAA;AAAA,IACV;AAAA,MACE,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,MAC9C,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,YAAA,CAAa,EAAE,MAAK,EAAG,IAAA,IAAQ,CAAA,CAAE,iBAAiB,CAAC;AAAA,KAC1E;AAAA,IACA,EAAE,SAAS,CAAA;AAAE,GACf,CACG,IAAA,CAAK,CAAC,GAAA,KAAQ;AACb,IAAA,IAAI,IAAI,EAAA,EAAI;AACV,MAAA,IAAI;AACF,QAAA,cAAA,CAAe,OAAA,CAAQ,KAAK,GAAG,CAAA;AAAA,MACjC,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF,CAAC,CAAA,CACA,KAAA,CAAM,MAAM;AAAA,EAEb,CAAC,CAAA;AACL;AAEO,SAAS,kBAAA,CAAmB,EAAE,IAAA,EAAM,IAAA,EAAK,EAA4B;AAC1E,EAAA,MAAM,KAAA,GAAQ,sBAAsB,IAAI,CAAA;AAExC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,KAAA,EAAO,gBAAA,CAAiB,IAAA,EAAM,IAAI,CAAA;AAAA,EACxC,CAAA,EAAG,CAAC,KAAA,EAAO,IAAA,EAAM,IAAI,CAAC,CAAA;AAEtB,EAAA,OAAO,IAAA;AACT","file":"ArticleViewTracker-CML36HJZ.mjs","sourcesContent":["'use client'\n\n/**\n * @sonordev/site-kit article — reader-side view tracking\n *\n * Counts actual readers by POSTing to /public/articles/view once per post per\n * browser session. This replaces the server-side increment that rode\n * GET posts/:slug — under ISR that GET fires on REVALIDATION, not on reads,\n * so view_count was measuring cache churn instead of people.\n *\n * Childless by design (renders null, never wraps children) and gated behind\n * the same idle deferral SiteKitLayout uses, so it stays out of the critical\n * hydration window.\n */\n\nimport { useEffect } from 'react'\nimport { useDeferredActivation } from '../shared/defer'\nimport { sonorFetch } from '../shared/sonorFetch'\nimport { withSiteBody } from '../sites/site-param'\n\nconst SESSION_KEY_PREFIX = '__sonor_article_viewed__:'\n\nexport interface ArticleViewTrackerProps {\n slug: string\n /**\n * Sub-site host the view counts toward. Omit to use the host SiteKitLayout\n * published (`__SITE_KIT_SITE__`, the same one analytics tags page views\n * with), then NEXT_PUBLIC_SITE_URL, then `window.location.host`.\n */\n site?: string\n}\n\n/**\n * The effect body, exported for tests (the package's vitest runs in node, so\n * the component's mount path can't be exercised directly).\n */\nexport function trackArticleView(slug: string, site?: string): void {\n if (!slug || typeof window === 'undefined') return\n\n // SiteKitLayout globals; the credential is the minted st1. token, never\n // the raw key. Without SiteKitLayout the globals are absent — no-op.\n const w = window as Window & {\n __SITE_KIT_API_URL__?: string\n __SITE_KIT_API_KEY__?: string\n __SITE_KIT_SITE__?: string\n }\n const baseUrl = w.__SITE_KIT_API_URL__\n if (!baseUrl || !w.__SITE_KIT_API_KEY__) return\n\n // One view per post per session. sessionStorage can throw (private\n // windows, storage-blocked browsers) — treat a throw as \"not yet viewed\"\n // and skip writing rather than skip counting.\n const key = `${SESSION_KEY_PREFIX}${slug}`\n try {\n if (sessionStorage.getItem(key)) return\n } catch {\n /* unreadable storage — count the view, skip dedupe */\n }\n\n // retries: 0 — the increment is not idempotent, so a retried request\n // would double-count.\n void sonorFetch(\n `${baseUrl}/public/articles/view`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(withSiteBody({ slug }, site || w.__SITE_KIT_SITE__)),\n },\n { retries: 0 }\n )\n .then((res) => {\n if (res.ok) {\n try {\n sessionStorage.setItem(key, '1')\n } catch {\n /* storage write blocked — worst case is a re-count next mount */\n }\n }\n })\n .catch(() => {\n /* view tracking is best-effort; never surface errors */\n })\n}\n\nexport function ArticleViewTracker({ slug, site }: ArticleViewTrackerProps) {\n const ready = useDeferredActivation(true)\n\n useEffect(() => {\n if (ready) trackArticleView(slug, site)\n }, [ready, slug, site])\n\n return null\n}\n"]}
@@ -5,9 +5,9 @@ var chunkHGNSJXWT_js = require('./chunk-HGNSJXWT.js');
5
5
  var chunkBBBJHYPH_js = require('./chunk-BBBJHYPH.js');
6
6
  require('./chunk-UZKXOZHR.js');
7
7
  require('./chunk-SWIKKSKL.js');
8
- var chunk5ABTBPPK_js = require('./chunk-5ABTBPPK.js');
9
- require('./chunk-TC3JSQUS.js');
10
- require('./chunk-F3EAAAZ3.js');
8
+ var chunkZQJGCQHC_js = require('./chunk-ZQJGCQHC.js');
9
+ require('./chunk-LF44SJPP.js');
10
+ require('./chunk-OXA4ZSX2.js');
11
11
  require('./chunk-SNAQBZPT.js');
12
12
  var react = require('react');
13
13
 
@@ -22,7 +22,7 @@ function trackArticleView(slug, site) {
22
22
  if (sessionStorage.getItem(key)) return;
23
23
  } catch {
24
24
  }
25
- void chunk5ABTBPPK_js.sonorFetch(
25
+ void chunkZQJGCQHC_js.sonorFetch(
26
26
  `${baseUrl}/public/articles/view`,
27
27
  {
28
28
  method: "POST",
@@ -50,5 +50,5 @@ function ArticleViewTracker({ slug, site }) {
50
50
 
51
51
  exports.ArticleViewTracker = ArticleViewTracker;
52
52
  exports.trackArticleView = trackArticleView;
53
- //# sourceMappingURL=ArticleViewTracker-OQB2VT73.js.map
54
- //# sourceMappingURL=ArticleViewTracker-OQB2VT73.js.map
53
+ //# sourceMappingURL=ArticleViewTracker-FE2YWHX4.js.map
54
+ //# sourceMappingURL=ArticleViewTracker-FE2YWHX4.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/articles/ArticleViewTracker.tsx"],"names":["sonorFetch","withSiteBody","useDeferredActivation","useEffect"],"mappings":";;;;;;;;;;;;AAoBA,IAAM,kBAAA,GAAqB,2BAAA;AAgBpB,SAAS,gBAAA,CAAiB,MAAc,IAAA,EAAqB;AAClE,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,MAAA,KAAW,WAAA,EAAa;AAI5C,EAAA,MAAM,CAAA,GAAI,MAAA;AAKV,EAAA,MAAM,UAAU,CAAA,CAAE,oBAAA;AAClB,EAAA,IAAI,CAAC,OAAA,IAAW,CAAC,CAAA,CAAE,oBAAA,EAAsB;AAKzC,EAAA,MAAM,GAAA,GAAM,CAAA,EAAG,kBAAkB,CAAA,EAAG,IAAI,CAAA,CAAA;AACxC,EAAA,IAAI;AACF,IAAA,IAAI,cAAA,CAAe,OAAA,CAAQ,GAAG,CAAA,EAAG;AAAA,EACnC,CAAA,CAAA,MAAQ;AAAA,EAER;AAIA,EAAA,KAAKA,2BAAA;AAAA,IACH,GAAG,OAAO,CAAA,qBAAA,CAAA;AAAA,IACV;AAAA,MACE,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,MAC9C,IAAA,EAAM,IAAA,CAAK,SAAA,CAAUC,6BAAA,CAAa,EAAE,MAAK,EAAG,IAAA,IAAQ,CAAA,CAAE,iBAAiB,CAAC;AAAA,KAC1E;AAAA,IACA,EAAE,SAAS,CAAA;AAAE,GACf,CACG,IAAA,CAAK,CAAC,GAAA,KAAQ;AACb,IAAA,IAAI,IAAI,EAAA,EAAI;AACV,MAAA,IAAI;AACF,QAAA,cAAA,CAAe,OAAA,CAAQ,KAAK,GAAG,CAAA;AAAA,MACjC,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF,CAAC,CAAA,CACA,KAAA,CAAM,MAAM;AAAA,EAEb,CAAC,CAAA;AACL;AAEO,SAAS,kBAAA,CAAmB,EAAE,IAAA,EAAM,IAAA,EAAK,EAA4B;AAC1E,EAAA,MAAM,KAAA,GAAQC,uCAAsB,IAAI,CAAA;AAExC,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,KAAA,EAAO,gBAAA,CAAiB,IAAA,EAAM,IAAI,CAAA;AAAA,EACxC,CAAA,EAAG,CAAC,KAAA,EAAO,IAAA,EAAM,IAAI,CAAC,CAAA;AAEtB,EAAA,OAAO,IAAA;AACT","file":"ArticleViewTracker-OQB2VT73.js","sourcesContent":["'use client'\n\n/**\n * @sonordev/site-kit article — reader-side view tracking\n *\n * Counts actual readers by POSTing to /public/articles/view once per post per\n * browser session. This replaces the server-side increment that rode\n * GET posts/:slug — under ISR that GET fires on REVALIDATION, not on reads,\n * so view_count was measuring cache churn instead of people.\n *\n * Childless by design (renders null, never wraps children) and gated behind\n * the same idle deferral SiteKitLayout uses, so it stays out of the critical\n * hydration window.\n */\n\nimport { useEffect } from 'react'\nimport { useDeferredActivation } from '../shared/defer'\nimport { sonorFetch } from '../shared/sonorFetch'\nimport { withSiteBody } from '../sites/site-param'\n\nconst SESSION_KEY_PREFIX = '__sonor_article_viewed__:'\n\nexport interface ArticleViewTrackerProps {\n slug: string\n /**\n * Sub-site host the view counts toward. Omit to use the host SiteKitLayout\n * published (`__SITE_KIT_SITE__`, the same one analytics tags page views\n * with), then NEXT_PUBLIC_SITE_URL, then `window.location.host`.\n */\n site?: string\n}\n\n/**\n * The effect body, exported for tests (the package's vitest runs in node, so\n * the component's mount path can't be exercised directly).\n */\nexport function trackArticleView(slug: string, site?: string): void {\n if (!slug || typeof window === 'undefined') return\n\n // SiteKitLayout globals; the credential is the minted st1. token, never\n // the raw key. Without SiteKitLayout the globals are absent — no-op.\n const w = window as Window & {\n __SITE_KIT_API_URL__?: string\n __SITE_KIT_API_KEY__?: string\n __SITE_KIT_SITE__?: string\n }\n const baseUrl = w.__SITE_KIT_API_URL__\n if (!baseUrl || !w.__SITE_KIT_API_KEY__) return\n\n // One view per post per session. sessionStorage can throw (private\n // windows, storage-blocked browsers) — treat a throw as \"not yet viewed\"\n // and skip writing rather than skip counting.\n const key = `${SESSION_KEY_PREFIX}${slug}`\n try {\n if (sessionStorage.getItem(key)) return\n } catch {\n /* unreadable storage — count the view, skip dedupe */\n }\n\n // retries: 0 — the increment is not idempotent, so a retried request\n // would double-count.\n void sonorFetch(\n `${baseUrl}/public/articles/view`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(withSiteBody({ slug }, site || w.__SITE_KIT_SITE__)),\n },\n { retries: 0 }\n )\n .then((res) => {\n if (res.ok) {\n try {\n sessionStorage.setItem(key, '1')\n } catch {\n /* storage write blocked — worst case is a re-count next mount */\n }\n }\n })\n .catch(() => {\n /* view tracking is best-effort; never surface errors */\n })\n}\n\nexport function ArticleViewTracker({ slug, site }: ArticleViewTrackerProps) {\n const ready = useDeferredActivation(true)\n\n useEffect(() => {\n if (ready) trackArticleView(slug, site)\n }, [ready, slug, site])\n\n return null\n}\n"]}
1
+ {"version":3,"sources":["../src/articles/ArticleViewTracker.tsx"],"names":["sonorFetch","withSiteBody","useDeferredActivation","useEffect"],"mappings":";;;;;;;;;;;;AAoBA,IAAM,kBAAA,GAAqB,2BAAA;AAgBpB,SAAS,gBAAA,CAAiB,MAAc,IAAA,EAAqB;AAClE,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,MAAA,KAAW,WAAA,EAAa;AAI5C,EAAA,MAAM,CAAA,GAAI,MAAA;AAKV,EAAA,MAAM,UAAU,CAAA,CAAE,oBAAA;AAClB,EAAA,IAAI,CAAC,OAAA,IAAW,CAAC,CAAA,CAAE,oBAAA,EAAsB;AAKzC,EAAA,MAAM,GAAA,GAAM,CAAA,EAAG,kBAAkB,CAAA,EAAG,IAAI,CAAA,CAAA;AACxC,EAAA,IAAI;AACF,IAAA,IAAI,cAAA,CAAe,OAAA,CAAQ,GAAG,CAAA,EAAG;AAAA,EACnC,CAAA,CAAA,MAAQ;AAAA,EAER;AAIA,EAAA,KAAKA,2BAAA;AAAA,IACH,GAAG,OAAO,CAAA,qBAAA,CAAA;AAAA,IACV;AAAA,MACE,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,MAC9C,IAAA,EAAM,IAAA,CAAK,SAAA,CAAUC,6BAAA,CAAa,EAAE,MAAK,EAAG,IAAA,IAAQ,CAAA,CAAE,iBAAiB,CAAC;AAAA,KAC1E;AAAA,IACA,EAAE,SAAS,CAAA;AAAE,GACf,CACG,IAAA,CAAK,CAAC,GAAA,KAAQ;AACb,IAAA,IAAI,IAAI,EAAA,EAAI;AACV,MAAA,IAAI;AACF,QAAA,cAAA,CAAe,OAAA,CAAQ,KAAK,GAAG,CAAA;AAAA,MACjC,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF,CAAC,CAAA,CACA,KAAA,CAAM,MAAM;AAAA,EAEb,CAAC,CAAA;AACL;AAEO,SAAS,kBAAA,CAAmB,EAAE,IAAA,EAAM,IAAA,EAAK,EAA4B;AAC1E,EAAA,MAAM,KAAA,GAAQC,uCAAsB,IAAI,CAAA;AAExC,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,KAAA,EAAO,gBAAA,CAAiB,IAAA,EAAM,IAAI,CAAA;AAAA,EACxC,CAAA,EAAG,CAAC,KAAA,EAAO,IAAA,EAAM,IAAI,CAAC,CAAA;AAEtB,EAAA,OAAO,IAAA;AACT","file":"ArticleViewTracker-FE2YWHX4.js","sourcesContent":["'use client'\n\n/**\n * @sonordev/site-kit article — reader-side view tracking\n *\n * Counts actual readers by POSTing to /public/articles/view once per post per\n * browser session. This replaces the server-side increment that rode\n * GET posts/:slug — under ISR that GET fires on REVALIDATION, not on reads,\n * so view_count was measuring cache churn instead of people.\n *\n * Childless by design (renders null, never wraps children) and gated behind\n * the same idle deferral SiteKitLayout uses, so it stays out of the critical\n * hydration window.\n */\n\nimport { useEffect } from 'react'\nimport { useDeferredActivation } from '../shared/defer'\nimport { sonorFetch } from '../shared/sonorFetch'\nimport { withSiteBody } from '../sites/site-param'\n\nconst SESSION_KEY_PREFIX = '__sonor_article_viewed__:'\n\nexport interface ArticleViewTrackerProps {\n slug: string\n /**\n * Sub-site host the view counts toward. Omit to use the host SiteKitLayout\n * published (`__SITE_KIT_SITE__`, the same one analytics tags page views\n * with), then NEXT_PUBLIC_SITE_URL, then `window.location.host`.\n */\n site?: string\n}\n\n/**\n * The effect body, exported for tests (the package's vitest runs in node, so\n * the component's mount path can't be exercised directly).\n */\nexport function trackArticleView(slug: string, site?: string): void {\n if (!slug || typeof window === 'undefined') return\n\n // SiteKitLayout globals; the credential is the minted st1. token, never\n // the raw key. Without SiteKitLayout the globals are absent — no-op.\n const w = window as Window & {\n __SITE_KIT_API_URL__?: string\n __SITE_KIT_API_KEY__?: string\n __SITE_KIT_SITE__?: string\n }\n const baseUrl = w.__SITE_KIT_API_URL__\n if (!baseUrl || !w.__SITE_KIT_API_KEY__) return\n\n // One view per post per session. sessionStorage can throw (private\n // windows, storage-blocked browsers) — treat a throw as \"not yet viewed\"\n // and skip writing rather than skip counting.\n const key = `${SESSION_KEY_PREFIX}${slug}`\n try {\n if (sessionStorage.getItem(key)) return\n } catch {\n /* unreadable storage — count the view, skip dedupe */\n }\n\n // retries: 0 — the increment is not idempotent, so a retried request\n // would double-count.\n void sonorFetch(\n `${baseUrl}/public/articles/view`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(withSiteBody({ slug }, site || w.__SITE_KIT_SITE__)),\n },\n { retries: 0 }\n )\n .then((res) => {\n if (res.ok) {\n try {\n sessionStorage.setItem(key, '1')\n } catch {\n /* storage write blocked — worst case is a re-count next mount */\n }\n }\n })\n .catch(() => {\n /* view tracking is best-effort; never surface errors */\n })\n}\n\nexport function ArticleViewTracker({ slug, site }: ArticleViewTrackerProps) {\n const ready = useDeferredActivation(true)\n\n useEffect(() => {\n if (ready) trackArticleView(slug, site)\n }, [ready, slug, site])\n\n return null\n}\n"]}
@@ -0,0 +1,16 @@
1
+ export { ChatWidget } from './chunk-4EJI4IOX.mjs';
2
+ import './chunk-4U5NCKQY.mjs';
3
+ import './chunk-OBKS3CPP.mjs';
4
+ import './chunk-NSWO43XW.mjs';
5
+ import './chunk-YP5WAFTI.mjs';
6
+ import './chunk-QRHVMTHF.mjs';
7
+ import './chunk-OQWAJBYA.mjs';
8
+ import './chunk-TMVKRQGJ.mjs';
9
+ import './chunk-5SANRSX3.mjs';
10
+ import './chunk-6EDVCUAG.mjs';
11
+ import './chunk-RGCUND5Q.mjs';
12
+ import './chunk-3DXJHMQM.mjs';
13
+ import './chunk-ACKFL6WA.mjs';
14
+ import './chunk-BEXTDHO4.mjs';
15
+ //# sourceMappingURL=ChatWidget-GVNDKGUU.mjs.map
16
+ //# sourceMappingURL=ChatWidget-GVNDKGUU.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"ChatWidget-56SW5ITH.mjs"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"ChatWidget-GVNDKGUU.mjs"}
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ var chunkXKJLJLQ4_js = require('./chunk-XKJLJLQ4.js');
4
+ require('./chunk-ESP5RMB7.js');
5
+ require('./chunk-NBOBOYMT.js');
6
+ require('./chunk-G6WF4G3E.js');
7
+ require('./chunk-SDGS2R5O.js');
8
+ require('./chunk-5XFFUP6D.js');
9
+ require('./chunk-AQUWWATH.js');
10
+ require('./chunk-KHPJ3CEL.js');
11
+ require('./chunk-P4Y75QSU.js');
12
+ require('./chunk-VJMPIRSD.js');
13
+ require('./chunk-ZQJGCQHC.js');
14
+ require('./chunk-LF44SJPP.js');
15
+ require('./chunk-OXA4ZSX2.js');
16
+ require('./chunk-SNAQBZPT.js');
17
+
18
+
19
+
20
+ Object.defineProperty(exports, "ChatWidget", {
21
+ enumerable: true,
22
+ get: function () { return chunkXKJLJLQ4_js.ChatWidget; }
23
+ });
24
+ //# sourceMappingURL=ChatWidget-YL34KOZM.js.map
25
+ //# sourceMappingURL=ChatWidget-YL34KOZM.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"ChatWidget-HR3OC3WA.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"ChatWidget-YL34KOZM.js"}
@@ -0,0 +1,12 @@
1
+ export { EngageWidget } from './chunk-NMKUYGKK.mjs';
2
+ import './chunk-4U5NCKQY.mjs';
3
+ import './chunk-NSWO43XW.mjs';
4
+ import './chunk-ZZQAGCSI.mjs';
5
+ import './chunk-6EDVCUAG.mjs';
6
+ import './chunk-A6UYV475.mjs';
7
+ import './chunk-RGCUND5Q.mjs';
8
+ import './chunk-3DXJHMQM.mjs';
9
+ import './chunk-ACKFL6WA.mjs';
10
+ import './chunk-BEXTDHO4.mjs';
11
+ //# sourceMappingURL=EngageWidget-465O5XUV.mjs.map
12
+ //# sourceMappingURL=EngageWidget-465O5XUV.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"EngageWidget-L4X35N2B.mjs"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"EngageWidget-465O5XUV.mjs"}
@@ -0,0 +1,21 @@
1
+ 'use strict';
2
+
3
+ var chunkRVFOUPK4_js = require('./chunk-RVFOUPK4.js');
4
+ require('./chunk-ESP5RMB7.js');
5
+ require('./chunk-G6WF4G3E.js');
6
+ require('./chunk-RC4WWQXR.js');
7
+ require('./chunk-VJMPIRSD.js');
8
+ require('./chunk-5RNAUYWE.js');
9
+ require('./chunk-ZQJGCQHC.js');
10
+ require('./chunk-LF44SJPP.js');
11
+ require('./chunk-OXA4ZSX2.js');
12
+ require('./chunk-SNAQBZPT.js');
13
+
14
+
15
+
16
+ Object.defineProperty(exports, "EngageWidget", {
17
+ enumerable: true,
18
+ get: function () { return chunkRVFOUPK4_js.EngageWidget; }
19
+ });
20
+ //# sourceMappingURL=EngageWidget-IQRKRYPG.js.map
21
+ //# sourceMappingURL=EngageWidget-IQRKRYPG.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"EngageWidget-HXGI3ZKG.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"EngageWidget-IQRKRYPG.js"}
@@ -1,9 +1,9 @@
1
1
  'use client';
2
2
  'use strict';
3
3
 
4
- var chunk5ABTBPPK_js = require('./chunk-5ABTBPPK.js');
5
- require('./chunk-TC3JSQUS.js');
6
- require('./chunk-F3EAAAZ3.js');
4
+ var chunkZQJGCQHC_js = require('./chunk-ZQJGCQHC.js');
5
+ require('./chunk-LF44SJPP.js');
6
+ require('./chunk-OXA4ZSX2.js');
7
7
  require('./chunk-SNAQBZPT.js');
8
8
  var React = require('react');
9
9
  var jsxRuntime = require('react/jsx-runtime');
@@ -67,7 +67,7 @@ async function uploadFormFile(file) {
67
67
  }
68
68
  const body = new FormData();
69
69
  body.append("file", file);
70
- const response = await chunk5ABTBPPK_js.sonorFetch(
70
+ const response = await chunkZQJGCQHC_js.sonorFetch(
71
71
  `${apiUrl}/api/public/forms/upload`,
72
72
  {
73
73
  method: "POST",
@@ -87,5 +87,5 @@ async function uploadFormFile(file) {
87
87
  }
88
88
 
89
89
  exports.FileField = FileField;
90
- //# sourceMappingURL=FileField-N2P6E2PN.js.map
91
- //# sourceMappingURL=FileField-N2P6E2PN.js.map
90
+ //# sourceMappingURL=FileField-F3PFKMIF.js.map
91
+ //# sourceMappingURL=FileField-F3PFKMIF.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/forms/FileField.tsx"],"names":["React","jsxs","Fragment","jsx","sonorFetch"],"mappings":"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","file":"FileField-N2P6E2PN.js","sourcesContent":["'use client'\n\n/**\n * File upload field — the one field type that cannot render on the server.\n *\n * It holds upload state and reads the browser-published credential globals, so\n * it lives here rather than in FormField.tsx. Pulling it out is what lets\n * FormField drop its `'use client'` directive and render inside a Server\n * Component (see StaticForm.tsx); a form WITHOUT a file field now ships no\n * field-rendering JavaScript at all.\n *\n * Uploads the chosen file to the Sonor API, which stores it and returns a\n * public URL — that URL becomes the field value, so large files never touch\n * the JSON submission body. The value stays empty until the upload succeeds,\n * so a required file field correctly blocks submission until the upload\n * completes (rather than submitting a useless bare filename).\n *\n * Note: forms containing a file field are NOT native-POST eligible (see\n * native-fallback.tsx) — a form-encoded POST cannot run this upload — so the\n * server shell renders a plain disabled-looking file input and the real\n * control arrives with the enhancement.\n */\n\nimport React from 'react'\nimport type { FormField as FormFieldType } from './types'\nimport { sonorFetch } from '../shared/sonorFetch'\n\nexport interface FileFieldProps {\n field: FormFieldType\n inputId: string\n classPrefix: string\n baseInputStyle: React.CSSProperties\n onChange: (value: unknown) => void\n}\n\nexport function FileField({\n field,\n inputId,\n classPrefix,\n baseInputStyle,\n onChange,\n}: FileFieldProps) {\n const [status, setStatus] = React.useState<'idle' | 'uploading' | 'error'>('idle')\n const [fileName, setFileName] = React.useState('')\n\n const handleFile = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0]\n if (!file) return\n setFileName(file.name)\n setStatus('uploading')\n onChange('') // clear any prior URL until this upload completes\n try {\n const url = await uploadFormFile(file)\n onChange(url)\n setStatus('idle')\n } catch (err) {\n console.error('[Forms] File upload failed:', err)\n onChange('')\n setStatus('error')\n }\n }\n\n return (\n <>\n <input\n id={inputId}\n className={`${classPrefix}__input ${classPrefix}__input--file`}\n type=\"file\"\n name={field.slug}\n required={field.is_required}\n disabled={status === 'uploading'}\n onChange={handleFile}\n style={baseInputStyle}\n />\n {status === 'uploading' && (\n <p style={{ color: 'var(--sk-text-tertiary, #6b7280)', fontSize: 14, marginTop: 4 }}>\n Uploading {fileName}…\n </p>\n )}\n {status === 'error' && (\n <p role=\"alert\" style={{ color: 'var(--sk-error, #dc2626)', fontSize: 14, marginTop: 4 }}>\n Upload failed. Please choose the file again.\n </p>\n )}\n </>\n )\n}\n\n/** Upload a single file to the Sonor API and return its stored public URL. */\nasync function uploadFormFile(file: File): Promise<string> {\n const apiUrl =\n typeof window !== 'undefined'\n ? (window as any).__SITE_KIT_API_URL__ || 'https://api.sonor.io'\n : 'https://api.sonor.io'\n const apiKey =\n typeof window !== 'undefined' ? (window as any).__SITE_KIT_API_KEY__ : undefined\n if (!apiKey) {\n throw new Error('API key is required to upload files.')\n }\n\n const body = new FormData()\n body.append('file', file)\n\n // No Content-Type header — the browser sets the multipart boundary itself.\n const response = await sonorFetch(\n `${apiUrl}/api/public/forms/upload`,\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${apiKey}` },\n body,\n },\n { retries: 1, timeoutMs: 60000 },\n )\n\n if (!response.ok) {\n throw new Error(`Upload failed (${response.status})`)\n }\n const data = (await response.json().catch(() => null)) as { url?: string } | null\n if (!data?.url) {\n throw new Error('Upload did not return a URL.')\n }\n return data.url\n}\n"]}
1
+ {"version":3,"sources":["../src/forms/FileField.tsx"],"names":["React","jsxs","Fragment","jsx","sonorFetch"],"mappings":"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","file":"FileField-F3PFKMIF.js","sourcesContent":["'use client'\n\n/**\n * File upload field — the one field type that cannot render on the server.\n *\n * It holds upload state and reads the browser-published credential globals, so\n * it lives here rather than in FormField.tsx. Pulling it out is what lets\n * FormField drop its `'use client'` directive and render inside a Server\n * Component (see StaticForm.tsx); a form WITHOUT a file field now ships no\n * field-rendering JavaScript at all.\n *\n * Uploads the chosen file to the Sonor API, which stores it and returns a\n * public URL — that URL becomes the field value, so large files never touch\n * the JSON submission body. The value stays empty until the upload succeeds,\n * so a required file field correctly blocks submission until the upload\n * completes (rather than submitting a useless bare filename).\n *\n * Note: forms containing a file field are NOT native-POST eligible (see\n * native-fallback.tsx) — a form-encoded POST cannot run this upload — so the\n * server shell renders a plain disabled-looking file input and the real\n * control arrives with the enhancement.\n */\n\nimport React from 'react'\nimport type { FormField as FormFieldType } from './types'\nimport { sonorFetch } from '../shared/sonorFetch'\n\nexport interface FileFieldProps {\n field: FormFieldType\n inputId: string\n classPrefix: string\n baseInputStyle: React.CSSProperties\n onChange: (value: unknown) => void\n}\n\nexport function FileField({\n field,\n inputId,\n classPrefix,\n baseInputStyle,\n onChange,\n}: FileFieldProps) {\n const [status, setStatus] = React.useState<'idle' | 'uploading' | 'error'>('idle')\n const [fileName, setFileName] = React.useState('')\n\n const handleFile = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0]\n if (!file) return\n setFileName(file.name)\n setStatus('uploading')\n onChange('') // clear any prior URL until this upload completes\n try {\n const url = await uploadFormFile(file)\n onChange(url)\n setStatus('idle')\n } catch (err) {\n console.error('[Forms] File upload failed:', err)\n onChange('')\n setStatus('error')\n }\n }\n\n return (\n <>\n <input\n id={inputId}\n className={`${classPrefix}__input ${classPrefix}__input--file`}\n type=\"file\"\n name={field.slug}\n required={field.is_required}\n disabled={status === 'uploading'}\n onChange={handleFile}\n style={baseInputStyle}\n />\n {status === 'uploading' && (\n <p style={{ color: 'var(--sk-text-tertiary, #6b7280)', fontSize: 14, marginTop: 4 }}>\n Uploading {fileName}…\n </p>\n )}\n {status === 'error' && (\n <p role=\"alert\" style={{ color: 'var(--sk-error, #dc2626)', fontSize: 14, marginTop: 4 }}>\n Upload failed. Please choose the file again.\n </p>\n )}\n </>\n )\n}\n\n/** Upload a single file to the Sonor API and return its stored public URL. */\nasync function uploadFormFile(file: File): Promise<string> {\n const apiUrl =\n typeof window !== 'undefined'\n ? (window as any).__SITE_KIT_API_URL__ || 'https://api.sonor.io'\n : 'https://api.sonor.io'\n const apiKey =\n typeof window !== 'undefined' ? (window as any).__SITE_KIT_API_KEY__ : undefined\n if (!apiKey) {\n throw new Error('API key is required to upload files.')\n }\n\n const body = new FormData()\n body.append('file', file)\n\n // No Content-Type header — the browser sets the multipart boundary itself.\n const response = await sonorFetch(\n `${apiUrl}/api/public/forms/upload`,\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${apiKey}` },\n body,\n },\n { retries: 1, timeoutMs: 60000 },\n )\n\n if (!response.ok) {\n throw new Error(`Upload failed (${response.status})`)\n }\n const data = (await response.json().catch(() => null)) as { url?: string } | null\n if (!data?.url) {\n throw new Error('Upload did not return a URL.')\n }\n return data.url\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  'use client';
2
- import { sonorFetch } from './chunk-SU42KCQD.mjs';
3
- import './chunk-L63DFM4R.mjs';
4
- import './chunk-7MAHP735.mjs';
2
+ import { sonorFetch } from './chunk-RGCUND5Q.mjs';
3
+ import './chunk-3DXJHMQM.mjs';
4
+ import './chunk-ACKFL6WA.mjs';
5
5
  import './chunk-BEXTDHO4.mjs';
6
6
  import React from 'react';
7
7
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
@@ -81,5 +81,5 @@ async function uploadFormFile(file) {
81
81
  }
82
82
 
83
83
  export { FileField };
84
- //# sourceMappingURL=FileField-GSTLU42O.mjs.map
85
- //# sourceMappingURL=FileField-GSTLU42O.mjs.map
84
+ //# sourceMappingURL=FileField-FWKSBFRX.mjs.map
85
+ //# sourceMappingURL=FileField-FWKSBFRX.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/forms/FileField.tsx"],"names":[],"mappings":"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","file":"FileField-GSTLU42O.mjs","sourcesContent":["'use client'\n\n/**\n * File upload field — the one field type that cannot render on the server.\n *\n * It holds upload state and reads the browser-published credential globals, so\n * it lives here rather than in FormField.tsx. Pulling it out is what lets\n * FormField drop its `'use client'` directive and render inside a Server\n * Component (see StaticForm.tsx); a form WITHOUT a file field now ships no\n * field-rendering JavaScript at all.\n *\n * Uploads the chosen file to the Sonor API, which stores it and returns a\n * public URL — that URL becomes the field value, so large files never touch\n * the JSON submission body. The value stays empty until the upload succeeds,\n * so a required file field correctly blocks submission until the upload\n * completes (rather than submitting a useless bare filename).\n *\n * Note: forms containing a file field are NOT native-POST eligible (see\n * native-fallback.tsx) — a form-encoded POST cannot run this upload — so the\n * server shell renders a plain disabled-looking file input and the real\n * control arrives with the enhancement.\n */\n\nimport React from 'react'\nimport type { FormField as FormFieldType } from './types'\nimport { sonorFetch } from '../shared/sonorFetch'\n\nexport interface FileFieldProps {\n field: FormFieldType\n inputId: string\n classPrefix: string\n baseInputStyle: React.CSSProperties\n onChange: (value: unknown) => void\n}\n\nexport function FileField({\n field,\n inputId,\n classPrefix,\n baseInputStyle,\n onChange,\n}: FileFieldProps) {\n const [status, setStatus] = React.useState<'idle' | 'uploading' | 'error'>('idle')\n const [fileName, setFileName] = React.useState('')\n\n const handleFile = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0]\n if (!file) return\n setFileName(file.name)\n setStatus('uploading')\n onChange('') // clear any prior URL until this upload completes\n try {\n const url = await uploadFormFile(file)\n onChange(url)\n setStatus('idle')\n } catch (err) {\n console.error('[Forms] File upload failed:', err)\n onChange('')\n setStatus('error')\n }\n }\n\n return (\n <>\n <input\n id={inputId}\n className={`${classPrefix}__input ${classPrefix}__input--file`}\n type=\"file\"\n name={field.slug}\n required={field.is_required}\n disabled={status === 'uploading'}\n onChange={handleFile}\n style={baseInputStyle}\n />\n {status === 'uploading' && (\n <p style={{ color: 'var(--sk-text-tertiary, #6b7280)', fontSize: 14, marginTop: 4 }}>\n Uploading {fileName}…\n </p>\n )}\n {status === 'error' && (\n <p role=\"alert\" style={{ color: 'var(--sk-error, #dc2626)', fontSize: 14, marginTop: 4 }}>\n Upload failed. Please choose the file again.\n </p>\n )}\n </>\n )\n}\n\n/** Upload a single file to the Sonor API and return its stored public URL. */\nasync function uploadFormFile(file: File): Promise<string> {\n const apiUrl =\n typeof window !== 'undefined'\n ? (window as any).__SITE_KIT_API_URL__ || 'https://api.sonor.io'\n : 'https://api.sonor.io'\n const apiKey =\n typeof window !== 'undefined' ? (window as any).__SITE_KIT_API_KEY__ : undefined\n if (!apiKey) {\n throw new Error('API key is required to upload files.')\n }\n\n const body = new FormData()\n body.append('file', file)\n\n // No Content-Type header — the browser sets the multipart boundary itself.\n const response = await sonorFetch(\n `${apiUrl}/api/public/forms/upload`,\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${apiKey}` },\n body,\n },\n { retries: 1, timeoutMs: 60000 },\n )\n\n if (!response.ok) {\n throw new Error(`Upload failed (${response.status})`)\n }\n const data = (await response.json().catch(() => null)) as { url?: string } | null\n if (!data?.url) {\n throw new Error('Upload did not return a URL.')\n }\n return data.url\n}\n"]}
1
+ {"version":3,"sources":["../src/forms/FileField.tsx"],"names":[],"mappings":"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","file":"FileField-FWKSBFRX.mjs","sourcesContent":["'use client'\n\n/**\n * File upload field — the one field type that cannot render on the server.\n *\n * It holds upload state and reads the browser-published credential globals, so\n * it lives here rather than in FormField.tsx. Pulling it out is what lets\n * FormField drop its `'use client'` directive and render inside a Server\n * Component (see StaticForm.tsx); a form WITHOUT a file field now ships no\n * field-rendering JavaScript at all.\n *\n * Uploads the chosen file to the Sonor API, which stores it and returns a\n * public URL — that URL becomes the field value, so large files never touch\n * the JSON submission body. The value stays empty until the upload succeeds,\n * so a required file field correctly blocks submission until the upload\n * completes (rather than submitting a useless bare filename).\n *\n * Note: forms containing a file field are NOT native-POST eligible (see\n * native-fallback.tsx) — a form-encoded POST cannot run this upload — so the\n * server shell renders a plain disabled-looking file input and the real\n * control arrives with the enhancement.\n */\n\nimport React from 'react'\nimport type { FormField as FormFieldType } from './types'\nimport { sonorFetch } from '../shared/sonorFetch'\n\nexport interface FileFieldProps {\n field: FormFieldType\n inputId: string\n classPrefix: string\n baseInputStyle: React.CSSProperties\n onChange: (value: unknown) => void\n}\n\nexport function FileField({\n field,\n inputId,\n classPrefix,\n baseInputStyle,\n onChange,\n}: FileFieldProps) {\n const [status, setStatus] = React.useState<'idle' | 'uploading' | 'error'>('idle')\n const [fileName, setFileName] = React.useState('')\n\n const handleFile = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0]\n if (!file) return\n setFileName(file.name)\n setStatus('uploading')\n onChange('') // clear any prior URL until this upload completes\n try {\n const url = await uploadFormFile(file)\n onChange(url)\n setStatus('idle')\n } catch (err) {\n console.error('[Forms] File upload failed:', err)\n onChange('')\n setStatus('error')\n }\n }\n\n return (\n <>\n <input\n id={inputId}\n className={`${classPrefix}__input ${classPrefix}__input--file`}\n type=\"file\"\n name={field.slug}\n required={field.is_required}\n disabled={status === 'uploading'}\n onChange={handleFile}\n style={baseInputStyle}\n />\n {status === 'uploading' && (\n <p style={{ color: 'var(--sk-text-tertiary, #6b7280)', fontSize: 14, marginTop: 4 }}>\n Uploading {fileName}…\n </p>\n )}\n {status === 'error' && (\n <p role=\"alert\" style={{ color: 'var(--sk-error, #dc2626)', fontSize: 14, marginTop: 4 }}>\n Upload failed. Please choose the file again.\n </p>\n )}\n </>\n )\n}\n\n/** Upload a single file to the Sonor API and return its stored public URL. */\nasync function uploadFormFile(file: File): Promise<string> {\n const apiUrl =\n typeof window !== 'undefined'\n ? (window as any).__SITE_KIT_API_URL__ || 'https://api.sonor.io'\n : 'https://api.sonor.io'\n const apiKey =\n typeof window !== 'undefined' ? (window as any).__SITE_KIT_API_KEY__ : undefined\n if (!apiKey) {\n throw new Error('API key is required to upload files.')\n }\n\n const body = new FormData()\n body.append('file', file)\n\n // No Content-Type header — the browser sets the multipart boundary itself.\n const response = await sonorFetch(\n `${apiUrl}/api/public/forms/upload`,\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${apiKey}` },\n body,\n },\n { retries: 1, timeoutMs: 60000 },\n )\n\n if (!response.ok) {\n throw new Error(`Upload failed (${response.status})`)\n }\n const data = (await response.json().catch(() => null)) as { url?: string } | null\n if (!data?.url) {\n throw new Error('Upload did not return a URL.')\n }\n return data.url\n}\n"]}
@@ -2,7 +2,7 @@
2
2
  'use strict';
3
3
 
4
4
  var chunkMVHR77V4_js = require('./chunk-MVHR77V4.js');
5
- var chunkUILVPI2R_js = require('./chunk-UILVPI2R.js');
5
+ var chunk36NE4PSS_js = require('./chunk-36NE4PSS.js');
6
6
  var chunkXA2GJFVM_js = require('./chunk-XA2GJFVM.js');
7
7
  var chunk5XFFUP6D_js = require('./chunk-5XFFUP6D.js');
8
8
  require('./chunk-SNAQBZPT.js');
@@ -46,7 +46,7 @@ function renderCompletionTemplate(template, field, values) {
46
46
  if (token === "label") return field.label;
47
47
  if (token === "first_name") {
48
48
  const key = Object.keys(values).find((k) => /^first[_-]?name$/i.test(k));
49
- return (key ? chunkUILVPI2R_js.stageFirstName(values[key]) : null) || "";
49
+ return (key ? chunk36NE4PSS_js.stageFirstName(values[key]) : null) || "";
50
50
  }
51
51
  return stringify(values[token]);
52
52
  }).replace(/\s{2,}/g, " ").trim();
@@ -68,7 +68,7 @@ function groupSentence(fields, values) {
68
68
  );
69
69
  if (firstNameField || anyNameField) {
70
70
  const source = firstNameField ?? anyNameField;
71
- const name = chunkUILVPI2R_js.stageFirstName(val(source));
71
+ const name = chunk36NE4PSS_js.stageFirstName(val(source));
72
72
  if (name) return `Nice to meet you, ${name}.`;
73
73
  }
74
74
  const email = fields.find(
@@ -255,9 +255,9 @@ function FormSpotlight({
255
255
  trackFieldBlur,
256
256
  honeypot
257
257
  }) {
258
- const fieldControls = chunkUILVPI2R_js.useFieldControls(config.fields);
258
+ const fieldControls = chunk36NE4PSS_js.useFieldControls(config.fields);
259
259
  const views = react.useMemo(() => {
260
- return chunkUILVPI2R_js.stageViews(config).map((v) => ({ ...v, fields: v.fields.filter(isFieldVisible) })).filter((v) => v.fields.length > 0);
260
+ return chunk36NE4PSS_js.stageViews(config).map((v) => ({ ...v, fields: v.fields.filter(isFieldVisible) })).filter((v) => v.fields.length > 0);
261
261
  }, [config, isFieldVisible]);
262
262
  const questions = react.useMemo(() => views.flatMap((v) => v.fields), [views]);
263
263
  const questionCount = questions.length;
@@ -593,7 +593,7 @@ function FormSpotlight({
593
593
  ),
594
594
  /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "sk-stage__head", "data-sk-part": "stage-head", children: [
595
595
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sk-stage__head-name", children: config.name }),
596
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sk-stage__head-meta", "data-sk-part": "stage-estimate", children: `Takes ${chunkUILVPI2R_js.stageEstimate(questionCount)}` })
596
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sk-stage__head-meta", "data-sk-part": "stage-estimate", children: `Takes ${chunk36NE4PSS_js.stageEstimate(questionCount)}` })
597
597
  ] }),
598
598
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sk-stage__hud", "data-sk-part": "stage-hud", children: [
599
599
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -689,7 +689,7 @@ function FormSpotlight({
689
689
  showValid: !!touched[f.slug] && !errors[f.slug] && fieldIsComplete(f),
690
690
  onChange: handleFieldChange(
691
691
  f,
692
- view.fields.length === 1 && chunkUILVPI2R_js.AUTO_ADVANCE_TYPES.includes(f.field_type)
692
+ view.fields.length === 1 && chunk36NE4PSS_js.AUTO_ADVANCE_TYPES.includes(f.field_type)
693
693
  ),
694
694
  onFocus: () => trackFieldFocus(f.slug),
695
695
  onBlur: () => trackFieldBlur(f.slug)
@@ -757,5 +757,5 @@ exports.FormSpotlight = FormSpotlight;
757
757
  exports.SpotStyles = SpotStyles;
758
758
  exports.groupSentence = groupSentence;
759
759
  exports.renderCompletionTemplate = renderCompletionTemplate;
760
- //# sourceMappingURL=FormSpotlight-QLXOS5LN.js.map
761
- //# sourceMappingURL=FormSpotlight-QLXOS5LN.js.map
760
+ //# sourceMappingURL=FormSpotlight-DJQ3P3EX.js.map
761
+ //# sourceMappingURL=FormSpotlight-DJQ3P3EX.js.map