@sonordev/site-kit 7.1.1 → 7.2.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 (282) hide show
  1. package/AGENTS.md +3 -3
  2. package/CHANGELOG.md +68 -3426
  3. package/README.md +9 -8
  4. package/agent-manifest.json +12 -4
  5. package/dist/{AnalyticsProvider-3MI5RCTL.js → AnalyticsProvider-SLFFBRPE.js} +4 -4
  6. package/dist/{ArticleViewTracker-CMBSJR3N.js → ArticleViewTracker-TM5AMGN2.js} +3 -3
  7. package/dist/{BlocksPopup-X3OAAK2L.js → BlocksPopup-YJNJIVPO.js} +5 -5
  8. package/dist/ChatWidget-XKOSH66S.js +17 -0
  9. package/dist/{FileField-QRSEGO6D.js → FileField-CI4UL5SH.js} +3 -3
  10. package/dist/{FormSpotlight-XKV7VTN4.js → FormSpotlight-F2AAEGBN.js} +20 -6
  11. package/dist/{FormStage-C3YGYAUZ.js → FormStage-BI5R5CNC.js} +27 -7
  12. package/dist/ManagedForm-G6COGHXL.js +16 -0
  13. package/dist/{ManagedNewsletterForm-LPIKGJF4.js → ManagedNewsletterForm-BO3WCGXX.js} +7 -5
  14. package/dist/{SignalCore-ICXXZBYI.js → SignalCore-ZA6WHKBR.js} +3 -3
  15. package/dist/SiteChat-YJADDTKG.js +5 -0
  16. package/dist/{SiteDesignReporter-A7MS2CIP.js → SiteDesignReporter-5YAW36AQ.js} +5 -5
  17. package/dist/SitePopups-ST5PMKZE.js +10 -0
  18. package/dist/SitemapSync-RJTEBINX.js +8 -0
  19. package/dist/SitemapSync.d.ts +1 -1
  20. package/dist/_client/booking-widget.js +5 -5
  21. package/dist/_client/testimonial-section.d.ts +1 -1
  22. package/dist/affiliates/api.d.ts +1 -1
  23. package/dist/affiliates/index.js +3 -3
  24. package/dist/analytics/AnalyticsProvider.d.ts +1 -1
  25. package/dist/analytics/index.js +4 -4
  26. package/dist/analytics/send-gate.d.ts +13 -26
  27. package/dist/articles/Article.d.ts +1 -1
  28. package/dist/articles/ArticleList.d.ts +1 -1
  29. package/dist/articles/ClusterLandingPage.d.ts +2 -2
  30. package/dist/articles/PublicationLayout.d.ts +1 -1
  31. package/dist/articles/PublicationSidebar.d.ts +1 -1
  32. package/dist/articles/RelatedPosts.d.ts +1 -1
  33. package/dist/articles/author-schema.d.ts +1 -1
  34. package/dist/articles/excerpt.d.ts +3 -4
  35. package/dist/articles/index.d.ts +2 -2
  36. package/dist/articles/index.js +2 -2
  37. package/dist/articles/news-sitemap.d.ts +1 -1
  38. package/dist/articles/processArticleHtml.d.ts +1 -1
  39. package/dist/articles/server-core.d.ts +4 -4
  40. package/dist/articles/server-ui.js +3 -2
  41. package/dist/articles/server.d.ts +4 -4
  42. package/dist/articles/server.js +2 -1
  43. package/dist/articles/types.d.ts +1 -1
  44. package/dist/{engage → chat}/ChatWidget.d.ts +8 -7
  45. package/dist/{engage → chat}/EchoUiActions.d.ts +4 -4
  46. package/dist/chat/SiteChat.d.ts +25 -0
  47. package/dist/{engage → chat}/brand-color.d.ts +2 -2
  48. package/dist/{engage → chat}/chat-messages.d.ts +1 -1
  49. package/dist/{engage → chat}/echo-config.d.ts +1 -1
  50. package/dist/chat/index.d.ts +10 -6
  51. package/dist/chat/index.js +14 -8
  52. package/dist/{engage → chat}/launcher-placement.d.ts +1 -7
  53. package/dist/{engage → chat}/socket-loader.d.ts +2 -2
  54. package/dist/chat/types.d.ts +136 -0
  55. package/dist/chunk-27FISYK4.js +4 -0
  56. package/dist/{chunk-QPEEKFCA.js → chunk-4NTBQNHA.js} +249 -80
  57. package/dist/{chunk-RK7GF7PQ.js → chunk-5V2V5C3Y.js} +2 -4
  58. package/dist/{chunk-ICFRH2ED.js → chunk-5YCEMV2L.js} +1 -1
  59. package/dist/{chunk-V4HUSHZO.js → chunk-64DKGVEI.js} +41 -7
  60. package/dist/{chunk-GLHQ3LRO.js → chunk-6U3VLV2C.js} +1 -1
  61. package/dist/{chunk-ZETJTCMV.js → chunk-BC4Y3S3D.js} +1 -1
  62. package/dist/{chunk-JSZN6LDZ.js → chunk-BF7TZYC3.js} +38 -31
  63. package/dist/{chunk-EFPS56JA.js → chunk-BKY2ZNM7.js} +1 -1
  64. package/dist/{chunk-AQSNPZY4.js → chunk-C2FZBUSS.js} +80 -1
  65. package/dist/{chunk-H42PZKVC.js → chunk-CF5QNFYU.js} +2 -2
  66. package/dist/{chunk-JC7KXNNM.js → chunk-D5DCBD4Y.js} +2 -2
  67. package/dist/{chunk-PXZ7LAOY.js → chunk-D6ZC7XKZ.js} +2 -2
  68. package/dist/chunk-EJVQJCG5.js +370 -0
  69. package/dist/{chunk-6OL23QYD.js → chunk-F24FNCTK.js} +1 -1
  70. package/dist/{chunk-NEKEH4CK.js → chunk-FR6BYFPC.js} +53 -21
  71. package/dist/{chunk-KOPVJQ2P.js → chunk-GEXT7BTE.js} +1 -1
  72. package/dist/{chunk-ZAWANWUS.js → chunk-GGORNBSC.js} +18 -6
  73. package/dist/{chunk-DUQMSNF2.js → chunk-HGFAEDG3.js} +1 -1
  74. package/dist/chunk-HODO5BX5.js +28 -0
  75. package/dist/chunk-HW7B43E5.js +14 -0
  76. package/dist/{chunk-EQBZ24VH.js → chunk-IAM3D3RL.js} +43 -24
  77. package/dist/{chunk-ODADQM6Z.js → chunk-JJ3DELUL.js} +2 -2
  78. package/dist/{chunk-542MIHNP.js → chunk-JMLK6MH7.js} +1 -1
  79. package/dist/{chunk-7FWA3I6A.js → chunk-JWTQS5F5.js} +1 -1
  80. package/dist/{chunk-OUNRFXDC.js → chunk-K4ZHAURD.js} +1 -1
  81. package/dist/chunk-NV72HZ4W.js +45 -0
  82. package/dist/{chunk-4Y5FWDPM.js → chunk-NZ4RXN3G.js} +55 -7
  83. package/dist/chunk-OWZ7ATLZ.js +187 -0
  84. package/dist/{chunk-TXBAOEKG.js → chunk-PKGN32AU.js} +5 -4
  85. package/dist/chunk-Q6E4OPGG.js +40 -0
  86. package/dist/{chunk-J4D6ZXRW.js → chunk-QL6XNPSD.js} +3 -3
  87. package/dist/{chunk-HAG4YIZY.js → chunk-R46IQIGK.js} +6 -6
  88. package/dist/{chunk-R3VKF43H.js → chunk-RPZ7OQGD.js} +1 -1
  89. package/dist/{chunk-Q3S26EQA.js → chunk-S54ECQGY.js} +1 -1
  90. package/dist/{chunk-JIL5VY2R.js → chunk-VHKEF5ZB.js} +1 -1
  91. package/dist/{chunk-ILAW3XKD.js → chunk-VO3JYRT5.js} +43 -29
  92. package/dist/chunk-VWWITFWM.js +300 -0
  93. package/dist/{chunk-OPRBF7VE.js → chunk-ZTLMPUO6.js} +3 -2
  94. package/dist/client/index.js +3 -3
  95. package/dist/cms/CmsPreview.d.ts +2 -2
  96. package/dist/cms/index.d.ts +3 -3
  97. package/dist/cms/server-api.d.ts +1 -1
  98. package/dist/cms/types.d.ts +1 -1
  99. package/dist/commerce/EventCheckout.d.ts +1 -1
  100. package/dist/commerce/EventsAgenda.d.ts +3 -3
  101. package/dist/commerce/api.d.ts +1 -1
  102. package/dist/commerce/index.js +4 -4
  103. package/dist/commerce/server.d.ts +3 -4
  104. package/dist/contracts/color.d.ts +1 -1
  105. package/dist/contracts/entries.d.ts +1 -1
  106. package/dist/contracts/error-page.d.ts +183 -0
  107. package/dist/contracts/fleet.d.ts +9 -9
  108. package/dist/contracts/forms.d.ts +4 -10
  109. package/dist/contracts/llms.d.ts +2 -1
  110. package/dist/contracts/portfolio.d.ts +11 -15
  111. package/dist/contracts/proposal-sitemap.d.ts +130 -0
  112. package/dist/contracts/schema-placeholders.d.ts +87 -0
  113. package/dist/contracts/seo-meta.d.ts +11 -12
  114. package/dist/contracts/seo-pages.d.ts +12 -18
  115. package/dist/contracts/site-cache.d.ts +4 -4
  116. package/dist/contracts/sites-normalize.d.ts +2 -4
  117. package/dist/contracts/sites.d.ts +3 -5
  118. package/dist/contracts/slot-content.d.ts +2 -2
  119. package/dist/contracts/slots.d.ts +6 -6
  120. package/dist/contracts/voice.d.ts +56 -0
  121. package/dist/contracts/website.d.ts +2 -2
  122. package/dist/engage/EngageWidget.d.ts +13 -12
  123. package/dist/engage/index.d.ts +10 -9
  124. package/dist/engage/index.js +31 -35
  125. package/dist/engage/types.d.ts +19 -244
  126. package/dist/fleet/FleetHeartbeat.d.ts +2 -2
  127. package/dist/fleet/index.js +4 -4
  128. package/dist/forms/FormEnhancer.d.ts +25 -12
  129. package/dist/forms/FormSpotlight.d.ts +2 -2
  130. package/dist/forms/ManagedForm.d.ts +1 -1
  131. package/dist/forms/ServerForm.d.ts +11 -10
  132. package/dist/forms/StaticForm.d.ts +4 -2
  133. package/dist/forms/field-autocomplete.d.ts +28 -0
  134. package/dist/forms/field-interactions.d.ts +2 -2
  135. package/dist/forms/form-dom-values.d.ts +42 -0
  136. package/dist/forms/form-loaded-at.d.ts +2 -2
  137. package/dist/forms/formsApi.d.ts +2 -2
  138. package/dist/forms/index.d.ts +1 -1
  139. package/dist/forms/index.js +11 -9
  140. package/dist/forms/server.d.ts +1 -1
  141. package/dist/forms/server.js +6 -4
  142. package/dist/forms/static.js +3 -2
  143. package/dist/forms/submitForm.d.ts +9 -2
  144. package/dist/forms/types.d.ts +44 -5
  145. package/dist/forms/useForm.d.ts +21 -4
  146. package/dist/forms/webmcp.d.ts +38 -0
  147. package/dist/images/ManagedFavicon.d.ts +1 -1
  148. package/dist/images/ManagedImage.d.ts +4 -4
  149. package/dist/images/api.d.ts +1 -1
  150. package/dist/images/index.d.ts +2 -2
  151. package/dist/images/index.js +4 -4
  152. package/dist/index.d.ts +4 -1
  153. package/dist/index.js +1 -1
  154. package/dist/landing/contract.d.ts +3 -4
  155. package/dist/layout/SiteKitClientProviders.d.ts +4 -4
  156. package/dist/layout/SiteKitLayout.d.ts +11 -11
  157. package/dist/layout/client.d.ts +4 -4
  158. package/dist/layout/client.js +7 -7
  159. package/dist/layout/index.js +8 -8
  160. package/dist/layout/types.d.ts +7 -6
  161. package/dist/llms/SpeakableSchema.d.ts +2 -2
  162. package/dist/llms/agent-access.d.ts +2 -2
  163. package/dist/llms/aiRobots.d.ts +4 -4
  164. package/dist/llms/api.d.ts +2 -2
  165. package/dist/llms/contract.js +1 -1
  166. package/dist/llms/generateLLMsTxt.d.ts +1 -1
  167. package/dist/llms/index.js +6 -6
  168. package/dist/llms/links.d.ts +1 -1
  169. package/dist/llms/revalidate.d.ts +1 -1
  170. package/dist/llms/seo-revalidate.d.ts +2 -3
  171. package/dist/llms/types.d.ts +6 -6
  172. package/dist/llms/writeLLMsTxt.d.ts +10 -10
  173. package/dist/manifest/index.d.ts +8 -8
  174. package/dist/maps/index.js +3 -3
  175. package/dist/mcp/WebMcpTools.d.ts +1 -1
  176. package/dist/mcp/index.js +2 -14
  177. package/dist/mcp/report.d.ts +2 -2
  178. package/dist/mcp/serverCard.d.ts +1 -1
  179. package/dist/mcp/sonor.d.ts +5 -6
  180. package/dist/mcp/sonor.js +14 -11
  181. package/dist/mcp/transport.d.ts +1 -2
  182. package/dist/mcp/types.d.ts +4 -5
  183. package/dist/motion/engine.d.ts +2 -3
  184. package/dist/motion/failsafe.d.ts +1 -2
  185. package/dist/motion/gsap.d.ts +2 -2
  186. package/dist/motion/three.d.ts +1 -1
  187. package/dist/og/config.d.ts +4 -4
  188. package/dist/og/contrast.d.ts +2 -2
  189. package/dist/og/pages.d.ts +5 -5
  190. package/dist/og/route-fit.d.ts +2 -2
  191. package/dist/og/route.d.ts +1 -2
  192. package/dist/og/template.d.ts +1 -1
  193. package/dist/proxy/index.d.ts +2 -2
  194. package/dist/proxy/securityHeaders.d.ts +10 -12
  195. package/dist/redirects/index.d.ts +7 -7
  196. package/dist/reputation/TestimonialSection.d.ts +1 -1
  197. package/dist/reputation/api.d.ts +1 -1
  198. package/dist/revalidate/index.js +2 -2
  199. package/dist/robots/index.d.ts +5 -5
  200. package/dist/robots/indexnow.d.ts +2 -3
  201. package/dist/seo/ManagedScripts.d.ts +2 -2
  202. package/dist/seo/client.js +4 -4
  203. package/dist/seo/getManagedMetadata.d.ts +1 -1
  204. package/dist/seo/groundingSchema.d.ts +2 -2
  205. package/dist/seo/index.js +15 -8
  206. package/dist/seo/llms/contract.js +1 -1
  207. package/dist/seo/llms.js +6 -6
  208. package/dist/seo/register-sitemap-cli-impl.d.ts +1 -1
  209. package/dist/seo/register-sitemap-cli.d.ts +1 -1
  210. package/dist/seo/register-sitemap-cli.js +2 -2
  211. package/dist/seo/schema-filter.d.ts +5 -6
  212. package/dist/seo/server-api.d.ts +1 -1
  213. package/dist/seo/sitemap.js +4 -4
  214. package/dist/seo/types.d.ts +4 -4
  215. package/dist/seo/withManagedMetadata.d.ts +4 -4
  216. package/dist/server/index.js +2 -2
  217. package/dist/server/mint-site-token.d.ts +1 -2
  218. package/dist/server/server-fetch.d.ts +5 -5
  219. package/dist/shared/build-entries.d.ts +4 -4
  220. package/dist/shared/dialog.d.ts +27 -0
  221. package/dist/shared/frame.d.ts +8 -9
  222. package/dist/shared/fresh-fetch.d.ts +1 -2
  223. package/dist/shared/identity.d.ts +1 -1
  224. package/dist/shared/layers.d.ts +7 -0
  225. package/dist/shared/mid-form.d.ts +53 -0
  226. package/dist/shared/next-files.d.ts +1 -1
  227. package/dist/shared/publishCredential.d.ts +1 -1
  228. package/dist/shared/reporting-gate.d.ts +1 -1
  229. package/dist/shared/uuid.d.ts +1 -2
  230. package/dist/shared/version.d.ts +1 -1
  231. package/dist/shared/visual-viewport-gap.d.ts +3 -3
  232. package/dist/signal/index.js +2 -2
  233. package/dist/signal/types.d.ts +1 -1
  234. package/dist/site-config/index.d.ts +2 -2
  235. package/dist/sitemap/index.d.ts +13 -13
  236. package/dist/sitemap/index.js +4 -4
  237. package/dist/sitemap/shared.d.ts +3 -3
  238. package/dist/sites/site-param.d.ts +2 -2
  239. package/dist/slots/ManagedRichText.d.ts +1 -1
  240. package/dist/slots/ManagedSlot.d.ts +1 -1
  241. package/dist/slots/index.d.ts +1 -3
  242. package/dist/{socket-loader-R24ZSRSQ.js → socket-loader-CGIPEG74.js} +1 -1
  243. package/dist/sync/index.js +5 -5
  244. package/dist/types.d.ts +4 -2
  245. package/dist/website/BlocksPopup.d.ts +1 -1
  246. package/dist/website/PopupBlocks.d.ts +8 -6
  247. package/dist/website/SitePopups.d.ts +25 -0
  248. package/dist/website/images.js +4 -4
  249. package/dist/website/index.js +6 -6
  250. package/dist/website/popup-rules.d.ts +46 -0
  251. package/dist/website/popup-types.d.ts +113 -0
  252. package/dist/website/popups.d.ts +2 -6
  253. package/dist/website/popups.js +6 -14
  254. package/dist/{writeLLMsTxt-QR23OQUE.js → writeLLMsTxt-OV24LQVL.js} +3 -3
  255. package/docs/MIGRATING-TO-7.md +50 -22
  256. package/docs.json +2 -1
  257. package/package.json +2 -2
  258. package/src/admin-auth/README.md +3 -3
  259. package/src/analytics/README.md +9 -11
  260. package/src/articles/README.md +10 -7
  261. package/src/{engage → chat}/README.md +63 -62
  262. package/src/cta-bar/README.md +11 -11
  263. package/src/forms/README.md +60 -12
  264. package/src/layout/README.md +8 -5
  265. package/src/llms/README.md +3 -3
  266. package/src/mcp/README.md +7 -7
  267. package/src/motion/README.md +1 -1
  268. package/src/og/README.md +26 -27
  269. package/src/proxy/README.md +14 -14
  270. package/src/seo/README.md +4 -2
  271. package/src/slots/README.md +1 -1
  272. package/src/sync/README.md +10 -0
  273. package/src/website/README.md +133 -0
  274. package/dist/ChatWidget-JMFBXJ75.js +0 -15
  275. package/dist/EngageWidget-A3QKZ3SU.js +0 -11
  276. package/dist/ManagedForm-2GSSDJPP.js +0 -14
  277. package/dist/SitemapSync-4U654SEA.js +0 -8
  278. package/dist/chunk-B73QPZPH.js +0 -837
  279. package/dist/chunk-NDF4A5JM.js +0 -37
  280. package/dist/chunk-RTMMHTEQ.js +0 -100
  281. package/dist/engage/DesignRenderer.d.ts +0 -57
  282. package/dist/engage/element-rules.d.ts +0 -38
@@ -1,6 +1,6 @@
1
+ import { declarativeToolField } from './chunk-HW7B43E5.js';
1
2
  import { jsx, jsxs } from 'react/jsx-runtime';
2
3
 
3
- // src/forms/delight.tsx
4
4
  var STYLE_ID = "sk-delight-css";
5
5
  var CSS = `
6
6
  /* Every frame must carry the centering translateY: the animation fills both
@@ -208,6 +208,101 @@ function FormMomentum({ done, total }) {
208
208
  }
209
209
  );
210
210
  }
211
+
212
+ // src/forms/field-autocomplete.ts
213
+ var TOKEN_BY_KEY = {
214
+ email: "email",
215
+ email_address: "email",
216
+ e_mail: "email",
217
+ first_name: "given-name",
218
+ firstname: "given-name",
219
+ fname: "given-name",
220
+ given_name: "given-name",
221
+ last_name: "family-name",
222
+ lastname: "family-name",
223
+ lname: "family-name",
224
+ surname: "family-name",
225
+ family_name: "family-name",
226
+ name: "name",
227
+ full_name: "name",
228
+ fullname: "name",
229
+ your_name: "name",
230
+ contact_name: "name",
231
+ phone: "tel",
232
+ phone_number: "tel",
233
+ telephone: "tel",
234
+ mobile: "tel",
235
+ mobile_phone: "tel",
236
+ cell: "tel",
237
+ cell_phone: "tel",
238
+ tel: "tel",
239
+ company: "organization",
240
+ company_name: "organization",
241
+ organization: "organization",
242
+ organisation: "organization",
243
+ business_name: "organization",
244
+ job_title: "organization-title",
245
+ website: "url",
246
+ website_url: "url",
247
+ company_website: "url",
248
+ url: "url",
249
+ street_address: "street-address",
250
+ address_line1: "address-line1",
251
+ address_line_1: "address-line1",
252
+ address1: "address-line1",
253
+ address_line2: "address-line2",
254
+ address_line_2: "address-line2",
255
+ address2: "address-line2",
256
+ city: "address-level2",
257
+ state: "address-level1",
258
+ province: "address-level1",
259
+ zip: "postal-code",
260
+ zip_code: "postal-code",
261
+ zipcode: "postal-code",
262
+ postal_code: "postal-code",
263
+ postcode: "postal-code",
264
+ country: "country-name"
265
+ };
266
+ var TOKEN_FIELD_TYPES = /* @__PURE__ */ new Set(["text", "email", "phone", "tel", "select"]);
267
+ function toKey(raw) {
268
+ return raw.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replace(/\*/g, "").trim().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
269
+ }
270
+ function withoutGeneratedSuffix(key) {
271
+ const match = /^(.+)_([a-z0-9]{6,10})$/.exec(key);
272
+ if (match && /\d/.test(match[2])) return match[1];
273
+ return key;
274
+ }
275
+ function tokenForKey(raw) {
276
+ if (!raw) return void 0;
277
+ const key = toKey(raw);
278
+ return TOKEN_BY_KEY[key] ?? TOKEN_BY_KEY[withoutGeneratedSuffix(key)];
279
+ }
280
+ function autocompleteFor(field) {
281
+ if (!TOKEN_FIELD_TYPES.has(field.field_type)) return void 0;
282
+ const destination = field.destination_field;
283
+ if (destination && !destination.startsWith("custom.")) {
284
+ const fromDestination = tokenForKey(destination);
285
+ if (fromDestination) return fromDestination;
286
+ }
287
+ if (field.field_type === "email") return "email";
288
+ if (field.field_type === "phone" || field.field_type === "tel") return "tel";
289
+ return tokenForKey(field.slug) ?? tokenForKey(field.label);
290
+ }
291
+ var SINGLE_CONTROL_TYPES = /* @__PURE__ */ new Set([
292
+ "text",
293
+ "email",
294
+ "phone",
295
+ "tel",
296
+ "number",
297
+ "textarea",
298
+ "select",
299
+ "multi-select",
300
+ "date",
301
+ "time",
302
+ "datetime",
303
+ "file",
304
+ "slider"
305
+ ]);
211
306
  function normalizeOptions(options) {
212
307
  if (!options) return [];
213
308
  if (Array.isArray(options)) {
@@ -243,8 +338,8 @@ function FormField({ field, value, error, onChange, onFocus, onBlur, classPrefix
243
338
  // cascade from the section produced cream-on-white on brand-colored panels
244
339
  // (found live). Default pairs with the white bg; theme both via vars.
245
340
  //
246
- // The fallback chain matters, and it is the reason this was broken on
247
- // upforgelabs.com: a dark site that set --sk-input-bg and --sk-text-primary
341
+ // The fallback chain matters, and it is why this once broke on a dark
342
+ // site: one that set --sk-input-bg and --sk-text-primary
248
343
  // but never heard of --sk-input-text landed on #111827 over its own dark
249
344
  // field, 1.03:1, an input you cannot read your own typing in. Falling
250
345
  // through --sk-text-primary first means theming the page's text is enough.
@@ -279,6 +374,17 @@ function FormField({ field, value, error, onChange, onFocus, onBlur, classPrefix
279
374
  }
280
375
  const hideLabel = !!field.hide_label;
281
376
  const effectivePlaceholder = hideLabel ? field.label : field.placeholder;
377
+ const labelId = `${inputId}-label`;
378
+ const errorId = `${inputId}-error`;
379
+ const helpId = `${inputId}-help`;
380
+ const showsHelp = !!field.help_text && !error && field.field_type !== "checkbox";
381
+ const describedBy = [error ? errorId : null, showsHelp ? helpId : null].filter(Boolean).join(" ") || void 0;
382
+ const a11y = {
383
+ "aria-invalid": error ? true : void 0,
384
+ "aria-describedby": describedBy
385
+ };
386
+ const autoComplete = autocompleteFor(field);
387
+ const toolParam = field.help_text ? declarativeToolField(`${field.label}. ${field.help_text}`) : {};
282
388
  const focusProps = interactive && (onFocus || onBlur) ? {
283
389
  onFocus: (e) => {
284
390
  if (!e.currentTarget.contains(e.relatedTarget)) onFocus?.();
@@ -288,10 +394,19 @@ function FormField({ field, value, error, onChange, onFocus, onBlur, classPrefix
288
394
  }
289
395
  } : {};
290
396
  return /* @__PURE__ */ jsxs("div", { className: `${classPrefix}__field ${classPrefix}__field--${field.field_type}`, ...focusProps, children: [
291
- /* @__PURE__ */ jsxs("label", { htmlFor: inputId, className: `${classPrefix}__label`, style: { ...labelStyle, ...hideLabel ? { position: "absolute", width: 1, height: 1, padding: 0, margin: -1, overflow: "hidden", clip: "rect(0,0,0,0)", whiteSpace: "nowrap", border: 0 } : {} }, children: [
292
- field.label,
293
- field.is_required && /* @__PURE__ */ jsx("span", { style: { color: "var(--sk-error, #dc2626)" }, children: " *" })
294
- ] }),
397
+ /* @__PURE__ */ jsxs(
398
+ "label",
399
+ {
400
+ id: labelId,
401
+ htmlFor: SINGLE_CONTROL_TYPES.has(field.field_type) ? inputId : void 0,
402
+ className: `${classPrefix}__label`,
403
+ style: { ...labelStyle, ...hideLabel ? { position: "absolute", width: 1, height: 1, padding: 0, margin: -1, overflow: "hidden", clip: "rect(0,0,0,0)", whiteSpace: "nowrap", border: 0 } : {} },
404
+ children: [
405
+ field.label,
406
+ field.is_required && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: { color: "var(--sk-error, #dc2626)" }, children: " *" })
407
+ ]
408
+ }
409
+ ),
295
410
  ["text", "email", "phone", "tel", "number"].includes(field.field_type) && /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, "data-sk-part": "field-input-wrap", "data-sk-valid": showValid ? "true" : void 0, children: [
296
411
  /* @__PURE__ */ jsx(
297
412
  "input",
@@ -303,6 +418,9 @@ function FormField({ field, value, error, onChange, onFocus, onBlur, classPrefix
303
418
  ...bind(String(value || "")),
304
419
  placeholder: effectivePlaceholder,
305
420
  required: field.is_required,
421
+ autoComplete,
422
+ ...a11y,
423
+ ...toolParam,
306
424
  style: baseInputStyle
307
425
  }
308
426
  ),
@@ -317,6 +435,8 @@ function FormField({ field, value, error, onChange, onFocus, onBlur, classPrefix
317
435
  ...bind(String(value || "")),
318
436
  placeholder: effectivePlaceholder,
319
437
  required: field.is_required,
438
+ ...a11y,
439
+ ...toolParam,
320
440
  rows: 4,
321
441
  style: { ...baseInputStyle, resize: "vertical" }
322
442
  }
@@ -329,6 +449,9 @@ function FormField({ field, value, error, onChange, onFocus, onBlur, classPrefix
329
449
  name: field.slug,
330
450
  ...bind(String(value || "")),
331
451
  required: field.is_required,
452
+ autoComplete,
453
+ ...a11y,
454
+ ...toolParam,
332
455
  style: baseInputStyle,
333
456
  children: [
334
457
  /* @__PURE__ */ jsx("option", { value: "", children: effectivePlaceholder || "Select an option" }),
@@ -347,24 +470,38 @@ function FormField({ field, value, error, onChange, onFocus, onBlur, classPrefix
347
470
  onChange: (e) => emit(Array.from(e.target.selectedOptions, (opt) => opt.value))
348
471
  } : { defaultValue: Array.isArray(value) ? value : [] },
349
472
  required: field.is_required,
473
+ ...a11y,
474
+ ...toolParam,
350
475
  multiple: true,
351
476
  style: { ...baseInputStyle, height: 120 },
352
477
  children: options.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
353
478
  }
354
479
  ),
355
- field.field_type === "radio" && /* @__PURE__ */ jsx("div", { className: `${classPrefix}__radio-group`, style: { display: "flex", flexDirection: "column", gap: 8 }, children: options.map((option) => /* @__PURE__ */ jsxs("label", { className: `${classPrefix}__radio-option`, style: { display: "flex", alignItems: "center", gap: 8 }, children: [
356
- /* @__PURE__ */ jsx(
357
- "input",
358
- {
359
- type: "radio",
360
- name: field.slug,
361
- value: option.value,
362
- ...interactive ? { checked: value === option.value, onChange: () => emit(option.value) } : { defaultChecked: value === option.value },
363
- required: field.is_required
364
- }
365
- ),
366
- option.label
367
- ] }, option.value)) }),
480
+ field.field_type === "radio" && /* @__PURE__ */ jsx(
481
+ "div",
482
+ {
483
+ className: `${classPrefix}__radio-group`,
484
+ role: "radiogroup",
485
+ "aria-labelledby": labelId,
486
+ "aria-required": field.is_required || void 0,
487
+ ...a11y,
488
+ style: { display: "flex", flexDirection: "column", gap: 8 },
489
+ children: options.map((option) => /* @__PURE__ */ jsxs("label", { className: `${classPrefix}__radio-option`, style: { display: "flex", alignItems: "center", gap: 8 }, children: [
490
+ /* @__PURE__ */ jsx(
491
+ "input",
492
+ {
493
+ type: "radio",
494
+ name: field.slug,
495
+ value: option.value,
496
+ ...interactive ? { checked: value === option.value, onChange: () => emit(option.value) } : { defaultChecked: value === option.value },
497
+ required: field.is_required,
498
+ ...toolParam
499
+ }
500
+ ),
501
+ option.label
502
+ ] }, option.value))
503
+ }
504
+ ),
368
505
  field.field_type === "checkbox" && options.length === 0 && /* @__PURE__ */ jsxs("label", { className: `${classPrefix}__checkbox`, style: { display: "flex", alignItems: "center", gap: 8 }, children: [
369
506
  /* @__PURE__ */ jsx(
370
507
  "input",
@@ -372,32 +509,43 @@ function FormField({ field, value, error, onChange, onFocus, onBlur, classPrefix
372
509
  type: "checkbox",
373
510
  name: field.slug,
374
511
  ...interactive ? { checked: !!value, onChange: (e) => emit(e.target.checked) } : { defaultChecked: !!value },
375
- required: field.is_required
512
+ required: field.is_required,
513
+ ...a11y
376
514
  }
377
515
  ),
378
516
  field.help_text || field.label
379
517
  ] }),
380
- field.field_type === "checkbox" && options.length > 0 && /* @__PURE__ */ jsx("div", { className: `${classPrefix}__checkbox-group`, style: { display: "flex", flexDirection: "column", gap: 8 }, children: options.map((option) => {
381
- const selectedValues = Array.isArray(value) ? value : [];
382
- const isChecked = selectedValues.includes(option.value);
383
- return /* @__PURE__ */ jsxs("label", { className: `${classPrefix}__checkbox-option`, style: { display: "flex", alignItems: "center", gap: 8 }, children: [
384
- /* @__PURE__ */ jsx(
385
- "input",
386
- {
387
- type: "checkbox",
388
- name: field.slug,
389
- value: option.value,
390
- ...interactive ? {
391
- checked: isChecked,
392
- onChange: () => emit(
393
- isChecked ? selectedValues.filter((v) => v !== option.value) : [...selectedValues, option.value]
394
- )
395
- } : { defaultChecked: isChecked }
396
- }
397
- ),
398
- option.label
399
- ] }, option.value);
400
- }) }),
518
+ field.field_type === "checkbox" && options.length > 0 && /* @__PURE__ */ jsx(
519
+ "div",
520
+ {
521
+ className: `${classPrefix}__checkbox-group`,
522
+ role: "group",
523
+ "aria-labelledby": labelId,
524
+ "aria-describedby": describedBy,
525
+ style: { display: "flex", flexDirection: "column", gap: 8 },
526
+ children: options.map((option) => {
527
+ const selectedValues = Array.isArray(value) ? value : [];
528
+ const isChecked = selectedValues.includes(option.value);
529
+ return /* @__PURE__ */ jsxs("label", { className: `${classPrefix}__checkbox-option`, style: { display: "flex", alignItems: "center", gap: 8 }, children: [
530
+ /* @__PURE__ */ jsx(
531
+ "input",
532
+ {
533
+ type: "checkbox",
534
+ name: field.slug,
535
+ value: option.value,
536
+ ...interactive ? {
537
+ checked: isChecked,
538
+ onChange: () => emit(
539
+ isChecked ? selectedValues.filter((v) => v !== option.value) : [...selectedValues, option.value]
540
+ )
541
+ } : { defaultChecked: isChecked }
542
+ }
543
+ ),
544
+ option.label
545
+ ] }, option.value);
546
+ })
547
+ }
548
+ ),
401
549
  field.field_type === "date" && (interactive && controls?.Date ? /* @__PURE__ */ jsx(
402
550
  controls.Date,
403
551
  {
@@ -421,6 +569,8 @@ function FormField({ field, value, error, onChange, onFocus, onBlur, classPrefix
421
569
  name: field.slug,
422
570
  defaultValue: String(value || "") || void 0,
423
571
  required: field.is_required,
572
+ ...a11y,
573
+ ...toolParam,
424
574
  style: baseInputStyle
425
575
  }
426
576
  )
@@ -434,6 +584,8 @@ function FormField({ field, value, error, onChange, onFocus, onBlur, classPrefix
434
584
  name: field.slug,
435
585
  ...bind(String(value || "")),
436
586
  required: field.is_required,
587
+ ...a11y,
588
+ ...toolParam,
437
589
  style: baseInputStyle
438
590
  }
439
591
  ),
@@ -446,6 +598,8 @@ function FormField({ field, value, error, onChange, onFocus, onBlur, classPrefix
446
598
  name: field.slug,
447
599
  ...bind(String(value || "")),
448
600
  required: field.is_required,
601
+ ...a11y,
602
+ ...toolParam,
449
603
  style: baseInputStyle
450
604
  }
451
605
  ),
@@ -469,47 +623,60 @@ function FormField({ field, value, error, onChange, onFocus, onBlur, classPrefix
469
623
  type: "file",
470
624
  name: field.slug,
471
625
  required: field.is_required,
626
+ ...a11y,
472
627
  style: baseInputStyle
473
628
  }
474
629
  )
475
630
  )),
476
- field.field_type === "rating" && /* @__PURE__ */ jsxs("div", { className: `${classPrefix}__rating`, style: { display: "flex", gap: 4 }, role: interactive ? void 0 : "radiogroup", "aria-label": interactive ? void 0 : field.label, children: [
477
- !interactive && [1, 2, 3, 4, 5].map((star) => (
478
- // Static: a real radio group. Submits natively, and is reachable
479
- // by keyboard and screen readers without any script.
480
- /* @__PURE__ */ jsxs("label", { className: `${classPrefix}__rating-star`, style: { fontSize: 24, cursor: "pointer", color: value >= star ? "var(--sk-rating-active, #fbbf24)" : "var(--sk-rating-inactive, #d1d5db)" }, children: [
481
- /* @__PURE__ */ jsx(
482
- "input",
631
+ field.field_type === "rating" && /* @__PURE__ */ jsxs(
632
+ "div",
633
+ {
634
+ className: `${classPrefix}__rating`,
635
+ style: { display: "flex", gap: 4 },
636
+ role: interactive ? "group" : "radiogroup",
637
+ "aria-labelledby": labelId,
638
+ "aria-describedby": describedBy,
639
+ children: [
640
+ !interactive && [1, 2, 3, 4, 5].map((star) => (
641
+ // Static: a real radio group. Submits natively, and is reachable
642
+ // by keyboard and screen readers without any script.
643
+ /* @__PURE__ */ jsxs("label", { className: `${classPrefix}__rating-star`, style: { fontSize: 24, cursor: "pointer", color: value >= star ? "var(--sk-rating-active, #fbbf24)" : "var(--sk-rating-inactive, #d1d5db)" }, children: [
644
+ /* @__PURE__ */ jsx(
645
+ "input",
646
+ {
647
+ type: "radio",
648
+ name: field.slug,
649
+ value: star,
650
+ defaultChecked: value === star,
651
+ required: field.is_required && star === 1,
652
+ style: { position: "absolute", width: 1, height: 1, opacity: 0 }
653
+ }
654
+ ),
655
+ /* @__PURE__ */ jsx("span", { "aria-label": `${star} star${star > 1 ? "s" : ""}`, children: "\u2605" })
656
+ ] }, `s${star}`)
657
+ )),
658
+ interactive && [1, 2, 3, 4, 5].map((star) => /* @__PURE__ */ jsx(
659
+ "button",
483
660
  {
484
- type: "radio",
485
- name: field.slug,
486
- value: star,
487
- defaultChecked: value === star,
488
- required: field.is_required && star === 1,
489
- style: { position: "absolute", width: 1, height: 1, opacity: 0 }
490
- }
491
- ),
492
- /* @__PURE__ */ jsx("span", { "aria-label": `${star} star${star > 1 ? "s" : ""}`, children: "\u2605" })
493
- ] }, `s${star}`)
494
- )),
495
- interactive && [1, 2, 3, 4, 5].map((star) => /* @__PURE__ */ jsx(
496
- "button",
497
- {
498
- type: "button",
499
- onClick: () => emit(star),
500
- className: `${classPrefix}__rating-star`,
501
- style: {
502
- background: "none",
503
- border: "none",
504
- fontSize: 24,
505
- cursor: "pointer",
506
- color: value >= star ? "var(--sk-rating-active, #fbbf24)" : "var(--sk-rating-inactive, #d1d5db)"
507
- },
508
- children: "\u2605"
509
- },
510
- star
511
- ))
512
- ] }),
661
+ type: "button",
662
+ onClick: () => emit(star),
663
+ "aria-label": `${star} star${star > 1 ? "s" : ""}`,
664
+ "aria-pressed": value === star,
665
+ className: `${classPrefix}__rating-star`,
666
+ style: {
667
+ background: "none",
668
+ border: "none",
669
+ fontSize: 24,
670
+ cursor: "pointer",
671
+ color: value >= star ? "var(--sk-rating-active, #fbbf24)" : "var(--sk-rating-inactive, #d1d5db)"
672
+ },
673
+ children: "\u2605"
674
+ },
675
+ star
676
+ ))
677
+ ]
678
+ }
679
+ ),
513
680
  field.field_type === "slider" && /* @__PURE__ */ jsxs("div", { className: `${classPrefix}__slider`, children: [
514
681
  /* @__PURE__ */ jsx(
515
682
  "input",
@@ -520,13 +687,15 @@ function FormField({ field, value, error, onChange, onFocus, onBlur, classPrefix
520
687
  ...interactive ? { value: Number(value || 50), onChange: (e) => emit(Number(e.target.value)) } : { defaultValue: Number(value || 50) },
521
688
  min: field.validation?.min || 0,
522
689
  max: field.validation?.max || 100,
690
+ ...a11y,
691
+ ...toolParam,
523
692
  style: { width: "100%" }
524
693
  }
525
694
  ),
526
695
  /* @__PURE__ */ jsx("div", { className: `${classPrefix}__slider-value`, style: { textAlign: "center", marginTop: 4 }, children: String(value || 50) })
527
696
  ] }),
528
- error && /* @__PURE__ */ jsx("p", { className: `${classPrefix}__error`, style: errorStyle, children: error }),
529
- field.help_text && !error && field.field_type !== "checkbox" && /* @__PURE__ */ jsx("p", { className: `${classPrefix}__help`, style: helpStyle, children: field.help_text })
697
+ error && /* @__PURE__ */ jsx("p", { id: errorId, role: "alert", className: `${classPrefix}__error`, style: errorStyle, children: error }),
698
+ showsHelp && /* @__PURE__ */ jsx("p", { id: helpId, className: `${classPrefix}__help`, style: helpStyle, children: field.help_text })
530
699
  ] });
531
700
  }
532
701
 
@@ -1,5 +1,5 @@
1
- import { FormEnhancer } from './chunk-RTMMHTEQ.js';
2
- import { StaticForm } from './chunk-OPRBF7VE.js';
1
+ import { FormEnhancer } from './chunk-OWZ7ATLZ.js';
2
+ import { StaticForm } from './chunk-ZTLMPUO6.js';
3
3
  import { serverApiFetch } from './chunk-X6F6SO4S.js';
4
4
  import { jsx } from 'react/jsx-runtime';
5
5
 
@@ -8,7 +8,6 @@ async function ServerForm({
8
8
  className,
9
9
  experience,
10
10
  enter = "fade",
11
- enhance = true,
12
11
  defer = true,
13
12
  initialConfig,
14
13
  configOptions,
@@ -16,7 +15,6 @@ async function ServerForm({
16
15
  }) {
17
16
  const config = initialConfig ?? await getFormConfig(formId, configOptions);
18
17
  const shell = /* @__PURE__ */ jsx(StaticForm, { config, className });
19
- if (!enhance) return shell;
20
18
  return /* @__PURE__ */ jsx(
21
19
  FormEnhancer,
22
20
  {
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { getSiteDesignTokens, tokensForReport, shouldReportDesign, sendSiteDesign } from './chunk-ODADQM6Z.js';
2
+ import { getSiteDesignTokens, tokensForReport, shouldReportDesign, sendSiteDesign } from './chunk-JJ3DELUL.js';
3
3
  import { designTokensHash } from './chunk-IHG36STL.js';
4
4
  import { useDeferredActivation } from './chunk-YC7ELZS3.js';
5
5
  import { resolveAnalyticsTarget } from './chunk-43OCZ3JA.js';
@@ -1,9 +1,10 @@
1
1
  'use client';
2
+ import { VISUAL_VIEWPORT_GAP_PROPERTY } from './chunk-6T6CQVNL.js';
2
3
  import { GlassStyles } from './chunk-JE4A5S4F.js';
3
- import { chatLauncherStyles } from './chunk-NDF4A5JM.js';
4
- import { useForm } from './chunk-NEKEH4CK.js';
4
+ import { DEFAULT_WIDGET_Z_INDEX } from './chunk-27FISYK4.js';
5
+ import { useForm } from './chunk-FR6BYFPC.js';
5
6
  import { parseColor, relativeLuminance, DARK_LUMINANCE, contrastRatio } from './chunk-S22FSH7C.js';
6
- import { sonorFetch } from './chunk-PXZ7LAOY.js';
7
+ import { sonorFetch } from './chunk-D6ZC7XKZ.js';
7
8
  import { useState, useEffect, useRef, useLayoutEffect, useCallback, Fragment as Fragment$1 } from 'react';
8
9
  import { lexer } from 'marked';
9
10
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -144,7 +145,7 @@ function Markdown({ children, components }) {
144
145
  return /* @__PURE__ */ jsx(Fragment, { children: blocks(lexer(children, { gfm: true }), components) });
145
146
  }
146
147
 
147
- // src/engage/brand-color.ts
148
+ // src/chat/brand-color.ts
148
149
  var TEXT_CONTRAST = 4.5;
149
150
  var RESTING_TINT = 0.082;
150
151
  var UNREADABLE_BRAND_SHARE = 40;
@@ -454,7 +455,40 @@ function EchoUiActions({ actions, primaryColor, primaryRgb, brandText, onSuggest
454
455
  ] });
455
456
  }
456
457
 
457
- // src/engage/chat-messages.ts
458
+ // src/chat/launcher-placement.ts
459
+ var DEFAULT_LAUNCHER_OFFSET_BOTTOM = "20px";
460
+ var LAUNCHER_OFFSET_PROPERTY = "--sk-echo-offset-bottom";
461
+ var SIDE_GUTTER = "20px";
462
+ var POPUP_ABOVE_LAUNCHER = "70px";
463
+ var POPUP_VERTICAL_RESERVE = "100px";
464
+ function toCssLength(value) {
465
+ if (typeof value === "number") return Number.isFinite(value) ? `${value}px` : void 0;
466
+ const trimmed = value?.trim();
467
+ return trimmed ? trimmed : void 0;
468
+ }
469
+ function chatLauncherStyles({ position, offsetBottom, zIndex } = {}) {
470
+ const side = position === "bottom-left" ? { left: SIDE_GUTTER } : { right: SIDE_GUTTER };
471
+ const offset = toCssLength(offsetBottom) ?? DEFAULT_LAUNCHER_OFFSET_BOTTOM;
472
+ const layer = typeof zIndex === "number" && Number.isFinite(zIndex) ? zIndex : DEFAULT_WIDGET_Z_INDEX;
473
+ const clearance = `var(${LAUNCHER_OFFSET_PROPERTY}, ${offset}) + env(safe-area-inset-bottom, 0px) + var(${VISUAL_VIEWPORT_GAP_PROPERTY}, 0px)`;
474
+ return {
475
+ launcher: {
476
+ ...side,
477
+ bottom: `calc(${clearance})`
478
+ },
479
+ popup: {
480
+ ...side,
481
+ bottom: `calc(${clearance} + ${POPUP_ABOVE_LAUNCHER})`,
482
+ maxHeight: `calc(100dvh - (${clearance}) - ${POPUP_VERTICAL_RESERVE})`
483
+ },
484
+ // One beneath the launcher. At 0 or below, the same layer instead: -1
485
+ // would paint the popup behind the page's own content, and at a shared
486
+ // layer the launcher, which renders after the popup, still paints on top.
487
+ zIndex: { launcher: layer, popup: layer > 0 ? layer - 1 : layer }
488
+ };
489
+ }
490
+
491
+ // src/chat/chat-messages.ts
458
492
  function userRequestedHandoff(text) {
459
493
  const lower = text.toLowerCase().trim();
460
494
  const patterns = [
@@ -839,7 +873,7 @@ function EchoChat({ projectId, widgetConfig, config, apiUrl: propApiUrl, signalU
839
873
  socketRef.current.disconnect();
840
874
  socketRef.current = null;
841
875
  }
842
- const { createSocket } = await import('./socket-loader-R24ZSRSQ.js');
876
+ const { createSocket } = await import('./socket-loader-CGIPEG74.js');
843
877
  const namespaceUrl = `${baseUrl.replace(/\/$/, "")}/engage/chat`;
844
878
  const socket = await createSocket(namespaceUrl, {
845
879
  query: { projectId, visitorId, sessionId: currentSessionId },
@@ -1800,4 +1834,4 @@ function EchoChat({ projectId, widgetConfig, config, apiUrl: propApiUrl, signalU
1800
1834
  ] });
1801
1835
  }
1802
1836
 
1803
- export { ChatWidget, EchoChat, isChatEnabled };
1837
+ export { ChatWidget, DEFAULT_LAUNCHER_OFFSET_BOTTOM, EchoChat, chatLauncherStyles, isChatEnabled };
@@ -1,5 +1,5 @@
1
1
  import { freshFetch } from './chunk-VX5CMNMM.js';
2
- import { LLM_GEO_CONTRACT_VERSION } from './chunk-4Y5FWDPM.js';
2
+ import { LLM_GEO_CONTRACT_VERSION } from './chunk-NZ4RXN3G.js';
3
3
  import { withSiteParam } from './chunk-MJP63SSS.js';
4
4
  import { cache } from 'react';
5
5
 
@@ -1,4 +1,4 @@
1
- import { generateLLMsTxt, generateLLMsFullTxt } from './chunk-J4D6ZXRW.js';
1
+ import { generateLLMsTxt, generateLLMsFullTxt } from './chunk-QL6XNPSD.js';
2
2
  import { createHash } from 'crypto';
3
3
  import { existsSync, readFileSync } from 'fs';
4
4
  import { join } from 'path';
@@ -3,12 +3,48 @@ import { normalizeDesignTokens, designTokenCssVars, sanitizePopupHtml, isSafePop
3
3
  import { useId, useRef, useEffect } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
5
5
 
6
+ var DIALOG_FOCUSABLE = 'a[href],button:not([disabled]),input,select,textarea,[tabindex]:not([tabindex="-1"])';
7
+ function usePopupDialog(panelRef, { enabled = true, modal, onClose }) {
8
+ const onCloseRef = useRef(onClose);
9
+ useEffect(() => {
10
+ onCloseRef.current = onClose;
11
+ });
12
+ useEffect(() => {
13
+ if (!enabled) return;
14
+ const panel = panelRef.current;
15
+ const previous = document.activeElement;
16
+ if (modal && panel) panel.focus({ preventScroll: true });
17
+ const onKey = (e) => {
18
+ if (e.key === "Escape") {
19
+ onCloseRef.current?.();
20
+ return;
21
+ }
22
+ if (!modal || e.key !== "Tab" || !panel) return;
23
+ const items = Array.from(panel.querySelectorAll(DIALOG_FOCUSABLE));
24
+ if (!items.length) return;
25
+ const first = items[0];
26
+ const last = items[items.length - 1];
27
+ const active = document.activeElement;
28
+ if (e.shiftKey && (active === first || active === panel)) {
29
+ e.preventDefault();
30
+ last.focus();
31
+ } else if (!e.shiftKey && active === last) {
32
+ e.preventDefault();
33
+ first.focus();
34
+ }
35
+ };
36
+ document.addEventListener("keydown", onKey);
37
+ return () => {
38
+ document.removeEventListener("keydown", onKey);
39
+ if (modal && previous && typeof previous.focus === "function") previous.focus({ preventScroll: true });
40
+ };
41
+ }, [enabled, modal, panelRef]);
42
+ }
6
43
  var DEFAULT_POPUP_TOKENS = normalizeDesignTokens({
7
44
  colors: { background: "#ffffff", text: "#111827", primary: "#111827", muted: "#4b5563", border: "#e5e7eb" },
8
45
  fonts: { body: "inherit", heading: "inherit" },
9
46
  radius: { control: "8px", card: "16px" }
10
47
  });
11
- var FOCUSABLE = 'a[href],button:not([disabled]),input,select,textarea,[tabindex]:not([tabindex="-1"])';
12
48
  var v = (name) => `var(--sk-site-${name})`;
13
49
  function renderBlocks(blocks, headingId, placement, press) {
14
50
  const out = [];
@@ -153,36 +189,7 @@ function PopupBlocks({
153
189
  const hasHeading = blocks.some((b) => b.type === "heading");
154
190
  const t = tokens ?? DEFAULT_POPUP_TOKENS;
155
191
  const vars = designTokenCssVars(t);
156
- useEffect(() => {
157
- if (inline) return;
158
- const panel2 = panelRef.current;
159
- const previous = typeof document !== "undefined" ? document.activeElement : null;
160
- if (modal && panel2) panel2.focus({ preventScroll: true });
161
- const onKey = (e) => {
162
- if (e.key === "Escape") {
163
- onClose?.();
164
- return;
165
- }
166
- if (!modal || e.key !== "Tab" || !panel2) return;
167
- const items = Array.from(panel2.querySelectorAll(FOCUSABLE));
168
- if (!items.length) return;
169
- const first = items[0];
170
- const last = items[items.length - 1];
171
- const active = document.activeElement;
172
- if (e.shiftKey && (active === first || active === panel2)) {
173
- e.preventDefault();
174
- last.focus();
175
- } else if (!e.shiftKey && active === last) {
176
- e.preventDefault();
177
- first.focus();
178
- }
179
- };
180
- document.addEventListener("keydown", onKey);
181
- return () => {
182
- document.removeEventListener("keydown", onKey);
183
- if (modal && previous && typeof previous.focus === "function") previous.focus({ preventScroll: true });
184
- };
185
- }, [inline, modal, onClose]);
192
+ usePopupDialog(panelRef, { enabled: !inline, modal, onClose });
186
193
  const press = (block, index, e) => {
187
194
  if (onButton?.(block, index) === false || !isSafePopupUrl(block.url)) e.preventDefault();
188
195
  };