@birdapi/velinstyle 1.0.0 → 1.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 (321) hide show
  1. package/README.de.md +87 -158
  2. package/README.md +188 -115
  3. package/cli/blueprint.js +98 -5
  4. package/cli/blueprints/app-chrome.html +18 -0
  5. package/cli/blueprints/benefits-grid.html +9 -0
  6. package/cli/blueprints/bottom-nav-mobile.html +2 -1
  7. package/cli/blueprints/cookie-consent.html +1 -1
  8. package/cli/blueprints/cta-band.html +6 -0
  9. package/cli/blueprints/faq-section.html +18 -0
  10. package/cli/blueprints/filter-bar.html +4 -4
  11. package/cli/blueprints/form-contact.html +6 -5
  12. package/cli/blueprints/hero-section.html +3 -3
  13. package/cli/blueprints/notification-center.html +1 -1
  14. package/cli/blueprints/onboarding.html +2 -1
  15. package/cli/blueprints/ops-console.html +29 -0
  16. package/cli/blueprints/pricing-band.html +37 -0
  17. package/cli/blueprints/process-steps.html +10 -0
  18. package/cli/blueprints/services-grid.html +9 -0
  19. package/cli/blueprints/settings-panel.html +8 -6
  20. package/cli/blueprints/split-hero.html +25 -0
  21. package/cli/blueprints/testimonials.html +8 -0
  22. package/cli/blueprints/trust-bar.html +10 -0
  23. package/cli/cli-manifest.json +81 -17
  24. package/cli/contrast-utils.js +97 -0
  25. package/cli/create.js +370 -0
  26. package/cli/docgen/extract-a11y.js +4 -2
  27. package/cli/docgen/extract-attributes.js +126 -10
  28. package/cli/docgen/extract-cli.js +3 -2
  29. package/cli/docgen/extract-components.js +61 -2
  30. package/cli/docgen/extract-utilities.js +10 -10
  31. package/cli/doctor.js +119 -0
  32. package/cli/index.js +535 -46
  33. package/cli/meta.js +84 -1
  34. package/cli/perf-audit.js +15 -2
  35. package/cli/prompt-engine.js +243 -0
  36. package/cli/review.js +268 -0
  37. package/cli/scaffold.js +119 -4
  38. package/cli/scanner-rules-data.js +7 -0
  39. package/cli/scanner.js +357 -4
  40. package/cli/serve.js +81 -0
  41. package/cli/skills.js +223 -0
  42. package/cli/wc-api.js +89 -0
  43. package/cli/workflow.js +42 -0
  44. package/components/index.js +4 -0
  45. package/components/runtime/component-loaders.js +4 -0
  46. package/components/velin-accordion.js +62 -13
  47. package/components/velin-bottom-nav.js +10 -0
  48. package/components/velin-calendar.js +212 -0
  49. package/components/velin-command.js +18 -2
  50. package/components/velin-data-table.js +438 -0
  51. package/components/velin-dialog.js +4 -1
  52. package/components/velin-drawer.js +2 -4
  53. package/components/velin-file-dropzone.js +162 -0
  54. package/components/velin-form-summary.js +348 -0
  55. package/components/velin-icon.js +22 -1
  56. package/components/velin-lightbox.js +5 -3
  57. package/components/velin-modal.js +2 -4
  58. package/components/velin-search.js +1 -1
  59. package/components/velin-sheet.js +2 -4
  60. package/core/a11y/component-contracts.json +35 -1
  61. package/core/highlight/languages/go.js +28 -0
  62. package/core/highlight/languages/python.js +29 -0
  63. package/core/highlight/languages/rust.js +32 -0
  64. package/core/highlight/languages/yaml.js +23 -0
  65. package/core/highlight/registry.js +13 -2
  66. package/core/meta/build.js +126 -6
  67. package/core/meta/design-constraints/contact.json +9 -0
  68. package/core/meta/design-constraints/faq.json +9 -0
  69. package/core/meta/design-constraints/hero.json +13 -0
  70. package/core/meta/knowledge/components.json +622 -0
  71. package/core/meta/knowledge/tokens.json +282 -0
  72. package/core/meta/pages/registry.json +294 -0
  73. package/core/meta/schema.js +3 -1
  74. package/core/meta/sections/registry.json +185 -0
  75. package/dist/chunks/{attributes-TPANLMLF.js → attributes-A423NX5J.js} +3 -3
  76. package/dist/chunks/{attributes-ADOKLKA7.js → attributes-F6UPIEVP.js} +2 -2
  77. package/dist/chunks/{attributes-STYCS3O5.js → attributes-GLYXGGAL.js} +3 -3
  78. package/dist/chunks/{attributes-5Q2WXGRC.js → attributes-UFTYOSAU.js} +20 -3
  79. package/dist/chunks/attributes-XGLXLR2T.js +391 -0
  80. package/dist/chunks/attributes-YF4NI2QS.js +391 -0
  81. package/dist/chunks/browser-AUABBYUK.js +1544 -0
  82. package/dist/chunks/{chunk-SRL6EWHP.js → chunk-2LSKX57P.js} +13 -11
  83. package/dist/chunks/{chunk-RSMRR27R.js → chunk-2TGCVDUL.js} +11 -9
  84. package/dist/chunks/{chunk-XIET2VSZ.js → chunk-5JWGUAPC.js} +17 -13
  85. package/dist/chunks/chunk-7YSO2OQ2.js +113 -0
  86. package/dist/chunks/{chunk-IEHKRARD.js → chunk-BHR3ZG6U.js} +10 -8
  87. package/dist/chunks/{chunk-J472ERN2.js → chunk-ERF5YVP4.js} +15 -4
  88. package/dist/chunks/{chunk-XYLJ2GCB.js → chunk-JMSIHEYX.js} +55 -7
  89. package/dist/chunks/{chunk-MKZP2TBH.js → chunk-L7ZGUU4D.js} +2 -2
  90. package/dist/chunks/chunk-TUGIPKW5.js +113 -0
  91. package/dist/chunks/go-HZ6XTFCK.js +31 -0
  92. package/dist/chunks/{highlight-GJHHV4KM.js → highlight-IBDX4XWS.js} +1 -1
  93. package/dist/chunks/{highlight-XTHRKBA3.js → highlight-M2ELQNYK.js} +1 -1
  94. package/dist/chunks/python-BMPKDKNO.js +32 -0
  95. package/dist/chunks/runtime-entry.js +1 -1
  96. package/dist/chunks/rust-2BWKI2MN.js +35 -0
  97. package/dist/chunks/{velin-accordion-FBIPAEXG.js → velin-accordion-WSY6BMLA.js} +64 -10
  98. package/dist/chunks/{velin-bottom-nav-7GEMPIM6.js → velin-bottom-nav-5RVRNGIP.js} +25 -0
  99. package/dist/chunks/{velin-bottom-nav-2N2TYWOF.js → velin-bottom-nav-N5F4UO2I.js} +26 -1
  100. package/dist/chunks/velin-calendar-OFE6THUD.js +210 -0
  101. package/dist/chunks/velin-calendar-PGVNJTXK.js +217 -0
  102. package/dist/chunks/velin-calendar-XC7JRDSH.js +217 -0
  103. package/dist/chunks/{velin-code-block-CIAURDRJ.js → velin-code-block-ITCLIC6T.js} +1 -1
  104. package/dist/chunks/{velin-code-block-TSUKPY4C.js → velin-code-block-ZGWG7JI5.js} +12 -3
  105. package/dist/chunks/{velin-combobox-NXS4QB4R.js → velin-combobox-56V2ZWT5.js} +1 -1
  106. package/dist/chunks/{velin-command-ONKO2A3J.js → velin-command-2HO2EKR6.js} +29 -2
  107. package/dist/chunks/{velin-command-TMLSRGIU.js → velin-command-ZJSLPK7B.js} +30 -3
  108. package/dist/chunks/{velin-copy-NYDBOKXT.js → velin-copy-XDQSV2XU.js} +1 -1
  109. package/dist/chunks/{velin-countdown-4ZVDGK4R.js → velin-countdown-V4Z5MA63.js} +1 -1
  110. package/dist/chunks/velin-data-table-KK7IDGTP.js +302 -0
  111. package/dist/chunks/velin-data-table-UVR42UUM.js +372 -0
  112. package/dist/chunks/{velin-dialog-GTTZI564.js → velin-dialog-3YK2RPRV.js} +9 -2
  113. package/dist/chunks/{velin-dialog-ELFOZIXZ.js → velin-dialog-S24KAP4S.js} +3 -1
  114. package/dist/chunks/{velin-drawer-BE6TYN3H.js → velin-drawer-5PNU624J.js} +2 -4
  115. package/dist/chunks/{velin-drawer-DLL2GLT6.js → velin-drawer-CCM6S25D.js} +3 -5
  116. package/dist/chunks/{velin-email-RBX6ZR4S.js → velin-email-TNFONXPW.js} +1 -1
  117. package/dist/chunks/velin-file-dropzone-QRGF2JNA.js +154 -0
  118. package/dist/chunks/velin-file-dropzone-XIZMJ3NB.js +154 -0
  119. package/dist/chunks/velin-form-summary-XRQ7COQH.js +273 -0
  120. package/dist/chunks/{velin-icon-7M76DOPB.js → velin-icon-A6NCXGUK.js} +59 -17
  121. package/dist/chunks/{velin-icon-ZTSKUOVN.js → velin-icon-EC4B5XCM.js} +18 -5
  122. package/dist/chunks/{velin-lightbox-6E7CAATP.js → velin-lightbox-FFK4TUUP.js} +7 -6
  123. package/dist/chunks/{velin-lightbox-I75U6JF4.js → velin-lightbox-P725FLTY.js} +15 -5
  124. package/dist/chunks/{velin-menubar-J66JXRWW.js → velin-menubar-6I2LM5HL.js} +1 -1
  125. package/dist/chunks/{velin-modal-5HYNNNUI.js → velin-modal-ICTMGRSU.js} +7 -8
  126. package/dist/chunks/{velin-modal-AEOTZ67O.js → velin-modal-JBNODH7D.js} +2 -4
  127. package/dist/chunks/{velin-popover-T72HK3DF.js → velin-popover-WZG2GHBL.js} +25 -4
  128. package/dist/chunks/{velin-rating-N4DI2XNL.js → velin-rating-QBBYRRBU.js} +1 -1
  129. package/dist/chunks/{velin-search-7B4KJ6TX.js → velin-search-B3L6J2RP.js} +2 -2
  130. package/dist/chunks/{velin-search-2XRQFBCS.js → velin-search-L3UG6NDQ.js} +5 -4
  131. package/dist/chunks/{velin-secure-field-6PV7RZDJ.js → velin-secure-field-DGYMP7OK.js} +1 -1
  132. package/dist/chunks/{velin-segmented-control-GYD5BIXQ.js → velin-segmented-control-P74GJPAR.js} +1 -1
  133. package/dist/chunks/{velin-sheet-R2Y67X6H.js → velin-sheet-M5GZZD7G.js} +2 -4
  134. package/dist/chunks/{velin-sheet-OUK6572T.js → velin-sheet-X7RXIWKV.js} +3 -5
  135. package/dist/chunks/{velin-stepper-X54WGHDG.js → velin-stepper-4YQJUHPC.js} +12 -5
  136. package/dist/chunks/{velin-toast-OMMOA2DK.js → velin-toast-UG5LWTG2.js} +5 -1
  137. package/dist/chunks/{velin-tooltip-BDHGRTQB.js → velin-tooltip-ZDH5SDLU.js} +1 -1
  138. package/dist/chunks/yaml-O67LEQYT.js +26 -0
  139. package/dist/llms.txt +29 -7
  140. package/dist/search-index.json +182 -10
  141. package/dist/velin-agent.json +3997 -26
  142. package/dist/velinstyle-components.iife.js +1952 -287
  143. package/dist/velinstyle-components.js +1956 -287
  144. package/dist/velinstyle-components.min.js +267 -119
  145. package/dist/velinstyle.css +381 -67
  146. package/dist/velinstyle.d.ts +4 -0
  147. package/dist/velinstyle.min.css +1 -1
  148. package/package.json +26 -6
  149. package/packages/skill-engine/README.md +24 -0
  150. package/packages/skill-engine/package.json +7 -0
  151. package/packages/skill-engine/src/catalog.js +29 -0
  152. package/packages/skill-engine/src/index.js +5 -0
  153. package/packages/skill-engine/src/install.js +69 -0
  154. package/packages/skill-engine/src/registry.js +27 -0
  155. package/packages/skill-engine/src/resolve.js +68 -0
  156. package/packages/skill-engine/src/validate.js +40 -0
  157. package/packages/velinstyle-skills/README.md +6 -0
  158. package/packages/velinstyle-skills/adapter.json +8 -0
  159. package/packages/velinstyle-skills/bundles/landing-starter.bundle.yaml +12 -0
  160. package/packages/velinstyle-skills/bundles/review-suite.bundle.yaml +11 -0
  161. package/packages/velinstyle-skills/catalog.json +1004 -0
  162. package/packages/velinstyle-skills/graphs/component-ship.graph.yaml +14 -0
  163. package/packages/velinstyle-skills/graphs/landingpage.graph.yaml +21 -0
  164. package/packages/velinstyle-skills/graphs/release-gate.graph.yaml +15 -0
  165. package/packages/velinstyle-skills/index.js +22 -0
  166. package/packages/velinstyle-skills/package.json +7 -0
  167. package/packages/velinstyle-skills/packs/accessibility.pack.yaml +9 -0
  168. package/packages/velinstyle-skills/packs/design.pack.yaml +10 -0
  169. package/packages/velinstyle-skills/packs/documentation.pack.yaml +8 -0
  170. package/packages/velinstyle-skills/packs/enterprise.pack.yaml +10 -0
  171. package/packages/velinstyle-skills/packs/frontend.pack.yaml +10 -0
  172. package/packages/velinstyle-skills/packs/review.pack.yaml +9 -0
  173. package/packages/velinstyle-skills/projects/admin-panel.project.yaml +12 -0
  174. package/packages/velinstyle-skills/projects/blog.project.yaml +11 -0
  175. package/packages/velinstyle-skills/projects/documentation-portal.project.yaml +13 -0
  176. package/packages/velinstyle-skills/projects/ecommerce-shop.project.yaml +12 -0
  177. package/packages/velinstyle-skills/projects/landing-page.project.yaml +12 -0
  178. package/packages/velinstyle-skills/projects/laravel-dashboard.project.yaml +12 -0
  179. package/packages/velinstyle-skills/projects/marketing-website.project.yaml +14 -0
  180. package/packages/velinstyle-skills/projects/portfolio.project.yaml +12 -0
  181. package/packages/velinstyle-skills/registry.json +142 -0
  182. package/packages/velinstyle-skills/scripts/generate-assets.mjs +163 -0
  183. package/packages/velinstyle-skills/skills/velin-a11y-focus-keyboard/SKILL.md +57 -0
  184. package/packages/velinstyle-skills/skills/velin-a11y-page-checklist/SKILL.md +57 -0
  185. package/packages/velinstyle-skills/skills/velin-a11y-scan-fix/SKILL.md +57 -0
  186. package/packages/velinstyle-skills/skills/velin-ai-agent-briefing/SKILL.md +57 -0
  187. package/packages/velinstyle-skills/skills/velin-ai-plan-scaffold-review/SKILL.md +57 -0
  188. package/packages/velinstyle-skills/skills/velin-ai-skill-authoring/SKILL.md +57 -0
  189. package/packages/velinstyle-skills/skills/velin-components-accessibility-map/SKILL.md +57 -0
  190. package/packages/velinstyle-skills/skills/velin-components-api-contract/SKILL.md +57 -0
  191. package/packages/velinstyle-skills/skills/velin-components-recipe/SKILL.md +57 -0
  192. package/packages/velinstyle-skills/skills/velin-components-select-compose/SKILL.md +57 -0
  193. package/packages/velinstyle-skills/skills/velin-design-hero-compose/SKILL.md +57 -0
  194. package/packages/velinstyle-skills/skills/velin-design-section-rhythm/SKILL.md +57 -0
  195. package/packages/velinstyle-skills/skills/velin-design-surface-roles/SKILL.md +57 -0
  196. package/packages/velinstyle-skills/skills/velin-dev-component-wire/SKILL.md +57 -0
  197. package/packages/velinstyle-skills/skills/velin-dev-page-scaffold/SKILL.md +57 -0
  198. package/packages/velinstyle-skills/skills/velin-dev-plan-first/SKILL.md +57 -0
  199. package/packages/velinstyle-skills/skills/velin-docs-component-guide/SKILL.md +57 -0
  200. package/packages/velinstyle-skills/skills/velin-docs-generate-api/SKILL.md +57 -0
  201. package/packages/velinstyle-skills/skills/velin-dx-doctor-setup/SKILL.md +57 -0
  202. package/packages/velinstyle-skills/skills/velin-dx-project-init/SKILL.md +57 -0
  203. package/packages/velinstyle-skills/skills/velin-migration-legacy-wc/SKILL.md +57 -0
  204. package/packages/velinstyle-skills/skills/velin-migration-prefix-classes/SKILL.md +57 -0
  205. package/packages/velinstyle-skills/skills/velin-motion-reduced-safe/SKILL.md +57 -0
  206. package/packages/velinstyle-skills/skills/velin-motion-reveal-feedback/SKILL.md +57 -0
  207. package/packages/velinstyle-skills/skills/velin-perf-audit-fix/SKILL.md +57 -0
  208. package/packages/velinstyle-skills/skills/velin-perf-runtime-tree-shake/SKILL.md +57 -0
  209. package/packages/velinstyle-skills/skills/velin-release-changelog-sync/SKILL.md +57 -0
  210. package/packages/velinstyle-skills/skills/velin-release-meta-rebuild/SKILL.md +57 -0
  211. package/packages/velinstyle-skills/skills/velin-review-conversion-hero/SKILL.md +57 -0
  212. package/packages/velinstyle-skills/skills/velin-review-page-gate/SKILL.md +57 -0
  213. package/packages/velinstyle-skills/skills/velin-review-seo-meta/SKILL.md +57 -0
  214. package/packages/velinstyle-skills/skills/velin-security-sanitize-html/SKILL.md +57 -0
  215. package/packages/velinstyle-skills/skills/velin-security-scan-pii/SKILL.md +57 -0
  216. package/packages/velinstyle-skills/skills/velin-testing-a11y-suite/SKILL.md +57 -0
  217. package/packages/velinstyle-skills/skills/velin-testing-visual-smoke/SKILL.md +57 -0
  218. package/packages/velinstyle-skills/skills/velin-themes-apply-token/SKILL.md +57 -0
  219. package/packages/velinstyle-skills/skills/velin-themes-contrast-check/SKILL.md +57 -0
  220. package/packages/velinstyle-skills/skills/velin-utilities-layout-apply/SKILL.md +57 -0
  221. package/packages/velinstyle-skills/skills/velin-utilities-prefix-fix/SKILL.md +57 -0
  222. package/packages/velinstyle-skills/skills/velin-utilities-token-map/SKILL.md +57 -0
  223. package/packages/velinstyle-skills/templates/admin-panel.template.yaml +12 -0
  224. package/packages/velinstyle-skills/templates/blog.template.yaml +12 -0
  225. package/packages/velinstyle-skills/templates/documentation-portal.template.yaml +12 -0
  226. package/packages/velinstyle-skills/templates/ecommerce-shop.template.yaml +13 -0
  227. package/packages/velinstyle-skills/templates/landing-page.template.yaml +12 -0
  228. package/packages/velinstyle-skills/templates/laravel-dashboard.template.yaml +11 -0
  229. package/packages/velinstyle-skills/templates/marketing-website.template.yaml +13 -0
  230. package/packages/velinstyle-skills/templates/portfolio.template.yaml +12 -0
  231. package/schemas/component-knowledge.schema.json +33 -0
  232. package/schemas/design-constraint.schema.json +15 -0
  233. package/schemas/design-intelligence.schema.json +19 -0
  234. package/schemas/design-rule.schema.json +19 -0
  235. package/schemas/page-template.schema.json +24 -0
  236. package/schemas/review-report.schema.json +36 -0
  237. package/schemas/section.schema.json +22 -0
  238. package/schemas/skill-bundle.schema.json +15 -0
  239. package/schemas/skill-graph.schema.json +25 -0
  240. package/schemas/skill-pack.schema.json +14 -0
  241. package/schemas/skill-project.schema.json +16 -0
  242. package/schemas/skill-record.schema.json +27 -0
  243. package/schemas/skill-registry.schema.json +17 -0
  244. package/schemas/skill-template.schema.json +17 -0
  245. package/schemas/token-graph.schema.json +27 -0
  246. package/schemas/utility.schema.json +166 -0
  247. package/src/base/wc-placeholder.css +7 -0
  248. package/src/components/alert.css +22 -1
  249. package/src/components/calendar-dropzone.css +5 -0
  250. package/src/components/data-table.css +93 -0
  251. package/src/components/form-validation.css +40 -0
  252. package/src/layout/app-shell.css +33 -0
  253. package/src/tokens/shadow.css +13 -0
  254. package/src/utilities/border.css +2 -0
  255. package/src/utilities/display.css +1 -24
  256. package/src/utilities/responsive.css +16 -1
  257. package/src/utilities/spacing.css +32 -0
  258. package/src/velinstyle.css +3 -0
  259. package/dist/chunks/attributes-3BZLI7XF.js +0 -364
  260. package/dist/chunks/attributes-4N5JP4CG.js +0 -364
  261. package/dist/chunks/attributes-AIR7SVLK.js +0 -364
  262. package/dist/chunks/attributes-BGRN5XZO.js +0 -364
  263. package/dist/chunks/attributes-BVSQAYLR.js +0 -364
  264. package/dist/chunks/attributes-CDYFUY7Y.js +0 -364
  265. package/dist/chunks/attributes-JMYJZBPE.js +0 -364
  266. package/dist/chunks/attributes-R5JY3X4O.js +0 -374
  267. package/dist/chunks/attributes-TNSPQTHX.js +0 -374
  268. package/dist/chunks/chunk-2354NEBP.js +0 -109
  269. package/dist/chunks/chunk-2VNA7G7S.js +0 -109
  270. package/dist/chunks/chunk-AQLRJSGG.js +0 -109
  271. package/dist/chunks/chunk-EWPSDMQT.js +0 -109
  272. package/dist/chunks/chunk-LA7264K4.js +0 -85
  273. package/dist/chunks/chunk-QTJJ3DPO.js +0 -109
  274. package/dist/chunks/chunk-RZQVWFHW.js +0 -109
  275. package/dist/chunks/chunk-U4XHMZ7D.js +0 -109
  276. package/dist/chunks/chunk-V5YIUA6G.js +0 -109
  277. package/dist/chunks/chunk-ZPSPKUYD.js +0 -109
  278. package/dist/chunks/velin-bottom-nav-N3DFQR66.js +0 -86
  279. package/dist/chunks/velin-carousel-Y5VCOABZ.js +0 -213
  280. package/dist/chunks/velin-code-block-ASUBTAQV.js +0 -119
  281. package/dist/chunks/velin-code-block-Y6CM726Q.js +0 -119
  282. package/dist/chunks/velin-combobox-2KDTCQ7N.js +0 -155
  283. package/dist/chunks/velin-command-M7RNHUPR.js +0 -136
  284. package/dist/chunks/velin-copy-T5KDTDXP.js +0 -81
  285. package/dist/chunks/velin-countdown-5XSTN3BH.js +0 -101
  286. package/dist/chunks/velin-counter-ISCTEAHI.js +0 -118
  287. package/dist/chunks/velin-dialog-D2GUHSCD.js +0 -144
  288. package/dist/chunks/velin-dialog-IJLFDXAJ.js +0 -144
  289. package/dist/chunks/velin-dialog-TNS3WXMB.js +0 -144
  290. package/dist/chunks/velin-drawer-MJS6WCF7.js +0 -130
  291. package/dist/chunks/velin-email-N5B5ZYTE.js +0 -90
  292. package/dist/chunks/velin-icon-KZO3IEHG.js +0 -164
  293. package/dist/chunks/velin-lightbox-QOTB4PRL.js +0 -140
  294. package/dist/chunks/velin-lightbox-UCD6N7HF.js +0 -140
  295. package/dist/chunks/velin-live-dot-XHUZCIDZ.js +0 -67
  296. package/dist/chunks/velin-menubar-SFEXJE56.js +0 -86
  297. package/dist/chunks/velin-modal-2HOBXE6F.js +0 -176
  298. package/dist/chunks/velin-modal-GLI2XZZB.js +0 -176
  299. package/dist/chunks/velin-popover-EXINRIGJ.js +0 -145
  300. package/dist/chunks/velin-popover-OXX7MRZ6.js +0 -145
  301. package/dist/chunks/velin-popover-YKCCROJG.js +0 -145
  302. package/dist/chunks/velin-rating-XRQJV4LO.js +0 -91
  303. package/dist/chunks/velin-scroll-top-F2FCZNYD.js +0 -58
  304. package/dist/chunks/velin-search-4YHJWYSS.js +0 -556
  305. package/dist/chunks/velin-search-LWHY23Q5.js +0 -474
  306. package/dist/chunks/velin-secure-field-H7FSBUJW.js +0 -95
  307. package/dist/chunks/velin-segmented-control-PQX6LKOJ.js +0 -109
  308. package/dist/chunks/velin-sheet-3OGI4HD7.js +0 -119
  309. package/dist/chunks/velin-sparkline-GVEANJVW.js +0 -184
  310. package/dist/chunks/velin-sparkline-KDA3GQAN.js +0 -162
  311. package/dist/chunks/velin-stepper-2ZIHLKGM.js +0 -141
  312. package/dist/chunks/velin-stepper-FYEUEWE6.js +0 -141
  313. package/dist/chunks/velin-stepper-wc-QU3KRVQR.js +0 -133
  314. package/dist/chunks/velin-theme-toggle-BNNS7WLV.js +0 -285
  315. package/dist/chunks/velin-theme-toggle-NO7V4TWI.js +0 -285
  316. package/dist/chunks/velin-theme-toggle-QELCJM2Q.js +0 -291
  317. package/dist/chunks/velin-toast-RCZ4CSUC.js +0 -109
  318. package/dist/chunks/velin-toast-WEUBTJG6.js +0 -109
  319. package/dist/chunks/velin-tooltip-664IOHZA.js +0 -112
  320. package/dist/chunks/velin-tooltip-wc-LXDVT6DE.js +0 -103
  321. package/dist/chunks/worker-client-R6DBYPFT.js +0 -46
@@ -316,7 +316,7 @@
316
316
  function createDOMPurify() {
317
317
  let window2 = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : getGlobal();
318
318
  const DOMPurify = (root) => createDOMPurify(root);
319
- DOMPurify.version = "3.4.5";
319
+ DOMPurify.version = "3.4.12";
320
320
  DOMPurify.removed = [];
321
321
  if (!window2 || !window2.document || window2.document.nodeType !== NODE_TYPE.document || !window2.Element) {
322
322
  DOMPurify.isSupported = false;
@@ -325,14 +325,21 @@
325
325
  let document2 = window2.document;
326
326
  const originalDocument = document2;
327
327
  const currentScript = originalDocument.currentScript;
328
- const DocumentFragment = window2.DocumentFragment, HTMLTemplateElement = window2.HTMLTemplateElement, Node = window2.Node, Element = window2.Element, NodeFilter = window2.NodeFilter, _window$NamedNodeMap = window2.NamedNodeMap, NamedNodeMap = _window$NamedNodeMap === void 0 ? window2.NamedNodeMap || window2.MozNamedAttrMap : _window$NamedNodeMap, HTMLFormElement = window2.HTMLFormElement, DOMParser2 = window2.DOMParser, trustedTypes = window2.trustedTypes;
328
+ window2.DocumentFragment;
329
+ const HTMLTemplateElement = window2.HTMLTemplateElement, Node = window2.Node, Element = window2.Element, NodeFilter = window2.NodeFilter, _window$NamedNodeMap = window2.NamedNodeMap;
330
+ _window$NamedNodeMap === void 0 ? window2.NamedNodeMap || window2.MozNamedAttrMap : _window$NamedNodeMap;
331
+ window2.HTMLFormElement;
332
+ const DOMParser2 = window2.DOMParser, trustedTypes = window2.trustedTypes;
329
333
  const ElementPrototype = Element.prototype;
330
334
  const cloneNode = lookupGetter(ElementPrototype, "cloneNode");
331
335
  const remove = lookupGetter(ElementPrototype, "remove");
332
336
  const getNextSibling = lookupGetter(ElementPrototype, "nextSibling");
333
337
  const getChildNodes = lookupGetter(ElementPrototype, "childNodes");
334
338
  const getParentNode = lookupGetter(ElementPrototype, "parentNode");
339
+ const getShadowRoot = lookupGetter(ElementPrototype, "shadowRoot");
340
+ const getAttributes = lookupGetter(ElementPrototype, "attributes");
335
341
  const getNodeType = Node && Node.prototype ? lookupGetter(Node.prototype, "nodeType") : null;
342
+ const getNodeName = Node && Node.prototype ? lookupGetter(Node.prototype, "nodeName") : null;
336
343
  if (typeof HTMLTemplateElement === "function") {
337
344
  const template = document2.createElement("template");
338
345
  if (template.content && template.content.ownerDocument) {
@@ -341,6 +348,39 @@
341
348
  }
342
349
  let trustedTypesPolicy;
343
350
  let emptyHTML = "";
351
+ let defaultTrustedTypesPolicy;
352
+ let defaultTrustedTypesPolicyResolved = false;
353
+ let IN_TRUSTED_TYPES_POLICY = 0;
354
+ const _assertNotInTrustedTypesPolicy = function _assertNotInTrustedTypesPolicy2() {
355
+ if (IN_TRUSTED_TYPES_POLICY > 0) {
356
+ throw typeErrorCreate('A configured TRUSTED_TYPES_POLICY callback (createHTML or createScriptURL) must not call DOMPurify.sanitize, as that causes infinite recursion. Do not pass a policy whose callbacks wrap DOMPurify as TRUSTED_TYPES_POLICY; see the "DOMPurify and Trusted Types" section of the README.');
357
+ }
358
+ };
359
+ const _createTrustedHTML = function _createTrustedHTML2(html2) {
360
+ _assertNotInTrustedTypesPolicy();
361
+ IN_TRUSTED_TYPES_POLICY++;
362
+ try {
363
+ return trustedTypesPolicy.createHTML(html2);
364
+ } finally {
365
+ IN_TRUSTED_TYPES_POLICY--;
366
+ }
367
+ };
368
+ const _createTrustedScriptURL = function _createTrustedScriptURL2(scriptUrl) {
369
+ _assertNotInTrustedTypesPolicy();
370
+ IN_TRUSTED_TYPES_POLICY++;
371
+ try {
372
+ return trustedTypesPolicy.createScriptURL(scriptUrl);
373
+ } finally {
374
+ IN_TRUSTED_TYPES_POLICY--;
375
+ }
376
+ };
377
+ const _getDefaultTrustedTypesPolicy = function _getDefaultTrustedTypesPolicy2() {
378
+ if (!defaultTrustedTypesPolicyResolved) {
379
+ defaultTrustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
380
+ defaultTrustedTypesPolicyResolved = true;
381
+ }
382
+ return defaultTrustedTypesPolicy;
383
+ };
344
384
  const _document = document2, implementation = _document.implementation, createNodeIterator = _document.createNodeIterator, createDocumentFragment = _document.createDocumentFragment, getElementsByTagName = _document.getElementsByTagName;
345
385
  const importNode = originalDocument.importNode;
346
386
  let hooks = _createHooksMap();
@@ -395,6 +435,8 @@
395
435
  let SAFE_FOR_XML = true;
396
436
  let WHOLE_DOCUMENT = false;
397
437
  let SET_CONFIG = false;
438
+ let SET_CONFIG_ALLOWED_TAGS = null;
439
+ let SET_CONFIG_ALLOWED_ATTR = null;
398
440
  let FORCE_BODY = false;
399
441
  let RETURN_DOM = false;
400
442
  let RETURN_DOM_FRAGMENT = false;
@@ -406,7 +448,43 @@
406
448
  let IN_PLACE = false;
407
449
  let USE_PROFILES = {};
408
450
  let FORBID_CONTENTS = null;
409
- const DEFAULT_FORBID_CONTENTS = addToSet({}, ["annotation-xml", "audio", "colgroup", "desc", "foreignobject", "head", "iframe", "math", "mi", "mn", "mo", "ms", "mtext", "noembed", "noframes", "noscript", "plaintext", "script", "style", "svg", "template", "thead", "title", "video", "xmp"]);
451
+ const DEFAULT_FORBID_CONTENTS = addToSet({}, [
452
+ "annotation-xml",
453
+ "audio",
454
+ "colgroup",
455
+ "desc",
456
+ "foreignobject",
457
+ "head",
458
+ "iframe",
459
+ "math",
460
+ "mi",
461
+ "mn",
462
+ "mo",
463
+ "ms",
464
+ "mtext",
465
+ "noembed",
466
+ "noframes",
467
+ "noscript",
468
+ "plaintext",
469
+ "script",
470
+ // <selectedcontent> mirrors the selected <option>'s subtree, cloned by
471
+ // the UA (customizable <select>) — including any on* handlers — and the
472
+ // engine re-mirrors synchronously whenever a removal changes which
473
+ // option/selectedcontent is current, even inside DOMPurify's inert
474
+ // DOMParser document. Hoisting its children on removal re-inserts a fresh
475
+ // mirror target ahead of the walk, which the engine refills, looping
476
+ // forever (DoS) and amplifying output. Dropping its content on removal
477
+ // (rather than hoisting) breaks that cascade; the content is a duplicate
478
+ // of the option, which is sanitized on its own. See campaign-3 F1/F6.
479
+ "selectedcontent",
480
+ "style",
481
+ "svg",
482
+ "template",
483
+ "thead",
484
+ "title",
485
+ "video",
486
+ "xmp"
487
+ ]);
410
488
  let DATA_URI_TAGS = null;
411
489
  const DEFAULT_DATA_URI_TAGS = addToSet({}, ["audio", "video", "img", "source", "image", "track"]);
412
490
  let URI_SAFE_ATTRIBUTES = null;
@@ -418,8 +496,10 @@
418
496
  let IS_EMPTY_INPUT = false;
419
497
  let ALLOWED_NAMESPACES = null;
420
498
  const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
421
- let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ["mi", "mo", "mn", "ms", "mtext"]);
422
- let HTML_INTEGRATION_POINTS = addToSet({}, ["annotation-xml"]);
499
+ const DEFAULT_MATHML_TEXT_INTEGRATION_POINTS = freeze(["mi", "mo", "mn", "ms", "mtext"]);
500
+ let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS);
501
+ const DEFAULT_HTML_INTEGRATION_POINTS = freeze(["annotation-xml"]);
502
+ let HTML_INTEGRATION_POINTS = addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS);
423
503
  const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ["title", "style", "font", "a", "script"]);
424
504
  let PARSER_MEDIA_TYPE = null;
425
505
  const SUPPORTED_PARSER_MEDIA_TYPES = ["application/xhtml+xml", "text/html"];
@@ -442,14 +522,32 @@
442
522
  PARSER_MEDIA_TYPE = // eslint-disable-next-line unicorn/prefer-includes
443
523
  SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
444
524
  transformCaseFunc = PARSER_MEDIA_TYPE === "application/xhtml+xml" ? stringToString : stringToLowerCase;
445
- ALLOWED_TAGS = objectHasOwnProperty(cfg, "ALLOWED_TAGS") && arrayIsArray(cfg.ALLOWED_TAGS) ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
446
- ALLOWED_ATTR = objectHasOwnProperty(cfg, "ALLOWED_ATTR") && arrayIsArray(cfg.ALLOWED_ATTR) ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
447
- ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, "ALLOWED_NAMESPACES") && arrayIsArray(cfg.ALLOWED_NAMESPACES) ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
448
- URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, "ADD_URI_SAFE_ATTR") && arrayIsArray(cfg.ADD_URI_SAFE_ATTR) ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
449
- DATA_URI_TAGS = objectHasOwnProperty(cfg, "ADD_DATA_URI_TAGS") && arrayIsArray(cfg.ADD_DATA_URI_TAGS) ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
450
- FORBID_CONTENTS = objectHasOwnProperty(cfg, "FORBID_CONTENTS") && arrayIsArray(cfg.FORBID_CONTENTS) ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
451
- FORBID_TAGS = objectHasOwnProperty(cfg, "FORBID_TAGS") && arrayIsArray(cfg.FORBID_TAGS) ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
452
- FORBID_ATTR = objectHasOwnProperty(cfg, "FORBID_ATTR") && arrayIsArray(cfg.FORBID_ATTR) ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
525
+ ALLOWED_TAGS = _resolveSetOption(cfg, "ALLOWED_TAGS", DEFAULT_ALLOWED_TAGS, {
526
+ transform: transformCaseFunc
527
+ });
528
+ ALLOWED_ATTR = _resolveSetOption(cfg, "ALLOWED_ATTR", DEFAULT_ALLOWED_ATTR, {
529
+ transform: transformCaseFunc
530
+ });
531
+ ALLOWED_NAMESPACES = _resolveSetOption(cfg, "ALLOWED_NAMESPACES", DEFAULT_ALLOWED_NAMESPACES, {
532
+ transform: stringToString
533
+ });
534
+ URI_SAFE_ATTRIBUTES = _resolveSetOption(cfg, "ADD_URI_SAFE_ATTR", DEFAULT_URI_SAFE_ATTRIBUTES, {
535
+ transform: transformCaseFunc,
536
+ base: DEFAULT_URI_SAFE_ATTRIBUTES
537
+ });
538
+ DATA_URI_TAGS = _resolveSetOption(cfg, "ADD_DATA_URI_TAGS", DEFAULT_DATA_URI_TAGS, {
539
+ transform: transformCaseFunc,
540
+ base: DEFAULT_DATA_URI_TAGS
541
+ });
542
+ FORBID_CONTENTS = _resolveSetOption(cfg, "FORBID_CONTENTS", DEFAULT_FORBID_CONTENTS, {
543
+ transform: transformCaseFunc
544
+ });
545
+ FORBID_TAGS = _resolveSetOption(cfg, "FORBID_TAGS", clone({}), {
546
+ transform: transformCaseFunc
547
+ });
548
+ FORBID_ATTR = _resolveSetOption(cfg, "FORBID_ATTR", clone({}), {
549
+ transform: transformCaseFunc
550
+ });
453
551
  USE_PROFILES = objectHasOwnProperty(cfg, "USE_PROFILES") ? cfg.USE_PROFILES && typeof cfg.USE_PROFILES === "object" ? clone(cfg.USE_PROFILES) : cfg.USE_PROFILES : false;
454
552
  ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false;
455
553
  ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false;
@@ -468,8 +566,8 @@
468
566
  IN_PLACE = cfg.IN_PLACE || false;
469
567
  IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI;
470
568
  NAMESPACE = typeof cfg.NAMESPACE === "string" ? cfg.NAMESPACE : HTML_NAMESPACE;
471
- MATHML_TEXT_INTEGRATION_POINTS = objectHasOwnProperty(cfg, "MATHML_TEXT_INTEGRATION_POINTS") && cfg.MATHML_TEXT_INTEGRATION_POINTS && typeof cfg.MATHML_TEXT_INTEGRATION_POINTS === "object" ? clone(cfg.MATHML_TEXT_INTEGRATION_POINTS) : addToSet({}, ["mi", "mo", "mn", "ms", "mtext"]);
472
- HTML_INTEGRATION_POINTS = objectHasOwnProperty(cfg, "HTML_INTEGRATION_POINTS") && cfg.HTML_INTEGRATION_POINTS && typeof cfg.HTML_INTEGRATION_POINTS === "object" ? clone(cfg.HTML_INTEGRATION_POINTS) : addToSet({}, ["annotation-xml"]);
569
+ MATHML_TEXT_INTEGRATION_POINTS = objectHasOwnProperty(cfg, "MATHML_TEXT_INTEGRATION_POINTS") && cfg.MATHML_TEXT_INTEGRATION_POINTS && typeof cfg.MATHML_TEXT_INTEGRATION_POINTS === "object" ? clone(cfg.MATHML_TEXT_INTEGRATION_POINTS) : addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS);
570
+ HTML_INTEGRATION_POINTS = objectHasOwnProperty(cfg, "HTML_INTEGRATION_POINTS") && cfg.HTML_INTEGRATION_POINTS && typeof cfg.HTML_INTEGRATION_POINTS === "object" ? clone(cfg.HTML_INTEGRATION_POINTS) : addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS);
473
571
  const customElementHandling = objectHasOwnProperty(cfg, "CUSTOM_ELEMENT_HANDLING") && cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING === "object" ? clone(cfg.CUSTOM_ELEMENT_HANDLING) : create(null);
474
572
  CUSTOM_ELEMENT_HANDLING = create(null);
475
573
  if (objectHasOwnProperty(customElementHandling, "tagNameCheck") && isRegexOrFunction(customElementHandling.tagNameCheck)) {
@@ -481,6 +579,7 @@
481
579
  if (objectHasOwnProperty(customElementHandling, "allowCustomizedBuiltInElements") && typeof customElementHandling.allowCustomizedBuiltInElements === "boolean") {
482
580
  CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements;
483
581
  }
582
+ seal(CUSTOM_ELEMENT_HANDLING);
484
583
  if (SAFE_FOR_TEMPLATES) {
485
584
  ALLOW_DATA_ATTR = false;
486
585
  }
@@ -564,14 +663,23 @@
564
663
  if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== "function") {
565
664
  throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
566
665
  }
666
+ const previousTrustedTypesPolicy = trustedTypesPolicy;
567
667
  trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
568
- emptyHTML = trustedTypesPolicy.createHTML("");
668
+ try {
669
+ emptyHTML = _createTrustedHTML("");
670
+ } catch (error) {
671
+ trustedTypesPolicy = previousTrustedTypesPolicy;
672
+ throw error;
673
+ }
674
+ } else if (cfg.TRUSTED_TYPES_POLICY === null) {
675
+ trustedTypesPolicy = void 0;
676
+ emptyHTML = "";
569
677
  } else {
570
678
  if (trustedTypesPolicy === void 0) {
571
- trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
679
+ trustedTypesPolicy = _getDefaultTrustedTypesPolicy();
572
680
  }
573
- if (trustedTypesPolicy !== null && typeof emptyHTML === "string") {
574
- emptyHTML = trustedTypesPolicy.createHTML("");
681
+ if (trustedTypesPolicy && typeof emptyHTML === "string") {
682
+ emptyHTML = _createTrustedHTML("");
575
683
  }
576
684
  }
577
685
  if (freeze) {
@@ -581,6 +689,33 @@
581
689
  };
582
690
  const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
583
691
  const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
692
+ const _checkSvgNamespace = function _checkSvgNamespace2(tagName, parent, parentTagName) {
693
+ if (parent.namespaceURI === HTML_NAMESPACE) {
694
+ return tagName === "svg";
695
+ }
696
+ if (parent.namespaceURI === MATHML_NAMESPACE) {
697
+ return tagName === "svg" && (parentTagName === "annotation-xml" || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
698
+ }
699
+ return Boolean(ALL_SVG_TAGS[tagName]);
700
+ };
701
+ const _checkMathMlNamespace = function _checkMathMlNamespace2(tagName, parent, parentTagName) {
702
+ if (parent.namespaceURI === HTML_NAMESPACE) {
703
+ return tagName === "math";
704
+ }
705
+ if (parent.namespaceURI === SVG_NAMESPACE) {
706
+ return tagName === "math" && HTML_INTEGRATION_POINTS[parentTagName];
707
+ }
708
+ return Boolean(ALL_MATHML_TAGS[tagName]);
709
+ };
710
+ const _checkHtmlNamespace = function _checkHtmlNamespace2(tagName, parent, parentTagName) {
711
+ if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
712
+ return false;
713
+ }
714
+ if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
715
+ return false;
716
+ }
717
+ return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
718
+ };
584
719
  const _checkValidNamespace = function _checkValidNamespace2(element) {
585
720
  let parent = getParentNode(element);
586
721
  if (!parent || !parent.tagName) {
@@ -595,31 +730,13 @@
595
730
  return false;
596
731
  }
597
732
  if (element.namespaceURI === SVG_NAMESPACE) {
598
- if (parent.namespaceURI === HTML_NAMESPACE) {
599
- return tagName === "svg";
600
- }
601
- if (parent.namespaceURI === MATHML_NAMESPACE) {
602
- return tagName === "svg" && (parentTagName === "annotation-xml" || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
603
- }
604
- return Boolean(ALL_SVG_TAGS[tagName]);
733
+ return _checkSvgNamespace(tagName, parent, parentTagName);
605
734
  }
606
735
  if (element.namespaceURI === MATHML_NAMESPACE) {
607
- if (parent.namespaceURI === HTML_NAMESPACE) {
608
- return tagName === "math";
609
- }
610
- if (parent.namespaceURI === SVG_NAMESPACE) {
611
- return tagName === "math" && HTML_INTEGRATION_POINTS[parentTagName];
612
- }
613
- return Boolean(ALL_MATHML_TAGS[tagName]);
736
+ return _checkMathMlNamespace(tagName, parent, parentTagName);
614
737
  }
615
738
  if (element.namespaceURI === HTML_NAMESPACE) {
616
- if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
617
- return false;
618
- }
619
- if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
620
- return false;
621
- }
622
- return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
739
+ return _checkHtmlNamespace(tagName, parent, parentTagName);
623
740
  }
624
741
  if (PARSER_MEDIA_TYPE === "application/xhtml+xml" && ALLOWED_NAMESPACES[element.namespaceURI]) {
625
742
  return true;
@@ -634,6 +751,38 @@
634
751
  getParentNode(node).removeChild(node);
635
752
  } catch (_) {
636
753
  remove(node);
754
+ if (!getParentNode(node)) {
755
+ throw typeErrorCreate("a node selected for removal could not be detached from its tree and cannot be safely returned; refusing to sanitize in place");
756
+ }
757
+ }
758
+ };
759
+ const _neutralizeRoot = function _neutralizeRoot2(root) {
760
+ _neutralizeSubtree(root);
761
+ const childNodes = getChildNodes(root);
762
+ if (childNodes) {
763
+ const snapshot = [];
764
+ arrayForEach(childNodes, (child) => {
765
+ arrayPush(snapshot, child);
766
+ });
767
+ arrayForEach(snapshot, (child) => {
768
+ try {
769
+ remove(child);
770
+ } catch (_) {
771
+ }
772
+ });
773
+ }
774
+ const attributes = getAttributes(root);
775
+ if (attributes) {
776
+ for (let i = attributes.length - 1; i >= 0; --i) {
777
+ const attribute = attributes[i];
778
+ const name = attribute && attribute.name;
779
+ if (typeof name === "string") {
780
+ try {
781
+ root.removeAttribute(name);
782
+ } catch (_) {
783
+ }
784
+ }
785
+ }
637
786
  }
638
787
  };
639
788
  const _removeAttribute = function _removeAttribute2(name, element) {
@@ -663,6 +812,75 @@
663
812
  }
664
813
  }
665
814
  };
815
+ const _stripDisallowedAttributes = function _stripDisallowedAttributes2(element) {
816
+ const attributes = getAttributes(element);
817
+ if (!attributes) {
818
+ return;
819
+ }
820
+ for (let i = attributes.length - 1; i >= 0; --i) {
821
+ const attribute = attributes[i];
822
+ const name = attribute && attribute.name;
823
+ if (typeof name !== "string" || ALLOWED_ATTR[transformCaseFunc(name)]) {
824
+ continue;
825
+ }
826
+ try {
827
+ element.removeAttribute(name);
828
+ } catch (_) {
829
+ }
830
+ }
831
+ };
832
+ const _neutralizeSubtree = function _neutralizeSubtree2(root) {
833
+ const stack = [root];
834
+ while (stack.length > 0) {
835
+ const node = stack.pop();
836
+ const nodeType = getNodeType ? getNodeType(node) : node.nodeType;
837
+ if (nodeType === NODE_TYPE.element) {
838
+ _stripDisallowedAttributes(node);
839
+ }
840
+ const childNodes = getChildNodes(node);
841
+ if (childNodes) {
842
+ for (let i = childNodes.length - 1; i >= 0; --i) {
843
+ stack.push(childNodes[i]);
844
+ }
845
+ }
846
+ }
847
+ };
848
+ const _neutralizePatchLinkage = function _neutralizePatchLinkage2(root) {
849
+ if (!SAFE_FOR_XML) {
850
+ return;
851
+ }
852
+ const stack = [root];
853
+ while (stack.length > 0) {
854
+ const node = stack.pop();
855
+ const nodeType = getNodeType ? getNodeType(node) : node.nodeType;
856
+ if (nodeType === NODE_TYPE.processingInstruction || nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, node.data)) {
857
+ try {
858
+ remove(node);
859
+ } catch (_) {
860
+ }
861
+ continue;
862
+ }
863
+ if (nodeType === NODE_TYPE.element) {
864
+ const element = node;
865
+ const lcTag = transformCaseFunc(getNodeName ? getNodeName(node) : node.nodeName);
866
+ try {
867
+ if (element.hasAttribute && element.hasAttribute("patchsrc")) {
868
+ element.removeAttribute("patchsrc");
869
+ }
870
+ if (element.hasAttribute && element.hasAttribute("for") && lcTag !== "label" && lcTag !== "output") {
871
+ element.removeAttribute("for");
872
+ }
873
+ } catch (_) {
874
+ }
875
+ }
876
+ const childNodes = getChildNodes(node);
877
+ if (childNodes) {
878
+ for (let i = childNodes.length - 1; i >= 0; --i) {
879
+ stack.push(childNodes[i]);
880
+ }
881
+ }
882
+ }
883
+ };
666
884
  const _initDocument = function _initDocument2(dirty) {
667
885
  let doc = null;
668
886
  let leadingWhitespace = null;
@@ -675,7 +893,7 @@
675
893
  if (PARSER_MEDIA_TYPE === "application/xhtml+xml" && NAMESPACE === HTML_NAMESPACE) {
676
894
  dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + "</body></html>";
677
895
  }
678
- const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
896
+ const dirtyPayload = trustedTypesPolicy ? _createTrustedHTML(dirty) : dirty;
679
897
  if (NAMESPACE === HTML_NAMESPACE) {
680
898
  try {
681
899
  doc = new DOMParser2().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
@@ -707,7 +925,14 @@
707
925
  null
708
926
  );
709
927
  };
710
- const _scrubTemplateExpressions = function _scrubTemplateExpressions2(node) {
928
+ const _stripTemplateExpressions = function _stripTemplateExpressions2(value) {
929
+ value = stringReplace(value, MUSTACHE_EXPR$1, " ");
930
+ value = stringReplace(value, ERB_EXPR$1, " ");
931
+ value = stringReplace(value, TMPLIT_EXPR$1, " ");
932
+ return value;
933
+ };
934
+ const _scrubTemplateExpressions2 = function _scrubTemplateExpressions(node) {
935
+ var _node$querySelectorAl;
711
936
  node.normalize();
712
937
  const walker = createNodeIterator.call(
713
938
  node.ownerDocument || node,
@@ -718,16 +943,60 @@
718
943
  );
719
944
  let currentNode = walker.nextNode();
720
945
  while (currentNode) {
721
- let data = currentNode.data;
722
- arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], (expr) => {
723
- data = stringReplace(data, expr, " ");
724
- });
725
- currentNode.data = data;
946
+ currentNode.data = _stripTemplateExpressions(currentNode.data);
726
947
  currentNode = walker.nextNode();
727
948
  }
949
+ const templates = (_node$querySelectorAl = node.querySelectorAll) === null || _node$querySelectorAl === void 0 ? void 0 : _node$querySelectorAl.call(node, "template");
950
+ if (templates) {
951
+ arrayForEach(templates, (tmpl) => {
952
+ if (_isDocumentFragment(tmpl.content)) {
953
+ _scrubTemplateExpressions2(tmpl.content);
954
+ }
955
+ });
956
+ }
728
957
  };
729
958
  const _isClobbered = function _isClobbered2(element) {
730
- return element instanceof HTMLFormElement && (typeof element.nodeName !== "string" || typeof element.textContent !== "string" || typeof element.removeChild !== "function" || !(element.attributes instanceof NamedNodeMap) || typeof element.removeAttribute !== "function" || typeof element.setAttribute !== "function" || typeof element.namespaceURI !== "string" || typeof element.insertBefore !== "function" || typeof element.hasChildNodes !== "function");
959
+ const realTagName = getNodeName ? getNodeName(element) : null;
960
+ if (typeof realTagName !== "string") {
961
+ return false;
962
+ }
963
+ if (transformCaseFunc(realTagName) !== "form") {
964
+ return false;
965
+ }
966
+ return typeof element.nodeName !== "string" || typeof element.textContent !== "string" || typeof element.removeChild !== "function" || // Realm-safe NamedNodeMap detection: equality against the cached
967
+ // prototype getter. Clobbered .attributes (e.g. <input name="attributes">)
968
+ // makes the direct read diverge from the cached read; a clean form
969
+ // (same-realm OR foreign-realm) has both reads pointing at the same
970
+ // canonical NamedNodeMap.
971
+ element.attributes !== getAttributes(element) || typeof element.removeAttribute !== "function" || typeof element.setAttribute !== "function" || typeof element.namespaceURI !== "string" || typeof element.insertBefore !== "function" || typeof element.hasChildNodes !== "function" || // NodeType clobbering probe. Cached Node.prototype.nodeType getter
972
+ // returns the integer 1 for any Element regardless of realm; direct
973
+ // read on a clobbered form (e.g. <input name="nodeType">) returns
974
+ // the named child element. Cheap addition — nodeType is read from
975
+ // an internal slot, no serialization cost — and removes a residual
976
+ // clobbering surface used by several mXSS / PI / comment branches
977
+ // in _sanitizeElements that compare currentNode.nodeType directly.
978
+ element.nodeType !== getNodeType(element) || // HTMLFormElement has [LegacyOverrideBuiltIns]: a descendant named
979
+ // "childNodes" shadows the prototype getter. Direct reads of
980
+ // form.childNodes from a clobbered form return the named child
981
+ // instead of the real NodeList, so any walk that reads it directly
982
+ // skips the form's real children. Compare the direct read to the
983
+ // cached Node.prototype getter — when the form's named-property
984
+ // getter intercepts the read, the two values differ and we flag
985
+ // the form. This catches every clobbering child type (input,
986
+ // select, etc.) regardless of whether the named child happens to
987
+ // carry a numeric .length, which a typeof-based probe would miss
988
+ // (e.g. HTMLSelectElement.length is a defined unsigned-long).
989
+ element.childNodes !== getChildNodes(element);
990
+ };
991
+ const _isDocumentFragment = function _isDocumentFragment2(value) {
992
+ if (!getNodeType || typeof value !== "object" || value === null) {
993
+ return false;
994
+ }
995
+ try {
996
+ return getNodeType(value) === NODE_TYPE.documentFragment;
997
+ } catch (_) {
998
+ return false;
999
+ }
731
1000
  };
732
1001
  const _isNode = function _isNode2(value) {
733
1002
  if (!getNodeType || typeof value !== "object" || value === null) {
@@ -740,74 +1009,90 @@
740
1009
  }
741
1010
  };
742
1011
  function _executeHooks(hooks2, currentNode, data) {
1012
+ if (hooks2.length === 0) {
1013
+ return;
1014
+ }
743
1015
  arrayForEach(hooks2, (hook) => {
744
1016
  hook.call(DOMPurify, currentNode, data, CONFIG);
745
1017
  });
746
1018
  }
747
- const _sanitizeElements = function _sanitizeElements2(currentNode) {
748
- let content = null;
1019
+ const _isUnsafeNode = function _isUnsafeNode2(currentNode, tagName) {
1020
+ if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.textContent) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.innerHTML)) {
1021
+ return true;
1022
+ }
1023
+ if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === "style" && _isNode(currentNode.firstElementChild)) {
1024
+ return true;
1025
+ }
1026
+ if (currentNode.nodeType === NODE_TYPE.processingInstruction) {
1027
+ return true;
1028
+ }
1029
+ if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, currentNode.data)) {
1030
+ return true;
1031
+ }
1032
+ return false;
1033
+ };
1034
+ const _sanitizeDisallowedNode = function _sanitizeDisallowedNode2(currentNode, tagName) {
1035
+ if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
1036
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
1037
+ return false;
1038
+ }
1039
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
1040
+ return false;
1041
+ }
1042
+ }
1043
+ if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
1044
+ const parentNode = getParentNode(currentNode);
1045
+ const childNodes = getChildNodes(currentNode);
1046
+ if (childNodes && parentNode) {
1047
+ const childCount = childNodes.length;
1048
+ for (let i = childCount - 1; i >= 0; --i) {
1049
+ const hoisted = IN_PLACE ? childNodes[i] : cloneNode(childNodes[i], true);
1050
+ parentNode.insertBefore(hoisted, getNextSibling(currentNode));
1051
+ }
1052
+ }
1053
+ }
1054
+ _forceRemove(currentNode);
1055
+ return true;
1056
+ };
1057
+ const _sanitizeElements = function _sanitizeElements2(currentNode, root) {
749
1058
  _executeHooks(hooks.beforeSanitizeElements, currentNode, null);
1059
+ if (currentNode !== root && getParentNode(currentNode) === null) {
1060
+ return true;
1061
+ }
750
1062
  if (_isClobbered(currentNode)) {
751
1063
  _forceRemove(currentNode);
752
1064
  return true;
753
1065
  }
754
- const tagName = transformCaseFunc(currentNode.nodeName);
1066
+ const tagName = transformCaseFunc(getNodeName ? getNodeName(currentNode) : currentNode.nodeName);
755
1067
  _executeHooks(hooks.uponSanitizeElement, currentNode, {
756
1068
  tagName,
757
1069
  allowedTags: ALLOWED_TAGS
758
1070
  });
759
- if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w!]/g, currentNode.innerHTML) && regExpTest(/<[/\w!]/g, currentNode.textContent)) {
760
- _forceRemove(currentNode);
761
- return true;
762
- }
763
- if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === "style" && _isNode(currentNode.firstElementChild)) {
764
- _forceRemove(currentNode);
765
- return true;
766
- }
767
- if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
768
- _forceRemove(currentNode);
1071
+ if (currentNode !== root && getParentNode(currentNode) === null) {
769
1072
  return true;
770
1073
  }
771
- if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\w]/g, currentNode.data)) {
1074
+ if (_isUnsafeNode(currentNode, tagName)) {
772
1075
  _forceRemove(currentNode);
773
1076
  return true;
774
1077
  }
775
1078
  if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
776
- if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
777
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
778
- return false;
779
- }
780
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
781
- return false;
782
- }
783
- }
784
- if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
785
- const parentNode = getParentNode(currentNode) || currentNode.parentNode;
786
- const childNodes = getChildNodes(currentNode) || currentNode.childNodes;
787
- if (childNodes && parentNode) {
788
- const childCount = childNodes.length;
789
- for (let i = childCount - 1; i >= 0; --i) {
790
- const childClone = cloneNode(childNodes[i], true);
791
- parentNode.insertBefore(childClone, getNextSibling(currentNode));
792
- }
793
- }
1079
+ const removed2 = _sanitizeDisallowedNode(currentNode, tagName);
1080
+ if (removed2 === false) {
1081
+ _executeHooks(hooks.afterSanitizeElements, currentNode, null);
794
1082
  }
795
- _forceRemove(currentNode);
796
- return true;
1083
+ return removed2;
797
1084
  }
798
- if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
1085
+ const nt = getNodeType ? getNodeType(currentNode) : currentNode.nodeType;
1086
+ if (nt === NODE_TYPE.element && !_checkValidNamespace(currentNode)) {
799
1087
  _forceRemove(currentNode);
800
1088
  return true;
801
1089
  }
802
- if ((tagName === "noscript" || tagName === "noembed" || tagName === "noframes") && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
1090
+ if ((tagName === "noscript" || tagName === "noembed" || tagName === "noframes") && regExpTest(FALLBACK_TAG_CLOSE, currentNode.innerHTML)) {
803
1091
  _forceRemove(currentNode);
804
1092
  return true;
805
1093
  }
806
1094
  if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
807
- content = currentNode.textContent;
808
- arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], (expr) => {
809
- content = stringReplace(content, expr, " ");
810
- });
1095
+ const content = _stripTemplateExpressions(currentNode.textContent);
811
1096
  if (currentNode.textContent !== content) {
812
1097
  arrayPush(DOMPurify.removed, {
813
1098
  element: currentNode.cloneNode()
@@ -822,13 +1107,19 @@
822
1107
  if (FORBID_ATTR[lcName]) {
823
1108
  return false;
824
1109
  }
1110
+ if (SAFE_FOR_XML && lcName === "patchsrc") {
1111
+ return false;
1112
+ }
1113
+ if (SAFE_FOR_XML && lcName === "for" && lcTag !== "label" && lcTag !== "output") {
1114
+ return false;
1115
+ }
825
1116
  if (SANITIZE_DOM && (lcName === "id" || lcName === "name") && (value in document2 || value in formElement)) {
826
1117
  return false;
827
1118
  }
828
1119
  const nameIsPermitted = ALLOWED_ATTR[lcName] || EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag);
829
- if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR$1, lcName)) ;
1120
+ if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$1, lcName)) ;
830
1121
  else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) ;
831
- else if (!nameIsPermitted || FORBID_ATTR[lcName]) {
1122
+ else if (!nameIsPermitted) {
832
1123
  if (
833
1124
  // First condition does a very basic check if a) it's basically a valid custom element tagname AND
834
1125
  // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
@@ -853,6 +1144,35 @@
853
1144
  const _isBasicCustomElement = function _isBasicCustomElement2(tagName) {
854
1145
  return !RESERVED_CUSTOM_ELEMENT_NAMES[stringToLowerCase(tagName)] && regExpTest(CUSTOM_ELEMENT$1, tagName);
855
1146
  };
1147
+ const _applyTrustedTypesToAttribute = function _applyTrustedTypesToAttribute2(lcTag, lcName, namespaceURI, value) {
1148
+ if (trustedTypesPolicy && typeof trustedTypes === "object" && typeof trustedTypes.getAttributeType === "function" && !namespaceURI) {
1149
+ switch (trustedTypes.getAttributeType(lcTag, lcName)) {
1150
+ case "TrustedHTML": {
1151
+ return _createTrustedHTML(value);
1152
+ }
1153
+ case "TrustedScriptURL": {
1154
+ return _createTrustedScriptURL(value);
1155
+ }
1156
+ }
1157
+ }
1158
+ return value;
1159
+ };
1160
+ const _setAttributeValue = function _setAttributeValue2(currentNode, name, namespaceURI, value) {
1161
+ try {
1162
+ if (namespaceURI) {
1163
+ currentNode.setAttributeNS(namespaceURI, name, value);
1164
+ } else {
1165
+ currentNode.setAttribute(name, value);
1166
+ }
1167
+ if (_isClobbered(currentNode)) {
1168
+ _forceRemove(currentNode);
1169
+ } else {
1170
+ arrayPop(DOMPurify.removed);
1171
+ }
1172
+ } catch (_) {
1173
+ _removeAttribute(name, currentNode);
1174
+ }
1175
+ };
856
1176
  const _sanitizeAttributes = function _sanitizeAttributes2(currentNode) {
857
1177
  _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
858
1178
  const attributes = currentNode.attributes;
@@ -867,6 +1187,7 @@
867
1187
  forceKeepAttr: void 0
868
1188
  };
869
1189
  let l = attributes.length;
1190
+ const lcTag = transformCaseFunc(currentNode.nodeName);
870
1191
  while (l--) {
871
1192
  const attr = attributes[l];
872
1193
  const name = attr.name, namespaceURI = attr.namespaceURI, attrValue = attr.value;
@@ -898,50 +1219,20 @@
898
1219
  _removeAttribute(name, currentNode);
899
1220
  continue;
900
1221
  }
901
- if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
1222
+ if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(SELF_CLOSING_TAG, value)) {
902
1223
  _removeAttribute(name, currentNode);
903
1224
  continue;
904
1225
  }
905
1226
  if (SAFE_FOR_TEMPLATES) {
906
- arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], (expr) => {
907
- value = stringReplace(value, expr, " ");
908
- });
1227
+ value = _stripTemplateExpressions(value);
909
1228
  }
910
- const lcTag = transformCaseFunc(currentNode.nodeName);
911
1229
  if (!_isValidAttribute(lcTag, lcName, value)) {
912
1230
  _removeAttribute(name, currentNode);
913
1231
  continue;
914
1232
  }
915
- if (trustedTypesPolicy && typeof trustedTypes === "object" && typeof trustedTypes.getAttributeType === "function") {
916
- if (namespaceURI) ;
917
- else {
918
- switch (trustedTypes.getAttributeType(lcTag, lcName)) {
919
- case "TrustedHTML": {
920
- value = trustedTypesPolicy.createHTML(value);
921
- break;
922
- }
923
- case "TrustedScriptURL": {
924
- value = trustedTypesPolicy.createScriptURL(value);
925
- break;
926
- }
927
- }
928
- }
929
- }
1233
+ value = _applyTrustedTypesToAttribute(lcTag, lcName, namespaceURI, value);
930
1234
  if (value !== initValue) {
931
- try {
932
- if (namespaceURI) {
933
- currentNode.setAttributeNS(namespaceURI, name, value);
934
- } else {
935
- currentNode.setAttribute(name, value);
936
- }
937
- if (_isClobbered(currentNode)) {
938
- _forceRemove(currentNode);
939
- } else {
940
- arrayPop(DOMPurify.removed);
941
- }
942
- } catch (_) {
943
- _removeAttribute(name, currentNode);
944
- }
1235
+ _setAttributeValue(currentNode, name, namespaceURI, value);
945
1236
  }
946
1237
  }
947
1238
  _executeHooks(hooks.afterSanitizeAttributes, currentNode, null);
@@ -952,30 +1243,69 @@
952
1243
  _executeHooks(hooks.beforeSanitizeShadowDOM, fragment, null);
953
1244
  while (shadowNode = shadowIterator.nextNode()) {
954
1245
  _executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);
955
- _sanitizeElements(shadowNode);
1246
+ _sanitizeElements(shadowNode, fragment);
956
1247
  _sanitizeAttributes(shadowNode);
957
- if (shadowNode.content instanceof DocumentFragment) {
1248
+ if (_isDocumentFragment(shadowNode.content)) {
958
1249
  _sanitizeShadowDOM2(shadowNode.content);
959
1250
  }
1251
+ const shadowNodeType = getNodeType ? getNodeType(shadowNode) : shadowNode.nodeType;
1252
+ if (shadowNodeType === NODE_TYPE.element) {
1253
+ const innerSr = getShadowRoot(shadowNode);
1254
+ if (_isDocumentFragment(innerSr)) {
1255
+ _sanitizeAttachedShadowRoots(innerSr);
1256
+ _sanitizeShadowDOM2(innerSr);
1257
+ }
1258
+ }
960
1259
  }
961
1260
  _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
962
1261
  };
963
- const _sanitizeAttachedShadowRoots2 = function _sanitizeAttachedShadowRoots(root) {
964
- if (root.nodeType === NODE_TYPE.element && root.shadowRoot instanceof DocumentFragment) {
965
- const sr = root.shadowRoot;
966
- _sanitizeAttachedShadowRoots2(sr);
967
- _sanitizeShadowDOM2(sr);
968
- }
969
- const childNodes = root.childNodes;
970
- if (!childNodes) {
971
- return;
972
- }
973
- const snapshot = [];
974
- arrayForEach(childNodes, (child) => {
975
- arrayPush(snapshot, child);
976
- });
977
- for (const child of snapshot) {
978
- _sanitizeAttachedShadowRoots2(child);
1262
+ const _sanitizeAttachedShadowRoots = function _sanitizeAttachedShadowRoots2(root) {
1263
+ const stack = [{
1264
+ node: root,
1265
+ shadow: null
1266
+ }];
1267
+ while (stack.length > 0) {
1268
+ const item = stack.pop();
1269
+ if (item.shadow) {
1270
+ _sanitizeShadowDOM2(item.shadow);
1271
+ continue;
1272
+ }
1273
+ const node = item.node;
1274
+ const nodeType = getNodeType ? getNodeType(node) : node.nodeType;
1275
+ const isElement = nodeType === NODE_TYPE.element;
1276
+ const childNodes = getChildNodes(node);
1277
+ if (childNodes) {
1278
+ for (let i = childNodes.length - 1; i >= 0; --i) {
1279
+ stack.push({
1280
+ node: childNodes[i],
1281
+ shadow: null
1282
+ });
1283
+ }
1284
+ }
1285
+ if (isElement) {
1286
+ const rootName = getNodeName ? getNodeName(node) : null;
1287
+ if (typeof rootName === "string" && transformCaseFunc(rootName) === "template") {
1288
+ const content = node.content;
1289
+ if (_isDocumentFragment(content)) {
1290
+ stack.push({
1291
+ node: content,
1292
+ shadow: null
1293
+ });
1294
+ }
1295
+ }
1296
+ }
1297
+ if (isElement) {
1298
+ const sr = getShadowRoot(node);
1299
+ if (_isDocumentFragment(sr)) {
1300
+ stack.push({
1301
+ node: null,
1302
+ shadow: sr
1303
+ }, {
1304
+ node: sr,
1305
+ shadow: null
1306
+ });
1307
+ }
1308
+ }
979
1309
  }
980
1310
  };
981
1311
  DOMPurify.sanitize = function(dirty) {
@@ -997,22 +1327,40 @@
997
1327
  if (!DOMPurify.isSupported) {
998
1328
  return dirty;
999
1329
  }
1000
- if (!SET_CONFIG) {
1330
+ if (SET_CONFIG) {
1331
+ ALLOWED_TAGS = SET_CONFIG_ALLOWED_TAGS;
1332
+ ALLOWED_ATTR = SET_CONFIG_ALLOWED_ATTR;
1333
+ } else {
1001
1334
  _parseConfig(cfg);
1002
1335
  }
1003
- DOMPurify.removed = [];
1004
- if (typeof dirty === "string") {
1005
- IN_PLACE = false;
1336
+ if (hooks.uponSanitizeElement.length > 0 || hooks.uponSanitizeAttribute.length > 0) {
1337
+ ALLOWED_TAGS = clone(ALLOWED_TAGS);
1006
1338
  }
1007
- if (IN_PLACE) {
1008
- const nn = dirty.nodeName;
1339
+ if (hooks.uponSanitizeAttribute.length > 0) {
1340
+ ALLOWED_ATTR = clone(ALLOWED_ATTR);
1341
+ }
1342
+ DOMPurify.removed = [];
1343
+ const inPlace = IN_PLACE && typeof dirty !== "string" && _isNode(dirty);
1344
+ if (inPlace) {
1345
+ _neutralizePatchLinkage(dirty);
1346
+ const nn = getNodeName ? getNodeName(dirty) : dirty.nodeName;
1009
1347
  if (typeof nn === "string") {
1010
1348
  const tagName = transformCaseFunc(nn);
1011
1349
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1350
+ _neutralizeRoot(dirty);
1012
1351
  throw typeErrorCreate("root node is forbidden and cannot be sanitized in-place");
1013
1352
  }
1014
1353
  }
1015
- _sanitizeAttachedShadowRoots2(dirty);
1354
+ if (_isClobbered(dirty)) {
1355
+ _neutralizeRoot(dirty);
1356
+ throw typeErrorCreate("root node is clobbered and cannot be sanitized in-place");
1357
+ }
1358
+ try {
1359
+ _sanitizeAttachedShadowRoots(dirty);
1360
+ } catch (error) {
1361
+ _neutralizeRoot(dirty);
1362
+ throw error;
1363
+ }
1016
1364
  } else if (_isNode(dirty)) {
1017
1365
  body = _initDocument("<!---->");
1018
1366
  importedNode = body.ownerDocument.importNode(dirty, true);
@@ -1023,11 +1371,11 @@
1023
1371
  } else {
1024
1372
  body.appendChild(importedNode);
1025
1373
  }
1026
- _sanitizeAttachedShadowRoots2(importedNode);
1374
+ _sanitizeAttachedShadowRoots(importedNode);
1027
1375
  } else {
1028
1376
  if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes
1029
1377
  dirty.indexOf("<") === -1) {
1030
- return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
1378
+ return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(dirty) : dirty;
1031
1379
  }
1032
1380
  body = _initDocument(dirty);
1033
1381
  if (!body) {
@@ -1037,23 +1385,41 @@
1037
1385
  if (body && FORCE_BODY) {
1038
1386
  _forceRemove(body.firstChild);
1039
1387
  }
1040
- const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);
1041
- while (currentNode = nodeIterator.nextNode()) {
1042
- _sanitizeElements(currentNode);
1043
- _sanitizeAttributes(currentNode);
1044
- if (currentNode.content instanceof DocumentFragment) {
1045
- _sanitizeShadowDOM2(currentNode.content);
1388
+ const walkRoot = inPlace ? dirty : body;
1389
+ const nodeIterator = _createNodeIterator(walkRoot);
1390
+ try {
1391
+ while (currentNode = nodeIterator.nextNode()) {
1392
+ _sanitizeElements(currentNode, walkRoot);
1393
+ _sanitizeAttributes(currentNode);
1394
+ if (_isDocumentFragment(currentNode.content)) {
1395
+ _sanitizeShadowDOM2(currentNode.content);
1396
+ }
1397
+ }
1398
+ } catch (error) {
1399
+ if (inPlace) {
1400
+ _neutralizeRoot(dirty);
1401
+ arrayForEach(DOMPurify.removed, (entry) => {
1402
+ if (entry.element) {
1403
+ _neutralizeSubtree(entry.element);
1404
+ }
1405
+ });
1046
1406
  }
1407
+ throw error;
1047
1408
  }
1048
- if (IN_PLACE) {
1409
+ if (inPlace) {
1410
+ arrayForEach(DOMPurify.removed, (entry) => {
1411
+ if (entry.element) {
1412
+ _neutralizeSubtree(entry.element);
1413
+ }
1414
+ });
1049
1415
  if (SAFE_FOR_TEMPLATES) {
1050
- _scrubTemplateExpressions(dirty);
1416
+ _scrubTemplateExpressions2(dirty);
1051
1417
  }
1052
1418
  return dirty;
1053
1419
  }
1054
1420
  if (RETURN_DOM) {
1055
1421
  if (SAFE_FOR_TEMPLATES) {
1056
- _scrubTemplateExpressions(body);
1422
+ _scrubTemplateExpressions2(body);
1057
1423
  }
1058
1424
  if (RETURN_DOM_FRAGMENT) {
1059
1425
  returnNode = createDocumentFragment.call(body.ownerDocument);
@@ -1073,20 +1439,24 @@
1073
1439
  serializedHTML = "<!DOCTYPE " + body.ownerDocument.doctype.name + ">\n" + serializedHTML;
1074
1440
  }
1075
1441
  if (SAFE_FOR_TEMPLATES) {
1076
- arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], (expr) => {
1077
- serializedHTML = stringReplace(serializedHTML, expr, " ");
1078
- });
1442
+ serializedHTML = _stripTemplateExpressions(serializedHTML);
1079
1443
  }
1080
- return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
1444
+ return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(serializedHTML) : serializedHTML;
1081
1445
  };
1082
1446
  DOMPurify.setConfig = function() {
1083
1447
  let cfg = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
1084
1448
  _parseConfig(cfg);
1085
1449
  SET_CONFIG = true;
1450
+ SET_CONFIG_ALLOWED_TAGS = ALLOWED_TAGS;
1451
+ SET_CONFIG_ALLOWED_ATTR = ALLOWED_ATTR;
1086
1452
  };
1087
1453
  DOMPurify.clearConfig = function() {
1088
1454
  CONFIG = null;
1089
1455
  SET_CONFIG = false;
1456
+ SET_CONFIG_ALLOWED_TAGS = null;
1457
+ SET_CONFIG_ALLOWED_ATTR = null;
1458
+ trustedTypesPolicy = defaultTrustedTypesPolicy;
1459
+ emptyHTML = "";
1090
1460
  };
1091
1461
  DOMPurify.isValidAttribute = function(tag, attr, value) {
1092
1462
  if (!CONFIG) {
@@ -1100,9 +1470,15 @@
1100
1470
  if (typeof hookFunction !== "function") {
1101
1471
  return;
1102
1472
  }
1473
+ if (!objectHasOwnProperty(hooks, entryPoint)) {
1474
+ return;
1475
+ }
1103
1476
  arrayPush(hooks[entryPoint], hookFunction);
1104
1477
  };
1105
1478
  DOMPurify.removeHook = function(entryPoint, hookFunction) {
1479
+ if (!objectHasOwnProperty(hooks, entryPoint)) {
1480
+ return void 0;
1481
+ }
1106
1482
  if (hookFunction !== void 0) {
1107
1483
  const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);
1108
1484
  return index === -1 ? void 0 : arraySplice(hooks[entryPoint], index, 1)[0];
@@ -1110,6 +1486,9 @@
1110
1486
  return arrayPop(hooks[entryPoint]);
1111
1487
  };
1112
1488
  DOMPurify.removeHooks = function(entryPoint) {
1489
+ if (!objectHasOwnProperty(hooks, entryPoint)) {
1490
+ return;
1491
+ }
1113
1492
  hooks[entryPoint] = [];
1114
1493
  };
1115
1494
  DOMPurify.removeAllHooks = function() {
@@ -1117,7 +1496,7 @@
1117
1496
  };
1118
1497
  return DOMPurify;
1119
1498
  }
1120
- var entries, setPrototypeOf, isFrozen, getPrototypeOf, getOwnPropertyDescriptor, freeze, seal, create, _ref, apply, construct, arrayForEach, arrayLastIndexOf, arrayPop, arrayPush, arraySplice, arrayIsArray, stringToLowerCase, stringToString, stringMatch, stringReplace, stringIndexOf, stringTrim, numberToString, booleanToString, bigintToString, symbolToString, objectHasOwnProperty, objectToString, regExpTest, typeErrorCreate, html$1, svg$1, svgFilters, svgDisallowed, mathMl$1, mathMlDisallowed, text, html, svg, mathMl, xml, MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR, DATA_ATTR, ARIA_ATTR, IS_ALLOWED_URI, IS_SCRIPT_OR_DATA, ATTR_WHITESPACE, DOCTYPE_NAME, CUSTOM_ELEMENT, NODE_TYPE, getGlobal, _createTrustedTypesPolicy, _createHooksMap, purify;
1499
+ var entries, setPrototypeOf, isFrozen, getPrototypeOf, getOwnPropertyDescriptor, freeze, seal, create, _ref, apply, construct, arrayForEach, arrayLastIndexOf, arrayPop, arrayPush, arraySplice, arrayIsArray, stringToLowerCase, stringToString, stringMatch, stringReplace, stringIndexOf, stringTrim, numberToString, booleanToString, bigintToString, symbolToString, objectHasOwnProperty, objectToString, regExpTest, typeErrorCreate, html$1, svg$1, svgFilters, svgDisallowed, mathMl$1, mathMlDisallowed, text, html, svg, mathMl, xml, MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR, DATA_ATTR, ARIA_ATTR, IS_ALLOWED_URI, IS_SCRIPT_OR_DATA, ATTR_WHITESPACE, DOCTYPE_NAME, CUSTOM_ELEMENT, ELEMENT_MARKUP_PROBE, COMMENT_MARKUP_PROBE, FALLBACK_TAG_CLOSE, SELF_CLOSING_TAG, NODE_TYPE, getGlobal, _createTrustedTypesPolicy, _createHooksMap, _resolveSetOption, purify;
1121
1500
  var init_purify_es = __esm({
1122
1501
  "node_modules/dompurify/dist/purify.es.mjs"() {
1123
1502
  entries = Object.entries;
@@ -1185,7 +1564,7 @@
1185
1564
  mathMlDisallowed = freeze(["maction", "maligngroup", "malignmark", "mlongdiv", "mscarries", "mscarry", "msgroup", "mstack", "msline", "msrow", "semantics", "annotation", "annotation-xml", "mprescripts", "none"]);
1186
1565
  text = freeze(["#text"]);
1187
1566
  html = freeze(["accept", "action", "align", "alt", "autocapitalize", "autocomplete", "autopictureinpicture", "autoplay", "background", "bgcolor", "border", "capture", "cellpadding", "cellspacing", "checked", "cite", "class", "clear", "color", "cols", "colspan", "command", "commandfor", "controls", "controlslist", "coords", "crossorigin", "datetime", "decoding", "default", "dir", "disabled", "disablepictureinpicture", "disableremoteplayback", "download", "draggable", "enctype", "enterkeyhint", "exportparts", "face", "for", "headers", "height", "hidden", "high", "href", "hreflang", "id", "inert", "inputmode", "integrity", "ismap", "kind", "label", "lang", "list", "loading", "loop", "low", "max", "maxlength", "media", "method", "min", "minlength", "multiple", "muted", "name", "nonce", "noshade", "novalidate", "nowrap", "open", "optimum", "part", "pattern", "placeholder", "playsinline", "popover", "popovertarget", "popovertargetaction", "poster", "preload", "pubdate", "radiogroup", "readonly", "rel", "required", "rev", "reversed", "role", "rows", "rowspan", "spellcheck", "scope", "selected", "shape", "size", "sizes", "slot", "span", "srclang", "start", "src", "srcset", "step", "style", "summary", "tabindex", "title", "translate", "type", "usemap", "valign", "value", "width", "wrap", "xmlns"]);
1188
- svg = freeze(["accent-height", "accumulate", "additive", "alignment-baseline", "amplitude", "ascent", "attributename", "attributetype", "azimuth", "basefrequency", "baseline-shift", "begin", "bias", "by", "class", "clip", "clippathunits", "clip-path", "clip-rule", "color", "color-interpolation", "color-interpolation-filters", "color-profile", "color-rendering", "cx", "cy", "d", "dx", "dy", "diffuseconstant", "direction", "display", "divisor", "dur", "edgemode", "elevation", "end", "exponent", "fill", "fill-opacity", "fill-rule", "filter", "filterunits", "flood-color", "flood-opacity", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "fx", "fy", "g1", "g2", "glyph-name", "glyphref", "gradientunits", "gradienttransform", "height", "href", "id", "image-rendering", "in", "in2", "intercept", "k", "k1", "k2", "k3", "k4", "kerning", "keypoints", "keysplines", "keytimes", "lang", "lengthadjust", "letter-spacing", "kernelmatrix", "kernelunitlength", "lighting-color", "local", "marker-end", "marker-mid", "marker-start", "markerheight", "markerunits", "markerwidth", "maskcontentunits", "maskunits", "max", "mask", "mask-type", "media", "method", "mode", "min", "name", "numoctaves", "offset", "operator", "opacity", "order", "orient", "orientation", "origin", "overflow", "paint-order", "path", "pathlength", "patterncontentunits", "patterntransform", "patternunits", "points", "preservealpha", "preserveaspectratio", "primitiveunits", "r", "rx", "ry", "radius", "refx", "refy", "repeatcount", "repeatdur", "restart", "result", "rotate", "scale", "seed", "shape-rendering", "slope", "specularconstant", "specularexponent", "spreadmethod", "startoffset", "stddeviation", "stitchtiles", "stop-color", "stop-opacity", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke", "stroke-width", "style", "surfacescale", "systemlanguage", "tabindex", "tablevalues", "targetx", "targety", "transform", "transform-origin", "text-anchor", "text-decoration", "text-rendering", "textlength", "type", "u1", "u2", "unicode", "values", "viewbox", "visibility", "version", "vert-adv-y", "vert-origin-x", "vert-origin-y", "width", "word-spacing", "wrap", "writing-mode", "xchannelselector", "ychannelselector", "x", "x1", "x2", "xmlns", "y", "y1", "y2", "z", "zoomandpan"]);
1567
+ svg = freeze(["accent-height", "accumulate", "additive", "alignment-baseline", "amplitude", "ascent", "attributename", "attributetype", "azimuth", "basefrequency", "baseline-shift", "begin", "bias", "by", "class", "clip", "clippathunits", "clip-path", "clip-rule", "color", "color-interpolation", "color-interpolation-filters", "color-profile", "color-rendering", "cx", "cy", "d", "dx", "dy", "diffuseconstant", "direction", "display", "divisor", "dominant-baseline", "dur", "edgemode", "elevation", "end", "exponent", "fill", "fill-opacity", "fill-rule", "filter", "filterunits", "flood-color", "flood-opacity", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "fx", "fy", "g1", "g2", "glyph-name", "glyphref", "gradientunits", "gradienttransform", "height", "href", "id", "image-rendering", "in", "in2", "intercept", "k", "k1", "k2", "k3", "k4", "kerning", "keypoints", "keysplines", "keytimes", "lang", "lengthadjust", "letter-spacing", "kernelmatrix", "kernelunitlength", "lighting-color", "local", "marker-end", "marker-mid", "marker-start", "markerheight", "markerunits", "markerwidth", "maskcontentunits", "maskunits", "max", "mask", "mask-type", "media", "method", "mode", "min", "name", "numoctaves", "offset", "operator", "opacity", "order", "orient", "orientation", "origin", "overflow", "paint-order", "path", "pathlength", "patterncontentunits", "patterntransform", "patternunits", "points", "preservealpha", "preserveaspectratio", "primitiveunits", "r", "rx", "ry", "radius", "refx", "refy", "repeatcount", "repeatdur", "restart", "result", "rotate", "scale", "seed", "shape-rendering", "slope", "specularconstant", "specularexponent", "spreadmethod", "startoffset", "stddeviation", "stitchtiles", "stop-color", "stop-opacity", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke", "stroke-width", "style", "surfacescale", "systemlanguage", "tabindex", "tablevalues", "targetx", "targety", "transform", "transform-origin", "text-anchor", "text-decoration", "text-orientation", "text-rendering", "textlength", "type", "u1", "u2", "unicode", "values", "viewbox", "visibility", "version", "vert-adv-y", "vert-origin-x", "vert-origin-y", "width", "word-spacing", "wrap", "writing-mode", "xchannelselector", "ychannelselector", "x", "x1", "x2", "xmlns", "y", "y1", "y2", "z", "zoomandpan"]);
1189
1568
  mathMl = freeze(["accent", "accentunder", "align", "bevelled", "close", "columnalign", "columnlines", "columnspacing", "columnspan", "denomalign", "depth", "dir", "display", "displaystyle", "encoding", "fence", "frame", "height", "href", "id", "largeop", "length", "linethickness", "lquote", "lspace", "mathbackground", "mathcolor", "mathsize", "mathvariant", "maxsize", "minsize", "movablelimits", "notation", "numalign", "open", "rowalign", "rowlines", "rowspacing", "rowspan", "rspace", "rquote", "scriptlevel", "scriptminsize", "scriptsizemultiplier", "selection", "separator", "separators", "stretchy", "subscriptshift", "supscriptshift", "symmetric", "voffset", "width", "xmlns"]);
1190
1569
  xml = freeze(["xlink:href", "xml:id", "xlink:title", "xml:space", "xmlns:xlink"]);
1191
1570
  MUSTACHE_EXPR = seal(/{{[\w\W]*|^[\w\W]*}}/g);
@@ -1204,13 +1583,26 @@
1204
1583
  );
1205
1584
  DOCTYPE_NAME = seal(/^html$/i);
1206
1585
  CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
1586
+ ELEMENT_MARKUP_PROBE = seal(/<[/\w!]/g);
1587
+ COMMENT_MARKUP_PROBE = seal(/<[/\w]/g);
1588
+ FALLBACK_TAG_CLOSE = seal(/<\/no(script|embed|frames)/i);
1589
+ SELF_CLOSING_TAG = seal(/\/>/i);
1207
1590
  NODE_TYPE = {
1208
1591
  element: 1,
1592
+ attribute: 2,
1209
1593
  text: 3,
1594
+ cdataSection: 4,
1595
+ entityReference: 5,
1210
1596
  // Deprecated
1211
- progressingInstruction: 7,
1597
+ entityNode: 6,
1598
+ // Deprecated
1599
+ processingInstruction: 7,
1212
1600
  comment: 8,
1213
- document: 9
1601
+ document: 9,
1602
+ documentType: 10,
1603
+ documentFragment: 11,
1604
+ notation: 12
1605
+ // Deprecated
1214
1606
  };
1215
1607
  getGlobal = function getGlobal2() {
1216
1608
  return typeof window === "undefined" ? null : window;
@@ -1252,6 +1644,9 @@
1252
1644
  uponSanitizeShadowNode: []
1253
1645
  };
1254
1646
  };
1647
+ _resolveSetOption = function _resolveSetOption2(cfg, key, fallback, options) {
1648
+ return objectHasOwnProperty(cfg, key) && arrayIsArray(cfg[key]) ? addToSet(options.base ? clone(options.base) : {}, cfg[key], options.transform) : fallback;
1649
+ };
1255
1650
  purify = createDOMPurify();
1256
1651
  }
1257
1652
  });
@@ -1541,7 +1936,6 @@
1541
1936
  this._previouslyFocused = saveFocus();
1542
1937
  setBackgroundInert(this);
1543
1938
  document.addEventListener("keydown", this._onKeydown);
1544
- document.body.style.overflow = "hidden";
1545
1939
  requestAnimationFrame(() => {
1546
1940
  const focusable = getFocusableElements(this.shadowRoot);
1547
1941
  if (focusable.length > 0) focusable[0].focus();
@@ -1549,8 +1943,7 @@
1549
1943
  }
1550
1944
  _close() {
1551
1945
  document.removeEventListener("keydown", this._onKeydown);
1552
- document.body.style.overflow = "";
1553
- clearBackgroundInert();
1946
+ clearBackgroundInert(this);
1554
1947
  restoreFocus(this._previouslyFocused);
1555
1948
  }
1556
1949
  _onKeydown(event) {
@@ -1562,7 +1955,7 @@
1562
1955
  }
1563
1956
  disconnectedCallback() {
1564
1957
  document.removeEventListener("keydown", this._onKeydown);
1565
- document.body.style.overflow = "";
1958
+ clearBackgroundInert(this);
1566
1959
  }
1567
1960
  };
1568
1961
  customElements.define("velin-modal", VelinModal);
@@ -1751,26 +2144,36 @@
1751
2144
  __export(velin_accordion_exports, {
1752
2145
  default: () => velin_accordion_default
1753
2146
  });
1754
- var styles3, VelinAccordion, velin_accordion_default;
2147
+ function ensureLightStyles() {
2148
+ if (typeof document === "undefined") return;
2149
+ if (document.getElementById(LIGHT_STYLE_ID)) return;
2150
+ const style = document.createElement("style");
2151
+ style.id = LIGHT_STYLE_ID;
2152
+ style.textContent = lightStyles;
2153
+ document.head.appendChild(style);
2154
+ }
2155
+ var LIGHT_STYLE_ID, lightStyles, shadowStyles, VelinAccordion, velin_accordion_default;
1755
2156
  var init_velin_accordion = __esm({
1756
2157
  "components/velin-accordion.js"() {
1757
- styles3 = `
1758
- :host {
2158
+ LIGHT_STYLE_ID = "velin-accordion-light-css";
2159
+ lightStyles = `
2160
+ velin-accordion {
1759
2161
  display: block;
1760
2162
  border: 1px solid var(--velin-color-border, #ddd);
1761
2163
  border-radius: var(--velin-radius-md, 0.5rem);
1762
2164
  overflow: hidden;
1763
2165
  }
1764
- ::slotted(details) {
2166
+ velin-accordion details {
1765
2167
  border-bottom: 1px solid var(--velin-color-border, #ddd);
1766
2168
  }
1767
- ::slotted(details:last-child) {
2169
+ velin-accordion details:last-child {
1768
2170
  border-bottom: none;
1769
2171
  }
1770
- ::slotted(details > summary) {
2172
+ velin-accordion details > summary {
1771
2173
  display: flex;
1772
2174
  align-items: center;
1773
2175
  justify-content: space-between;
2176
+ gap: var(--velin-space-3, 0.75rem);
1774
2177
  padding: var(--velin-space-4, 1rem);
1775
2178
  min-block-size: 2.75rem;
1776
2179
  font-size: var(--velin-text-base, 1rem);
@@ -1779,37 +2182,57 @@
1779
2182
  user-select: none;
1780
2183
  list-style: none;
1781
2184
  }
1782
- ::slotted(details > summary::-webkit-details-marker) {
2185
+ velin-accordion details > summary::-webkit-details-marker {
1783
2186
  display: none;
1784
2187
  }
1785
- ::slotted(details[open] > summary) {
2188
+ velin-accordion details > summary::after {
2189
+ content: "";
2190
+ flex-shrink: 0;
2191
+ inline-size: 0.5rem;
2192
+ block-size: 0.5rem;
2193
+ border-inline-end: 2px solid currentColor;
2194
+ border-block-end: 2px solid currentColor;
2195
+ transform: rotate(45deg);
2196
+ transition: transform 150ms ease;
2197
+ }
2198
+ velin-accordion details[open] > summary {
1786
2199
  background: var(--velin-color-primary-subtle, #eff6ff);
1787
2200
  color: var(--velin-color-primary, #2563eb);
1788
2201
  }
1789
- ::slotted(details > :not(summary)) {
2202
+ velin-accordion details[open] > summary::after {
2203
+ transform: rotate(225deg);
2204
+ }
2205
+ velin-accordion details > :not(summary) {
1790
2206
  padding: var(--velin-space-4, 1rem) var(--velin-space-5, 1.25rem);
1791
- background: var(--velin-color-bg-subtle, #f8fafc);
2207
+ background: var(--velin-color-surface-dim, var(--velin-color-bg-subtle, #f8fafc));
1792
2208
  color: var(--velin-color-text-muted, #64748b);
1793
2209
  font-size: var(--velin-text-sm, 0.875rem);
1794
2210
  line-height: 1.6;
1795
2211
  border-block-start: 1px solid var(--velin-color-border, #e2e8f0);
1796
2212
  }
2213
+ `;
2214
+ shadowStyles = `
2215
+ :host {
2216
+ display: block;
2217
+ }
1797
2218
  `;
1798
2219
  VelinAccordion = class extends HTMLElement {
1799
2220
  constructor() {
1800
2221
  super();
1801
2222
  this.attachShadow({ mode: "open" });
1802
2223
  this._onToggle = this._onToggle.bind(this);
2224
+ this._onKeydown = this._onKeydown.bind(this);
1803
2225
  }
1804
2226
  connectedCallback() {
2227
+ ensureLightStyles();
1805
2228
  this.shadowRoot.innerHTML = `
1806
- <style>${styles3}</style>
2229
+ <style>${shadowStyles}</style>
1807
2230
  <slot></slot>
1808
2231
  `;
1809
2232
  this._exclusive = this.hasAttribute("exclusive");
1810
2233
  this._wireDetails();
1811
2234
  this.addEventListener("toggle", this._onToggle, true);
1812
- this.addEventListener("keydown", this._onKeydown.bind(this));
2235
+ this.addEventListener("keydown", this._onKeydown);
1813
2236
  }
1814
2237
  _wireDetails() {
1815
2238
  let panelIndex = 0;
@@ -1820,13 +2243,21 @@
1820
2243
  if (panel && !panel.id) panel.id = panelId;
1821
2244
  if (summary && panel) {
1822
2245
  summary.setAttribute("aria-controls", panelId);
2246
+ summary.setAttribute("aria-expanded", details.open ? "true" : "false");
2247
+ }
2248
+ if (summary && !summary.hasAttribute("tabindex")) {
2249
+ summary.setAttribute("tabindex", "0");
1823
2250
  }
1824
2251
  }
1825
2252
  }
1826
2253
  _onToggle(event) {
1827
- if (!this._exclusive) return;
1828
2254
  const openedDetail = event.target;
1829
- if (!openedDetail.open) return;
2255
+ if (!(openedDetail instanceof HTMLDetailsElement)) return;
2256
+ const summary = openedDetail.querySelector("summary");
2257
+ if (summary) {
2258
+ summary.setAttribute("aria-expanded", openedDetail.open ? "true" : "false");
2259
+ }
2260
+ if (!this._exclusive || !openedDetail.open) return;
1830
2261
  const details = [...this.querySelectorAll("details")];
1831
2262
  details.forEach((d) => {
1832
2263
  if (d !== openedDetail && d.open) {
@@ -1856,6 +2287,9 @@
1856
2287
  event.preventDefault();
1857
2288
  nextIndex = summaries.length - 1;
1858
2289
  break;
2290
+ case "Enter":
2291
+ case " ":
2292
+ return;
1859
2293
  default:
1860
2294
  return;
1861
2295
  }
@@ -1863,6 +2297,7 @@
1863
2297
  }
1864
2298
  disconnectedCallback() {
1865
2299
  this.removeEventListener("toggle", this._onToggle, true);
2300
+ this.removeEventListener("keydown", this._onKeydown);
1866
2301
  }
1867
2302
  };
1868
2303
  customElements.define("velin-accordion", VelinAccordion);
@@ -1875,11 +2310,11 @@
1875
2310
  __export(velin_tabs_exports, {
1876
2311
  default: () => velin_tabs_default
1877
2312
  });
1878
- var styles4, VelinTabs, velin_tabs_default;
2313
+ var styles3, VelinTabs, velin_tabs_default;
1879
2314
  var init_velin_tabs = __esm({
1880
2315
  "components/velin-tabs.js"() {
1881
2316
  init_focus_manager();
1882
- styles4 = `
2317
+ styles3 = `
1883
2318
  :host {
1884
2319
  display: block;
1885
2320
  }
@@ -1931,7 +2366,7 @@
1931
2366
  }
1932
2367
  connectedCallback() {
1933
2368
  this.shadowRoot.innerHTML = `
1934
- <style>${styles4}</style>
2369
+ <style>${styles3}</style>
1935
2370
  <div class="tablist" role="tablist" part="tablist">
1936
2371
  <slot name="tab"></slot>
1937
2372
  </div>
@@ -2017,11 +2452,11 @@
2017
2452
  __export(velin_toast_exports, {
2018
2453
  default: () => velin_toast_default
2019
2454
  });
2020
- var styles5, VelinToast, velin_toast_default;
2455
+ var styles4, VelinToast, velin_toast_default;
2021
2456
  var init_velin_toast = __esm({
2022
2457
  "components/velin-toast.js"() {
2023
2458
  init_sanitize();
2024
- styles5 = `
2459
+ styles4 = `
2025
2460
  :host {
2026
2461
  position: fixed;
2027
2462
  z-index: var(--velin-z-toast, 600);
@@ -2091,7 +2526,7 @@
2091
2526
  this._container = null;
2092
2527
  }
2093
2528
  connectedCallback() {
2094
- this.shadowRoot.innerHTML = `<style>${styles5}</style>`;
2529
+ this.shadowRoot.innerHTML = `<style>${styles4}</style>`;
2095
2530
  this._container = this.shadowRoot;
2096
2531
  this.setAttribute("role", "status");
2097
2532
  this.setAttribute("aria-live", "polite");
@@ -2139,7 +2574,18 @@
2139
2574
  if (variant && variants?.[variant]) return variants[variant];
2140
2575
  return PROVIDER_CDNS[provider];
2141
2576
  }
2142
- var PROVIDER_CDNS, PROVIDER_VARIANTS, _svgCache, VelinIcon, velin_icon_default;
2577
+ function resolveDefaultSpriteUrl() {
2578
+ if (typeof document !== "undefined") {
2579
+ const meta = document.querySelector('meta[name="velin-icon-sprite"]');
2580
+ const fromMeta = meta?.getAttribute("content")?.trim();
2581
+ if (fromMeta) return fromMeta;
2582
+ const fromHtml = document.documentElement?.getAttribute("data-velin-icon-sprite")?.trim();
2583
+ if (fromHtml) return fromHtml;
2584
+ }
2585
+ const configured = typeof VelinIcon.defaultSprite === "string" ? VelinIcon.defaultSprite.trim() : "";
2586
+ return configured || DEFAULT_SPRITE;
2587
+ }
2588
+ var PROVIDER_CDNS, PROVIDER_VARIANTS, _svgCache, DEFAULT_SPRITE, VelinIcon, velin_icon_default;
2143
2589
  var init_velin_icon = __esm({
2144
2590
  "components/velin-icon.js"() {
2145
2591
  init_sanitize();
@@ -2163,7 +2609,10 @@
2163
2609
  }
2164
2610
  };
2165
2611
  _svgCache = /* @__PURE__ */ new Map();
2612
+ DEFAULT_SPRITE = "velin-icons.svg";
2166
2613
  VelinIcon = class extends HTMLElement {
2614
+ /** @type {string} Relative or absolute sprite URL used when `sprite` attribute is omitted. */
2615
+ static defaultSprite = DEFAULT_SPRITE;
2167
2616
  static get observedAttributes() {
2168
2617
  return ["name", "size", "label", "provider", "variant", "sprite"];
2169
2618
  }
@@ -2219,7 +2668,8 @@
2219
2668
  if (spriteAttr === "" || spriteAttr == null && isLocalSymbol) {
2220
2669
  href = `#${name}`;
2221
2670
  } else {
2222
- const spriteUrl = sanitizeURL(spriteAttr || "/dist/velin-icons.svg") || "/dist/velin-icons.svg";
2671
+ const fallback = resolveDefaultSpriteUrl();
2672
+ const spriteUrl = sanitizeURL(spriteAttr || fallback) || fallback || DEFAULT_SPRITE;
2223
2673
  href = `${spriteUrl}#${name}`;
2224
2674
  }
2225
2675
  use.setAttribute("href", href);
@@ -2328,12 +2778,12 @@
2328
2778
  __export(velin_drawer_exports, {
2329
2779
  default: () => velin_drawer_default
2330
2780
  });
2331
- var styles6, VelinDrawer, velin_drawer_default;
2781
+ var styles5, VelinDrawer, velin_drawer_default;
2332
2782
  var init_velin_drawer = __esm({
2333
2783
  "components/velin-drawer.js"() {
2334
2784
  init_focus_manager();
2335
2785
  init_sanitize();
2336
- styles6 = `
2786
+ styles5 = `
2337
2787
  :host { display: contents; }
2338
2788
  .overlay {
2339
2789
  position: fixed;
@@ -2394,7 +2844,7 @@
2394
2844
  const safeTitle = escapeHTML(title);
2395
2845
  const titleId = "velin-drawer-title";
2396
2846
  this.shadowRoot.innerHTML = `
2397
- <style>${styles6}</style>
2847
+ <style>${styles5}</style>
2398
2848
  <div class="overlay" part="overlay"></div>
2399
2849
  <div class="drawer" role="dialog" aria-modal="true" aria-labelledby="${titleId}" part="drawer">
2400
2850
  <div class="header" part="header">
@@ -2421,7 +2871,6 @@
2421
2871
  this._prev = saveFocus();
2422
2872
  setBackgroundInert(this);
2423
2873
  document.addEventListener("keydown", this._onKey);
2424
- document.body.style.overflow = "hidden";
2425
2874
  requestAnimationFrame(() => {
2426
2875
  const f = getFocusableElements(this.shadowRoot);
2427
2876
  if (f.length) f[0].focus();
@@ -2429,8 +2878,7 @@
2429
2878
  }
2430
2879
  _close() {
2431
2880
  document.removeEventListener("keydown", this._onKey);
2432
- document.body.style.overflow = "";
2433
- clearBackgroundInert();
2881
+ clearBackgroundInert(this);
2434
2882
  restoreFocus(this._prev);
2435
2883
  }
2436
2884
  _onKey(e) {
@@ -2442,7 +2890,7 @@
2442
2890
  }
2443
2891
  disconnectedCallback() {
2444
2892
  document.removeEventListener("keydown", this._onKey);
2445
- document.body.style.overflow = "";
2893
+ clearBackgroundInert(this);
2446
2894
  }
2447
2895
  };
2448
2896
  customElements.define("velin-drawer", VelinDrawer);
@@ -2470,7 +2918,7 @@
2470
2918
  document.head.appendChild(link);
2471
2919
  loadedThemeStylesheets.add(slug);
2472
2920
  }
2473
- var THEMES, BUILTIN_THEMES, loadedThemeStylesheets, styles7, VelinThemeToggle, velin_theme_toggle_default;
2921
+ var THEMES, BUILTIN_THEMES, loadedThemeStylesheets, styles6, VelinThemeToggle, velin_theme_toggle_default;
2474
2922
  var init_velin_theme_toggle = __esm({
2475
2923
  "components/velin-theme-toggle.js"() {
2476
2924
  THEMES = [
@@ -2492,7 +2940,7 @@
2492
2940
  ];
2493
2941
  BUILTIN_THEMES = /* @__PURE__ */ new Set(["", "dark"]);
2494
2942
  loadedThemeStylesheets = /* @__PURE__ */ new Set();
2495
- styles7 = `
2943
+ styles6 = `
2496
2944
  :host { display: inline-flex; position: relative; }
2497
2945
  .group {
2498
2946
  display: inline-flex; align-items: stretch;
@@ -2581,7 +3029,7 @@
2581
3029
  }
2582
3030
  connectedCallback() {
2583
3031
  this.shadowRoot.innerHTML = `
2584
- <style>${styles7}</style>
3032
+ <style>${styles6}</style>
2585
3033
  <div class="group" part="group">
2586
3034
  <button class="toggle" part="button" aria-label="Toggle dark mode" aria-pressed="false">
2587
3035
  <svg class="sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true" focusable="false">
@@ -2753,12 +3201,12 @@
2753
3201
  __export(velin_popover_exports, {
2754
3202
  default: () => velin_popover_default
2755
3203
  });
2756
- var styles8, popoverId, VelinPopover, velin_popover_default;
3204
+ var styles7, popoverId, VelinPopover, velin_popover_default;
2757
3205
  var init_velin_popover = __esm({
2758
3206
  "components/velin-popover.js"() {
2759
3207
  init_sanitize();
2760
3208
  init_focus_manager();
2761
- styles8 = `
3209
+ styles7 = `
2762
3210
  :host { position: relative; display: inline-block; }
2763
3211
  .popover {
2764
3212
  position: absolute; z-index: var(--velin-z-dropdown, 200);
@@ -2806,7 +3254,7 @@
2806
3254
  this._isDialog = role === "dialog";
2807
3255
  const titleId = title ? `${this._popoverId}-title` : "";
2808
3256
  this.shadowRoot.innerHTML = `
2809
- <style>${styles8}</style>
3257
+ <style>${styles7}</style>
2810
3258
  <slot name="trigger"></slot>
2811
3259
  <div class="popover popover--${placement}" id="${this._popoverId}" role="${role}" part="popover"${titleId ? ` aria-labelledby="${titleId}"` : ""}>
2812
3260
  ${title ? `<div class="popover__title" id="${titleId}" part="title">${escapeHTML(title)}</div>` : ""}
@@ -2924,11 +3372,11 @@
2924
3372
  __export(velin_copy_exports, {
2925
3373
  default: () => velin_copy_default
2926
3374
  });
2927
- var styles9, VelinCopy, velin_copy_default;
3375
+ var styles8, VelinCopy, velin_copy_default;
2928
3376
  var init_velin_copy = __esm({
2929
3377
  "components/velin-copy.js"() {
2930
3378
  init_sanitize();
2931
- styles9 = `
3379
+ styles8 = `
2932
3380
  :host { display: inline-flex; }
2933
3381
  button {
2934
3382
  display: inline-flex; align-items: center; justify-content: center; gap: 0.375rem;
@@ -2957,7 +3405,7 @@
2957
3405
  connectedCallback() {
2958
3406
  const label = escapeHTML(this.getAttribute("label") || "");
2959
3407
  this.shadowRoot.innerHTML = `
2960
- <style>${styles9}</style>
3408
+ <style>${styles8}</style>
2961
3409
  <button part="button" aria-label="Copy">
2962
3410
  <svg class="copy-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
2963
3411
  <rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/>
@@ -3009,10 +3457,10 @@
3009
3457
  __export(velin_scroll_top_exports, {
3010
3458
  default: () => velin_scroll_top_default
3011
3459
  });
3012
- var styles10, VelinScrollTop, velin_scroll_top_default;
3460
+ var styles9, VelinScrollTop, velin_scroll_top_default;
3013
3461
  var init_velin_scroll_top = __esm({
3014
3462
  "components/velin-scroll-top.js"() {
3015
- styles10 = `
3463
+ styles9 = `
3016
3464
  :host { position: fixed; inset-block-end: var(--velin-scroll-top-bottom, var(--velin-space-4, 1rem)); inset-inline-end: var(--velin-scroll-top-end, var(--velin-space-4, 1rem)); z-index: var(--velin-z-fixed, 300); }
3017
3465
  button {
3018
3466
  display: inline-flex; align-items: center; justify-content: center;
@@ -3039,7 +3487,7 @@
3039
3487
  const threshold = parseInt(this.getAttribute("threshold") || "300", 10);
3040
3488
  this._threshold = threshold;
3041
3489
  this.shadowRoot.innerHTML = `
3042
- <style>${styles10}</style>
3490
+ <style>${styles9}</style>
3043
3491
  <button part="button" aria-label="Scroll to top">
3044
3492
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
3045
3493
  <line x1="12" y1="19" x2="12" y2="5"/><polyline points="5 12 12 5 19 12"/>
@@ -3074,10 +3522,10 @@
3074
3522
  __export(velin_carousel_exports, {
3075
3523
  default: () => velin_carousel_default
3076
3524
  });
3077
- var styles11, VelinCarousel, velin_carousel_default;
3525
+ var styles10, VelinCarousel, velin_carousel_default;
3078
3526
  var init_velin_carousel = __esm({
3079
3527
  "components/velin-carousel.js"() {
3080
- styles11 = `
3528
+ styles10 = `
3081
3529
  :host { display: block; position: relative; overflow: hidden; }
3082
3530
  .track {
3083
3531
  display: flex; transition: transform 400ms ease;
@@ -3143,7 +3591,7 @@
3143
3591
  }
3144
3592
  connectedCallback() {
3145
3593
  this.shadowRoot.innerHTML = `
3146
- <style>${styles11}</style>
3594
+ <style>${styles10}</style>
3147
3595
  <div class="track" role="group" aria-roledescription="carousel" part="track"><slot></slot></div>
3148
3596
  <div class="controls" part="controls">
3149
3597
  <button class="prev" aria-label="Previous slide" part="prev">
@@ -3298,10 +3746,10 @@
3298
3746
  const tag = el.tagName;
3299
3747
  return tag === "BUTTON" || tag === "A" && el.hasAttribute("href") || el.getAttribute("role") === "button";
3300
3748
  }
3301
- var styles12, collapseId, VelinCollapse, velin_collapse_default;
3749
+ var styles11, collapseId, VelinCollapse, velin_collapse_default;
3302
3750
  var init_velin_collapse = __esm({
3303
3751
  "components/velin-collapse.js"() {
3304
- styles12 = `
3752
+ styles11 = `
3305
3753
  :host { display: block; }
3306
3754
  .content {
3307
3755
  overflow: hidden;
@@ -3326,7 +3774,7 @@
3326
3774
  }
3327
3775
  connectedCallback() {
3328
3776
  const panelId = this._contentId;
3329
- this.shadowRoot.innerHTML = "<style>" + styles12 + '</style><slot name="trigger"></slot><div class="content" id="' + panelId + '" part="content"><div class="inner"><slot></slot></div></div>';
3777
+ this.shadowRoot.innerHTML = "<style>" + styles11 + '</style><slot name="trigger"></slot><div class="content" id="' + panelId + '" part="content"><div class="inner"><slot></slot></div></div>';
3330
3778
  const triggerSlot = this.shadowRoot.querySelector('slot[name="trigger"]');
3331
3779
  triggerSlot.addEventListener("slotchange", () => this._wireTrigger());
3332
3780
  this._wireTrigger();
@@ -3440,11 +3888,11 @@
3440
3888
  VelinTooltipWC: () => VelinTooltipWC,
3441
3889
  default: () => velin_tooltip_default
3442
3890
  });
3443
- var styles13, tooltipId, VelinTooltip, VelinTooltipWC, velin_tooltip_default;
3891
+ var styles12, tooltipId, VelinTooltip, VelinTooltipWC, velin_tooltip_default;
3444
3892
  var init_velin_tooltip = __esm({
3445
3893
  "components/velin-tooltip.js"() {
3446
3894
  init_sanitize();
3447
- styles13 = `
3895
+ styles12 = `
3448
3896
  :host { position: relative; display: inline-block; }
3449
3897
  .tip {
3450
3898
  position: absolute; z-index: var(--velin-z-tooltip, 700);
@@ -3478,7 +3926,7 @@
3478
3926
  const placement = this.getAttribute("placement") || "top";
3479
3927
  const D = "div";
3480
3928
  this.shadowRoot.innerHTML = `
3481
- <style>${styles13}</style>
3929
+ <style>${styles12}</style>
3482
3930
  <slot></slot>
3483
3931
  <${D} class="tip" id="${this._tipId}" role="tooltip" data-placement="${escapeHTML(placement)}" part="tip">${escapeHTML(this.getAttribute("content") || "")}</${D}>
3484
3932
  `;
@@ -3610,13 +4058,13 @@
3610
4058
  __export(velin_lightbox_exports, {
3611
4059
  default: () => velin_lightbox_default
3612
4060
  });
3613
- var styles14, VelinLightbox, velin_lightbox_default;
4061
+ var styles13, VelinLightbox, velin_lightbox_default;
3614
4062
  var init_velin_lightbox = __esm({
3615
4063
  "components/velin-lightbox.js"() {
3616
4064
  init_focus_manager();
3617
4065
  init_a11y_utils();
3618
4066
  init_sanitize();
3619
- styles14 = `
4067
+ styles13 = `
3620
4068
  :host { display: contents; }
3621
4069
  .overlay {
3622
4070
  position: fixed; inset: 0; z-index: var(--velin-z-modal, 500);
@@ -3669,7 +4117,7 @@
3669
4117
  }
3670
4118
  connectedCallback() {
3671
4119
  this.shadowRoot.innerHTML = `
3672
- <style>${styles14}</style>
4120
+ <style>${styles13}</style>
3673
4121
  <slot></slot>
3674
4122
  <div class="overlay" role="dialog" aria-modal="true" aria-label="Image lightbox" aria-roledescription="lightbox" part="overlay">
3675
4123
  <button class="nav nav--prev" aria-label="Previous">&#8249;</button>
@@ -3705,7 +4153,6 @@
3705
4153
  this.setAttribute("open", "");
3706
4154
  setBackgroundInert(this);
3707
4155
  this._render();
3708
- document.body.style.overflow = "hidden";
3709
4156
  const overlay = this.shadowRoot.querySelector(".overlay");
3710
4157
  overlay.removeEventListener("keydown", this._onTrapKey);
3711
4158
  overlay.addEventListener("keydown", this._onTrapKey);
@@ -3713,14 +4160,16 @@
3713
4160
  }
3714
4161
  close() {
3715
4162
  this.removeAttribute("open");
3716
- clearBackgroundInert();
3717
- document.body.style.overflow = "";
4163
+ clearBackgroundInert(this);
3718
4164
  if (this._previousFocus) {
3719
4165
  this._previousFocus.focus();
3720
4166
  this._previousFocus = null;
3721
4167
  }
3722
4168
  this.dispatchEvent(new CustomEvent("velin-close", { bubbles: true }));
3723
4169
  }
4170
+ disconnectedCallback() {
4171
+ clearBackgroundInert(this);
4172
+ }
3724
4173
  _prev() {
3725
4174
  this._index = (this._index - 1 + this._items.length) % this._items.length;
3726
4175
  this._render();
@@ -3758,11 +4207,11 @@
3758
4207
  VelinStepperWC: () => VelinStepperWC,
3759
4208
  default: () => velin_stepper_default
3760
4209
  });
3761
- var styles15, VelinStepper, VelinStepperWC, velin_stepper_default;
4210
+ var styles14, VelinStepper, VelinStepperWC, velin_stepper_default;
3762
4211
  var init_velin_stepper = __esm({
3763
4212
  "components/velin-stepper.js"() {
3764
4213
  init_sanitize();
3765
- styles15 = `
4214
+ styles14 = `
3766
4215
  :host { display: block; }
3767
4216
  .steps {
3768
4217
  display: flex; gap: var(--velin-space-2, 0.5rem);
@@ -3841,7 +4290,7 @@
3841
4290
  return `<div class="step ${state}" role="listitem" aria-label="${escapeHTML(itemLabel)}"${ariaCurrent}><span class="step__marker">${marker}</span><span class="step__label">${escapeHTML(label)}</span></div>`;
3842
4291
  }).join("");
3843
4292
  this.shadowRoot.innerHTML = `
3844
- <style>${styles15}</style>
4293
+ <style>${styles14}</style>
3845
4294
  <div class="steps" role="list" aria-label="${escapeHTML(listLabel)}" part="steps">${stepsHTML}</div>
3846
4295
  <div class="panels" part="panels"><slot></slot></div>
3847
4296
  `;
@@ -3908,11 +4357,11 @@
3908
4357
  __export(velin_dialog_exports, {
3909
4358
  default: () => velin_dialog_default
3910
4359
  });
3911
- var styles16, VelinDialog, velin_dialog_default;
4360
+ var styles15, VelinDialog, velin_dialog_default;
3912
4361
  var init_velin_dialog = __esm({
3913
4362
  "components/velin-dialog.js"() {
3914
4363
  init_sanitize();
3915
- styles16 = `
4364
+ styles15 = `
3916
4365
  :host { display: contents; }
3917
4366
  dialog {
3918
4367
  border: none; border-radius: var(--velin-radius-lg, 0.75rem);
@@ -3978,7 +4427,7 @@
3978
4427
  this._previousFocus = null;
3979
4428
  }
3980
4429
  connectedCallback() {
3981
- this.shadowRoot.innerHTML = `<style>${styles16}</style><dialog part="dialog"></dialog>`;
4430
+ this.shadowRoot.innerHTML = `<style>${styles15}</style><dialog part="dialog"></dialog>`;
3982
4431
  const dialog = this.shadowRoot.querySelector("dialog");
3983
4432
  if (dialog) {
3984
4433
  dialog.setAttribute("aria-label", this.getAttribute("aria-label") || "Dialog");
@@ -4051,7 +4500,9 @@
4051
4500
  }
4052
4501
  this._resolve(value);
4053
4502
  this._resolve = null;
4054
- this.dispatchEvent(new CustomEvent("velin-dialog-close", { bubbles: true, detail: { value } }));
4503
+ const detail = { value };
4504
+ this.dispatchEvent(new CustomEvent("velin-close", { bubbles: true, detail }));
4505
+ this.dispatchEvent(new CustomEvent("velin-dialog-close", { bubbles: true, detail }));
4055
4506
  }
4056
4507
  };
4057
4508
  customElements.define("velin-dialog", VelinDialog);
@@ -4064,11 +4515,11 @@
4064
4515
  __export(velin_countdown_exports, {
4065
4516
  default: () => velin_countdown_default
4066
4517
  });
4067
- var styles17, VelinCountdown, velin_countdown_default;
4518
+ var styles16, VelinCountdown, velin_countdown_default;
4068
4519
  var init_velin_countdown = __esm({
4069
4520
  "components/velin-countdown.js"() {
4070
4521
  init_sanitize();
4071
- styles17 = `
4522
+ styles16 = `
4072
4523
  :host { display: inline-flex; font-variant-numeric: tabular-nums; }
4073
4524
  .wrap { display: inline-flex; gap: var(--velin-space-3, 0.75rem); align-items: flex-start; }
4074
4525
  .segment {
@@ -4109,7 +4560,7 @@
4109
4560
  this._timer = null;
4110
4561
  }
4111
4562
  connectedCallback() {
4112
- this.shadowRoot.innerHTML = `<style>${styles17}</style><div class="wrap"></div>`;
4563
+ this.shadowRoot.innerHTML = `<style>${styles16}</style><div class="wrap"></div>`;
4113
4564
  this._wrap = this.shadowRoot.querySelector(".wrap");
4114
4565
  this.setAttribute("role", "timer");
4115
4566
  this.setAttribute("aria-live", "polite");
@@ -4169,10 +4620,10 @@
4169
4620
  __export(velin_progress_ring_exports, {
4170
4621
  default: () => velin_progress_ring_default
4171
4622
  });
4172
- var styles18, VelinProgressRing, velin_progress_ring_default;
4623
+ var styles17, VelinProgressRing, velin_progress_ring_default;
4173
4624
  var init_velin_progress_ring = __esm({
4174
4625
  "components/velin-progress-ring.js"() {
4175
- styles18 = `
4626
+ styles17 = `
4176
4627
  :host { display: inline-flex; align-items: center; justify-content: center; }
4177
4628
  svg { transform: rotate(-90deg); }
4178
4629
  .track { fill: none; stroke: var(--velin-color-border, #e5e5e5); }
@@ -4213,7 +4664,7 @@
4213
4664
  const colorStyle = color ? `stroke: var(--velin-color-${color}, ${color});` : "";
4214
4665
  const showLabel = this.getAttribute("label") !== "false";
4215
4666
  this.shadowRoot.innerHTML = `
4216
- <style>${styles18}</style>
4667
+ <style>${styles17}</style>
4217
4668
  <svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" part="svg" role="progressbar" aria-valuenow="${value}" aria-valuemin="0" aria-valuemax="100">
4218
4669
  <circle class="track" cx="${size / 2}" cy="${size / 2}" r="${radius}" stroke-width="${strokeWidth}" />
4219
4670
  <circle class="fill" cx="${size / 2}" cy="${size / 2}" r="${radius}" stroke-width="${strokeWidth}"
@@ -4359,12 +4810,12 @@
4359
4810
  __export(velin_combobox_exports, {
4360
4811
  default: () => velin_combobox_default
4361
4812
  });
4362
- var styles19, VelinCombobox, velin_combobox_default;
4813
+ var styles18, VelinCombobox, velin_combobox_default;
4363
4814
  var init_velin_combobox = __esm({
4364
4815
  "components/velin-combobox.js"() {
4365
4816
  init_focus_manager();
4366
4817
  init_sanitize();
4367
- styles19 = `
4818
+ styles18 = `
4368
4819
  :host { display: inline-block; position: relative; }
4369
4820
  .listbox {
4370
4821
  position: absolute; z-index: var(--velin-z-dropdown, 100);
@@ -4405,7 +4856,7 @@
4405
4856
  this._listId = listId;
4406
4857
  const listLabel = escapeHTML(this.getAttribute("aria-label") || "Options");
4407
4858
  this.shadowRoot.innerHTML = `
4408
- <style>${styles19}</style>
4859
+ <style>${styles18}</style>
4409
4860
  <slot name="trigger"></slot>
4410
4861
  <div class="listbox" id="${listId}" role="listbox" aria-label="${listLabel}" part="listbox"><slot></slot></div>
4411
4862
  `;
@@ -4516,11 +4967,11 @@
4516
4967
  __export(velin_bottom_nav_exports, {
4517
4968
  default: () => velin_bottom_nav_default
4518
4969
  });
4519
- var styles20, VelinBottomNav, velin_bottom_nav_default;
4970
+ var styles19, VelinBottomNav, velin_bottom_nav_default;
4520
4971
  var init_velin_bottom_nav = __esm({
4521
4972
  "components/velin-bottom-nav.js"() {
4522
4973
  init_sanitize();
4523
- styles20 = `
4974
+ styles19 = `
4524
4975
  :host { display: block; }
4525
4976
  nav {
4526
4977
  display: flex;
@@ -4560,7 +5011,7 @@
4560
5011
  connectedCallback() {
4561
5012
  const label = escapeHTML(this.getAttribute("aria-label") || "Bottom navigation");
4562
5013
  this.shadowRoot.innerHTML = `
4563
- <style>${styles20}</style>
5014
+ <style>${styles19}</style>
4564
5015
  <nav role="navigation" aria-label="${label}"><slot></slot></nav>
4565
5016
  `;
4566
5017
  const slot = this.shadowRoot.querySelector("slot");
@@ -4582,6 +5033,16 @@
4582
5033
  }
4583
5034
  if (el.tagName === "A" && !el.getAttribute("href")) {
4584
5035
  el.setAttribute("role", "button");
5036
+ if (!el.hasAttribute("tabindex")) el.setAttribute("tabindex", "0");
5037
+ if (!el._velinBottomNavKey) {
5038
+ el._velinBottomNavKey = (e) => {
5039
+ if (e.key === "Enter" || e.key === " ") {
5040
+ e.preventDefault();
5041
+ el.click();
5042
+ }
5043
+ };
5044
+ el.addEventListener("keydown", el._velinBottomNavKey);
5045
+ }
4585
5046
  }
4586
5047
  });
4587
5048
  }
@@ -4645,13 +5106,13 @@
4645
5106
  __export(velin_sheet_exports, {
4646
5107
  default: () => velin_sheet_default
4647
5108
  });
4648
- var styles21, VelinSheet, velin_sheet_default;
5109
+ var styles20, VelinSheet, velin_sheet_default;
4649
5110
  var init_velin_sheet = __esm({
4650
5111
  "components/velin-sheet.js"() {
4651
5112
  init_focus_manager();
4652
5113
  init_sanitize();
4653
5114
  init_shadow_a11y_styles();
4654
- styles21 = `
5115
+ styles20 = `
4655
5116
  ${SHADOW_A11Y_STYLES}
4656
5117
  :host { display: contents; }
4657
5118
  .overlay {
@@ -4697,7 +5158,7 @@
4697
5158
  const title = escapeHTML(this.getAttribute("title") || this.getAttribute("label") || "");
4698
5159
  const titleId = "velin-sheet-title";
4699
5160
  this.shadowRoot.innerHTML = `
4700
- <style>${styles21}</style>
5161
+ <style>${styles20}</style>
4701
5162
  <div class="overlay" part="overlay"></div>
4702
5163
  <div class="sheet" role="dialog" aria-modal="true" aria-labelledby="${titleId}" part="sheet">
4703
5164
  <div class="header" part="header">
@@ -4725,7 +5186,6 @@
4725
5186
  this._prev = saveFocus();
4726
5187
  setBackgroundInert(this);
4727
5188
  document.addEventListener("keydown", this._onKey);
4728
- document.body.style.overflow = "hidden";
4729
5189
  requestAnimationFrame(() => {
4730
5190
  const f = getFocusableElements(this.shadowRoot);
4731
5191
  if (f.length) f[0].focus();
@@ -4733,8 +5193,7 @@
4733
5193
  }
4734
5194
  _close() {
4735
5195
  document.removeEventListener("keydown", this._onKey);
4736
- document.body.style.overflow = "";
4737
- clearBackgroundInert();
5196
+ clearBackgroundInert(this);
4738
5197
  restoreFocus(this._prev);
4739
5198
  }
4740
5199
  _onKey(e) {
@@ -4746,7 +5205,7 @@
4746
5205
  }
4747
5206
  disconnectedCallback() {
4748
5207
  document.removeEventListener("keydown", this._onKey);
4749
- document.body.style.overflow = "";
5208
+ clearBackgroundInert(this);
4750
5209
  }
4751
5210
  };
4752
5211
  customElements.define("velin-sheet", VelinSheet);
@@ -4759,13 +5218,13 @@
4759
5218
  __export(velin_segmented_control_exports, {
4760
5219
  default: () => velin_segmented_control_default
4761
5220
  });
4762
- var styles22, VelinSegmentedControl, velin_segmented_control_default;
5221
+ var styles21, VelinSegmentedControl, velin_segmented_control_default;
4763
5222
  var init_velin_segmented_control = __esm({
4764
5223
  "components/velin-segmented-control.js"() {
4765
5224
  init_focus_manager();
4766
5225
  init_sanitize();
4767
5226
  init_shadow_a11y_styles();
4768
- styles22 = `
5227
+ styles21 = `
4769
5228
  ${SHADOW_A11Y_STYLES}
4770
5229
  :host { display: block; }
4771
5230
  .group {
@@ -4803,7 +5262,7 @@
4803
5262
  connectedCallback() {
4804
5263
  const label = escapeHTML(this.getAttribute("aria-label") || "Segmented control");
4805
5264
  this.shadowRoot.innerHTML = `
4806
- <style>${styles22}</style>
5265
+ <style>${styles21}</style>
4807
5266
  <div class="group" role="group" aria-label="${label}"><slot></slot></div>
4808
5267
  `;
4809
5268
  this.addEventListener("click", this._onClick);
@@ -4868,13 +5327,13 @@
4868
5327
  __export(velin_rating_exports, {
4869
5328
  default: () => velin_rating_default
4870
5329
  });
4871
- var styles23, MAX, VelinRating, velin_rating_default;
5330
+ var styles22, MAX, VelinRating, velin_rating_default;
4872
5331
  var init_velin_rating = __esm({
4873
5332
  "components/velin-rating.js"() {
4874
5333
  init_focus_manager();
4875
5334
  init_sanitize();
4876
5335
  init_shadow_a11y_styles();
4877
- styles23 = `
5336
+ styles22 = `
4878
5337
  ${SHADOW_A11Y_STYLES}
4879
5338
  :host { display: inline-block; }
4880
5339
  .stars { display: inline-flex; gap: var(--velin-space-1, 0.25rem); }
@@ -4897,7 +5356,7 @@
4897
5356
  this._onKey = this._onKey.bind(this);
4898
5357
  }
4899
5358
  connectedCallback() {
4900
- this.shadowRoot.innerHTML = `<style>${styles23}</style><div class="stars" role="radiogroup"></div>`;
5359
+ this.shadowRoot.innerHTML = `<style>${styles22}</style><div class="stars" role="radiogroup"></div>`;
4901
5360
  this._render();
4902
5361
  this.shadowRoot.querySelector(".stars").addEventListener("click", this._onClick);
4903
5362
  this.shadowRoot.querySelector(".stars").addEventListener("keydown", this._onKey);
@@ -4959,13 +5418,13 @@
4959
5418
  __export(velin_menubar_exports, {
4960
5419
  default: () => velin_menubar_default
4961
5420
  });
4962
- var styles24, VelinMenubar, velin_menubar_default;
5421
+ var styles23, VelinMenubar, velin_menubar_default;
4963
5422
  var init_velin_menubar = __esm({
4964
5423
  "components/velin-menubar.js"() {
4965
5424
  init_focus_manager();
4966
5425
  init_sanitize();
4967
5426
  init_shadow_a11y_styles();
4968
- styles24 = `
5427
+ styles23 = `
4969
5428
  ${SHADOW_A11Y_STYLES}
4970
5429
  :host { display: block; }
4971
5430
  .menubar {
@@ -5000,7 +5459,7 @@
5000
5459
  connectedCallback() {
5001
5460
  const label = escapeHTML(this.getAttribute("aria-label") || "Menu bar");
5002
5461
  this.shadowRoot.innerHTML = `
5003
- <style>${styles24}</style>
5462
+ <style>${styles23}</style>
5004
5463
  <div class="menubar" role="menubar" aria-label="${label}"><slot></slot></div>
5005
5464
  `;
5006
5465
  this.addEventListener("keydown", this._onKey);
@@ -5045,13 +5504,13 @@
5045
5504
  __export(velin_command_exports, {
5046
5505
  default: () => velin_command_default
5047
5506
  });
5048
- var styles25, VelinCommand, velin_command_default;
5507
+ var styles24, VelinCommand, velin_command_default;
5049
5508
  var init_velin_command = __esm({
5050
5509
  "components/velin-command.js"() {
5051
5510
  init_focus_manager();
5052
5511
  init_sanitize();
5053
5512
  init_shadow_a11y_styles();
5054
- styles25 = `
5513
+ styles24 = `
5055
5514
  ${SHADOW_A11Y_STYLES}
5056
5515
  :host { display: contents; }
5057
5516
  .overlay {
@@ -5060,9 +5519,14 @@
5060
5519
  padding: 10vh var(--velin-space-4, 1rem) var(--velin-space-4, 1rem);
5061
5520
  background: var(--velin-color-overlay, rgba(0,0,0,0.4));
5062
5521
  opacity: 0; visibility: hidden;
5522
+ pointer-events: none;
5063
5523
  transition: opacity 150ms ease, visibility 150ms ease;
5064
5524
  }
5065
- :host([open]) .overlay { opacity: 1; visibility: visible; }
5525
+ :host([open]) .overlay {
5526
+ opacity: 1;
5527
+ visibility: visible;
5528
+ pointer-events: auto;
5529
+ }
5066
5530
  .panel {
5067
5531
  inline-size: min(32rem, 100%);
5068
5532
  background: var(--velin-color-surface-bright, #fff);
@@ -5105,7 +5569,7 @@
5105
5569
  connectedCallback() {
5106
5570
  const placeholder = escapeHTML(this.getAttribute("placeholder") || "Search commands\u2026");
5107
5571
  this.shadowRoot.innerHTML = `
5108
- <style>${styles25}</style>
5572
+ <style>${styles24}</style>
5109
5573
  <div class="overlay" part="overlay">
5110
5574
  <div class="panel" role="dialog" aria-modal="true" aria-label="Command palette" part="panel">
5111
5575
  <input class="search" type="search" autocomplete="off" placeholder="${placeholder}" aria-label="Search" part="search" />
@@ -5114,6 +5578,7 @@
5114
5578
  </div>
5115
5579
  `;
5116
5580
  this.shadowRoot.querySelector(".search").addEventListener("input", this._onInput);
5581
+ this.shadowRoot.querySelector(".overlay")?.addEventListener("click", this._onOverlayClick);
5117
5582
  this.shadowRoot.querySelector("slot")?.addEventListener("slotchange", () => this._filter(""));
5118
5583
  this._filter("");
5119
5584
  }
@@ -5127,6 +5592,13 @@
5127
5592
  this.removeAttribute("open");
5128
5593
  this.dispatchEvent(new CustomEvent("velin-close", { bubbles: true }));
5129
5594
  }
5595
+ toggle() {
5596
+ if (this.hasAttribute("open")) this.close();
5597
+ else this.open();
5598
+ }
5599
+ _onOverlayClick = (event) => {
5600
+ if (event.target === event.currentTarget) this.close();
5601
+ };
5130
5602
  _open() {
5131
5603
  this._prev = saveFocus();
5132
5604
  setBackgroundInert(this);
@@ -5138,7 +5610,7 @@
5138
5610
  }
5139
5611
  _close() {
5140
5612
  document.removeEventListener("keydown", this._onKey);
5141
- clearBackgroundInert();
5613
+ clearBackgroundInert(this);
5142
5614
  restoreFocus(this._prev);
5143
5615
  const input = this.shadowRoot.querySelector(".search");
5144
5616
  if (input) input.value = "";
@@ -5177,6 +5649,8 @@
5177
5649
  }
5178
5650
  disconnectedCallback() {
5179
5651
  document.removeEventListener("keydown", this._onKey);
5652
+ this.shadowRoot?.querySelector(".overlay")?.removeEventListener("click", this._onOverlayClick);
5653
+ if (this.hasAttribute("open")) clearBackgroundInert(this);
5180
5654
  }
5181
5655
  };
5182
5656
  customElements.define("velin-command", VelinCommand);
@@ -5189,17 +5663,17 @@
5189
5663
  __export(velin_announcer_exports, {
5190
5664
  default: () => velin_announcer_default
5191
5665
  });
5192
- var styles26, VelinAnnouncer, velin_announcer_default;
5666
+ var styles25, VelinAnnouncer, velin_announcer_default;
5193
5667
  var init_velin_announcer = __esm({
5194
5668
  "components/velin-announcer.js"() {
5195
- styles26 = `
5669
+ styles25 = `
5196
5670
  :host { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
5197
5671
  `;
5198
5672
  VelinAnnouncer = class extends HTMLElement {
5199
5673
  connectedCallback() {
5200
5674
  if (!this.shadowRoot) this.attachShadow({ mode: "open" });
5201
5675
  const live = this.getAttribute("polite") === "false" ? "assertive" : "polite";
5202
- this.shadowRoot.innerHTML = "<style>" + styles26 + '</style><div role="status" aria-live="' + live + '" aria-atomic="true" part="region"></div>';
5676
+ this.shadowRoot.innerHTML = "<style>" + styles25 + '</style><div role="status" aria-live="' + live + '" aria-atomic="true" part="region"></div>';
5203
5677
  this._region = this.shadowRoot.querySelector('[role="status"]');
5204
5678
  }
5205
5679
  announce(message, { assertive = false } = {}) {
@@ -5243,11 +5717,11 @@
5243
5717
  return raw;
5244
5718
  }
5245
5719
  }
5246
- var styles27, VelinEmail, velin_email_default;
5720
+ var styles26, VelinEmail, velin_email_default;
5247
5721
  var init_velin_email = __esm({
5248
5722
  "components/velin-email.js"() {
5249
5723
  init_sanitize();
5250
- styles27 = `
5724
+ styles26 = `
5251
5725
  :host { display: inline; }
5252
5726
  button {
5253
5727
  font: inherit;
@@ -5294,13 +5768,13 @@
5294
5768
  const safeEmail = escapeHTML(email);
5295
5769
  if (this._revealed) {
5296
5770
  this.shadowRoot.innerHTML = `
5297
- <style>${styles27}</style>
5771
+ <style>${styles26}</style>
5298
5772
  <span class="revealed" part="email"><a href="mailto:${escapeHTMLAttribute(email)}">${safeEmail}</a></span>
5299
5773
  `;
5300
5774
  return;
5301
5775
  }
5302
5776
  this.shadowRoot.innerHTML = `
5303
- <style>${styles27}</style>
5777
+ <style>${styles26}</style>
5304
5778
  <button type="button" part="reveal" aria-label="${escapeHTMLAttribute(label)}">${escapeHTML(label)}</button>
5305
5779
  `;
5306
5780
  this.shadowRoot.querySelector("button").addEventListener("click", () => {
@@ -5317,6 +5791,227 @@
5317
5791
  }
5318
5792
  });
5319
5793
 
5794
+ // components/velin-calendar.js
5795
+ var velin_calendar_exports = {};
5796
+ __export(velin_calendar_exports, {
5797
+ default: () => velin_calendar_default
5798
+ });
5799
+ function iso(d) {
5800
+ const y = d.getFullYear();
5801
+ const m = String(d.getMonth() + 1).padStart(2, "0");
5802
+ const day = String(d.getDate()).padStart(2, "0");
5803
+ return `${y}-${m}-${day}`;
5804
+ }
5805
+ function parseISO(v) {
5806
+ if (!v) return null;
5807
+ const m = String(v).match(/^(\d{4})-(\d{2})-(\d{2})$/);
5808
+ if (!m) return null;
5809
+ const d = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
5810
+ return Number.isNaN(d.getTime()) ? null : d;
5811
+ }
5812
+ var styles27, VelinCalendar, velin_calendar_default;
5813
+ var init_velin_calendar = __esm({
5814
+ "components/velin-calendar.js"() {
5815
+ init_sanitize();
5816
+ styles27 = `
5817
+ :host { display: block; max-inline-size: 100%; }
5818
+ .cal {
5819
+ box-sizing: border-box;
5820
+ border: 1px solid var(--velin-color-border, #ddd);
5821
+ border-radius: var(--velin-radius-md, 0.5rem);
5822
+ background: var(--velin-color-surface, #fff);
5823
+ color: var(--velin-color-text, #111);
5824
+ padding: var(--velin-space-3, 0.75rem);
5825
+ max-inline-size: min(20rem, 100%);
5826
+ inline-size: 100%;
5827
+ }
5828
+ .head {
5829
+ display: flex; align-items: center; justify-content: space-between;
5830
+ gap: var(--velin-space-2, 0.5rem); margin-block-end: var(--velin-space-3, 0.75rem);
5831
+ }
5832
+ .title { font-weight: var(--velin-weight-semibold, 600); margin: 0; font-size: var(--velin-text-base, 1rem); }
5833
+ .nav {
5834
+ display: inline-flex; align-items: center; justify-content: center;
5835
+ flex-shrink: 0;
5836
+ min-inline-size: 2.5rem; min-block-size: 2.5rem;
5837
+ border: 1px solid var(--velin-color-border, #ddd);
5838
+ border-radius: var(--velin-radius-sm, 0.25rem);
5839
+ background: var(--velin-color-surface-bright, #fff);
5840
+ color: inherit; cursor: pointer;
5841
+ }
5842
+ .nav:focus-visible { outline: 2px solid var(--velin-color-focus, #2563eb); outline-offset: 2px; }
5843
+ .grid {
5844
+ display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 0.25rem;
5845
+ }
5846
+ .dow {
5847
+ text-align: center; font-size: var(--velin-text-xs, 0.75rem);
5848
+ color: var(--velin-color-text-muted, #64748b); padding-block: 0.25rem;
5849
+ }
5850
+ .day {
5851
+ inline-size: 100%;
5852
+ min-inline-size: 0;
5853
+ min-block-size: 2.25rem;
5854
+ aspect-ratio: 1;
5855
+ border: none; border-radius: var(--velin-radius-sm, 0.25rem);
5856
+ background: transparent; color: inherit; cursor: pointer;
5857
+ font: inherit;
5858
+ padding: 0;
5859
+ }
5860
+ .day[aria-disabled="true"] { opacity: 0.35; cursor: not-allowed; }
5861
+ .day[aria-selected="true"] {
5862
+ background: var(--velin-color-primary, #2563eb);
5863
+ color: var(--velin-color-on-primary, #fff);
5864
+ }
5865
+ .day:not([aria-disabled="true"]):hover {
5866
+ background: var(--velin-color-primary-subtle, #eff6ff);
5867
+ }
5868
+ .day:focus-visible { outline: 2px solid var(--velin-color-focus, #2563eb); outline-offset: 1px; }
5869
+ .out { color: var(--velin-color-text-muted, #94a3b8); }
5870
+ `;
5871
+ VelinCalendar = class extends HTMLElement {
5872
+ static get observedAttributes() {
5873
+ return ["value", "min", "max", "label"];
5874
+ }
5875
+ constructor() {
5876
+ super();
5877
+ this.attachShadow({ mode: "open" });
5878
+ this._view = /* @__PURE__ */ new Date();
5879
+ this._view.setDate(1);
5880
+ this._onKey = this._onKey.bind(this);
5881
+ }
5882
+ connectedCallback() {
5883
+ const selected = parseISO(this.getAttribute("value")) || /* @__PURE__ */ new Date();
5884
+ this._view = new Date(selected.getFullYear(), selected.getMonth(), 1);
5885
+ this._render();
5886
+ }
5887
+ attributeChangedCallback(name, prev, next) {
5888
+ if (prev === next || !this.shadowRoot?.querySelector(".cal")) return;
5889
+ if (name === "value" && next) {
5890
+ const d = parseISO(next);
5891
+ if (d) this._view = new Date(d.getFullYear(), d.getMonth(), 1);
5892
+ }
5893
+ this._render();
5894
+ }
5895
+ get value() {
5896
+ return this.getAttribute("value") || "";
5897
+ }
5898
+ set value(v) {
5899
+ if (v) this.setAttribute("value", v);
5900
+ else this.removeAttribute("value");
5901
+ }
5902
+ _inRange(d) {
5903
+ const min = parseISO(this.getAttribute("min"));
5904
+ const max = parseISO(this.getAttribute("max"));
5905
+ if (min && d < min) return false;
5906
+ if (max && d > max) return false;
5907
+ return true;
5908
+ }
5909
+ _select(d) {
5910
+ if (!this._inRange(d)) return;
5911
+ const v = iso(d);
5912
+ this.setAttribute("value", v);
5913
+ this.dispatchEvent(new CustomEvent("velin-change", { bubbles: true, detail: { value: v, date: d } }));
5914
+ this._render();
5915
+ }
5916
+ _shiftMonth(delta) {
5917
+ this._view = new Date(this._view.getFullYear(), this._view.getMonth() + delta, 1);
5918
+ this._render();
5919
+ }
5920
+ _onKey(e) {
5921
+ const selected = parseISO(this.getAttribute("value")) || /* @__PURE__ */ new Date();
5922
+ let next = new Date(selected);
5923
+ switch (e.key) {
5924
+ case "ArrowLeft":
5925
+ next.setDate(next.getDate() - 1);
5926
+ break;
5927
+ case "ArrowRight":
5928
+ next.setDate(next.getDate() + 1);
5929
+ break;
5930
+ case "ArrowUp":
5931
+ next.setDate(next.getDate() - 7);
5932
+ break;
5933
+ case "ArrowDown":
5934
+ next.setDate(next.getDate() + 7);
5935
+ break;
5936
+ case "Home":
5937
+ next = new Date(next.getFullYear(), next.getMonth(), 1);
5938
+ break;
5939
+ case "End":
5940
+ next = new Date(next.getFullYear(), next.getMonth() + 1, 0);
5941
+ break;
5942
+ case "PageUp":
5943
+ next = new Date(next.getFullYear(), next.getMonth() - 1, next.getDate());
5944
+ break;
5945
+ case "PageDown":
5946
+ next = new Date(next.getFullYear(), next.getMonth() + 1, next.getDate());
5947
+ break;
5948
+ case "Enter":
5949
+ case " ":
5950
+ e.preventDefault();
5951
+ this._select(selected);
5952
+ return;
5953
+ default:
5954
+ return;
5955
+ }
5956
+ e.preventDefault();
5957
+ if (!this._inRange(next)) return;
5958
+ this._view = new Date(next.getFullYear(), next.getMonth(), 1);
5959
+ this.setAttribute("value", iso(next));
5960
+ this.dispatchEvent(new CustomEvent("velin-change", { bubbles: true, detail: { value: iso(next), date: next } }));
5961
+ this._render();
5962
+ this.shadowRoot.querySelector(`[data-iso="${iso(next)}"]`)?.focus();
5963
+ }
5964
+ _render() {
5965
+ const label = escapeHTML(this.getAttribute("label") || "Choose date");
5966
+ const selected = parseISO(this.getAttribute("value"));
5967
+ const y = this._view.getFullYear();
5968
+ const m = this._view.getMonth();
5969
+ const title = this._view.toLocaleString(void 0, { month: "long", year: "numeric" });
5970
+ const start = new Date(y, m, 1);
5971
+ const startDow = (start.getDay() + 6) % 7;
5972
+ const daysInMonth = new Date(y, m + 1, 0).getDate();
5973
+ const dows = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
5974
+ let cells = dows.map((d) => `<div class="dow" aria-hidden="true">${d}</div>`).join("");
5975
+ for (let i = 0; i < startDow; i += 1) {
5976
+ const d = new Date(y, m, -startDow + i + 1);
5977
+ cells += `<button type="button" class="day out" tabindex="-1" data-iso="${iso(d)}" aria-label="${iso(d)}">${d.getDate()}</button>`;
5978
+ }
5979
+ for (let day = 1; day <= daysInMonth; day += 1) {
5980
+ const d = new Date(y, m, day);
5981
+ const id = iso(d);
5982
+ const sel = selected && iso(selected) === id;
5983
+ const disabled = !this._inRange(d);
5984
+ cells += `<button type="button" class="day" data-iso="${id}" aria-label="${id}" aria-selected="${sel ? "true" : "false"}" aria-disabled="${disabled ? "true" : "false"}" tabindex="${sel ? "0" : "-1"}">${day}</button>`;
5985
+ }
5986
+ this.shadowRoot.innerHTML = `
5987
+ <style>${styles27}</style>
5988
+ <div class="cal" role="group" aria-label="${label}">
5989
+ <div class="head">
5990
+ <button type="button" class="nav" data-nav="-1" aria-label="Previous month">\u2039</button>
5991
+ <p class="title" id="cal-title">${escapeHTML(title)}</p>
5992
+ <button type="button" class="nav" data-nav="1" aria-label="Next month">\u203A</button>
5993
+ </div>
5994
+ <div class="grid" role="grid" aria-labelledby="cal-title">${cells}</div>
5995
+ </div>
5996
+ `;
5997
+ this.shadowRoot.querySelectorAll("[data-nav]").forEach((btn) => {
5998
+ btn.addEventListener("click", () => this._shiftMonth(Number(btn.getAttribute("data-nav"))));
5999
+ });
6000
+ this.shadowRoot.querySelectorAll(".day").forEach((btn) => {
6001
+ btn.addEventListener("click", () => {
6002
+ if (btn.getAttribute("aria-disabled") === "true") return;
6003
+ const d = parseISO(btn.getAttribute("data-iso"));
6004
+ if (d) this._select(d);
6005
+ });
6006
+ btn.addEventListener("keydown", this._onKey);
6007
+ });
6008
+ }
6009
+ };
6010
+ customElements.define("velin-calendar", VelinCalendar);
6011
+ velin_calendar_default = VelinCalendar;
6012
+ }
6013
+ });
6014
+
5320
6015
  // core/highlight/languages/_utils.js
5321
6016
  function tokenize(code, rules) {
5322
6017
  const tokens = [];
@@ -5706,6 +6401,146 @@
5706
6401
  }
5707
6402
  });
5708
6403
 
6404
+ // core/highlight/languages/python.js
6405
+ var python_exports = {};
6406
+ __export(python_exports, {
6407
+ default: () => lexPython
6408
+ });
6409
+ function lexPython(code) {
6410
+ return tokenize(code, RULES9);
6411
+ }
6412
+ var RULES9;
6413
+ var init_python = __esm({
6414
+ "core/highlight/languages/python.js"() {
6415
+ init_utils();
6416
+ RULES9 = [
6417
+ { type: "comment", re: /#[^\n]*/y },
6418
+ // Triple-quoted strings first so the single-quote rules cannot split them.
6419
+ { type: "string", re: /[rbfu]{0,2}"""[\s\S]*?"""/y },
6420
+ { type: "string", re: /[rbfu]{0,2}'''[\s\S]*?'''/y },
6421
+ { type: "string", re: /[rbfu]{0,2}"(?:\\.|[^"\\\n])*"/y },
6422
+ { type: "string", re: /[rbfu]{0,2}'(?:\\.|[^'\\\n])*'/y },
6423
+ { type: "number", re: /\b0[xX][\da-fA-F_]+\b|\b\d[\d_]*(?:\.[\d_]+)?(?:[eE][+-]?\d+)?j?\b/y },
6424
+ {
6425
+ type: "keyword",
6426
+ re: /\b(?:and|as|assert|async|await|break|class|continue|def|del|elif|else|except|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|raise|return|try|while|with|yield)\b/y
6427
+ },
6428
+ {
6429
+ type: "builtin",
6430
+ re: /\b(?:None|True|False|self|cls|abs|all|any|bool|bytes|dict|dir|enumerate|filter|float|format|frozenset|getattr|hasattr|int|isinstance|issubclass|iter|len|list|map|max|min|next|object|open|print|range|repr|reversed|round|set|setattr|sorted|str|sum|super|tuple|type|zip)\b/y
6431
+ },
6432
+ // Multi-character operators first so `->` is not split into `-` and `>`.
6433
+ { type: "operator", re: /->|:=|\*\*=?|\/\/=?|<<=?|>>=?|[-+*/%&|^~<>!=]=?/y },
6434
+ { type: "punctuation", re: /[[\]{}(),:;.@]/y },
6435
+ { type: "identifier", re: /\b[A-Za-z_]\w*\b/y }
6436
+ ];
6437
+ }
6438
+ });
6439
+
6440
+ // core/highlight/languages/yaml.js
6441
+ var yaml_exports = {};
6442
+ __export(yaml_exports, {
6443
+ default: () => lexYaml
6444
+ });
6445
+ function lexYaml(code) {
6446
+ return tokenize(code, RULES10);
6447
+ }
6448
+ var RULES10;
6449
+ var init_yaml = __esm({
6450
+ "core/highlight/languages/yaml.js"() {
6451
+ init_utils();
6452
+ RULES10 = [
6453
+ { type: "comment", re: /#[^\n]*/y },
6454
+ // Document markers and block scalar indicators.
6455
+ { type: "punctuation", re: /^(?:---|\.\.\.)$/my },
6456
+ { type: "string", re: /"(?:\\.|[^"\\])*"/y },
6457
+ { type: "string", re: /'(?:''|[^'])*'/y },
6458
+ // Keys are the primary structure in YAML, so they get the attr-name colour.
6459
+ { type: "attr-name", re: /^[ \t]*-?[ \t]*[\w.$-]+(?=[ \t]*:(?:[ \t]|$))/my },
6460
+ { type: "punctuation", re: /^[ \t]*-(?=[ \t]|$)/my },
6461
+ { type: "keyword", re: /\b(?:true|false|null|yes|no|on|off|~)\b/yi },
6462
+ { type: "number", re: /\b-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\b/y },
6463
+ // Anchors, aliases, tags and block scalar headers.
6464
+ { type: "operator", re: /[&*]\w+|![\w/!-]*|[|>][+-]?\d*(?=\s*$)/my },
6465
+ { type: "punctuation", re: /[:,[\]{}]/y }
6466
+ ];
6467
+ }
6468
+ });
6469
+
6470
+ // core/highlight/languages/go.js
6471
+ var go_exports = {};
6472
+ __export(go_exports, {
6473
+ default: () => lexGo
6474
+ });
6475
+ function lexGo(code) {
6476
+ return tokenize(code, RULES11);
6477
+ }
6478
+ var RULES11;
6479
+ var init_go = __esm({
6480
+ "core/highlight/languages/go.js"() {
6481
+ init_utils();
6482
+ RULES11 = [
6483
+ { type: "comment", re: /\/\/[^\n]*/y },
6484
+ { type: "comment", re: /\/\*[\s\S]*?\*\//y },
6485
+ // Raw strings may span lines.
6486
+ { type: "string", re: /`[^`]*`/y },
6487
+ { type: "string", re: /"(?:\\.|[^"\\\n])*"/y },
6488
+ { type: "string", re: /'(?:\\.|[^'\\\n])*'/y },
6489
+ { type: "number", re: /\b0[xX][\da-fA-F_]+\b|\b\d[\d_]*(?:\.[\d_]*)?(?:[eE][+-]?\d+)?i?\b/y },
6490
+ {
6491
+ type: "keyword",
6492
+ re: /\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go|goto|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/y
6493
+ },
6494
+ {
6495
+ type: "builtin",
6496
+ re: /\b(?:append|bool|byte|cap|clear|close|complex|complex64|complex128|copy|delete|error|float32|float64|imag|int|int8|int16|int32|int64|len|make|max|min|new|nil|panic|print|println|real|recover|rune|string|true|false|iota|uint|uint8|uint16|uint32|uint64|uintptr|any)\b/y
6497
+ },
6498
+ { type: "operator", re: /:=|\.\.\.|&&|\|\||<-|\+\+|--|<<=?|>>=?|&\^=?|[-+*/%&|^<>!=]=?|~/y },
6499
+ { type: "punctuation", re: /[[\]{}(),;:.]/y },
6500
+ { type: "identifier", re: /\b[A-Za-z_]\w*\b/y }
6501
+ ];
6502
+ }
6503
+ });
6504
+
6505
+ // core/highlight/languages/rust.js
6506
+ var rust_exports = {};
6507
+ __export(rust_exports, {
6508
+ default: () => lexRust
6509
+ });
6510
+ function lexRust(code) {
6511
+ return tokenize(code, RULES12);
6512
+ }
6513
+ var RULES12;
6514
+ var init_rust = __esm({
6515
+ "core/highlight/languages/rust.js"() {
6516
+ init_utils();
6517
+ RULES12 = [
6518
+ { type: "comment", re: /\/\/\/?[^\n]*/y },
6519
+ { type: "comment", re: /\/\*[\s\S]*?\*\//y },
6520
+ // Raw strings close on the same number of hashes they opened with.
6521
+ { type: "string", re: /b?r(#*)"[\s\S]*?"\1/y },
6522
+ { type: "string", re: /b?"(?:\\.|[^"\\])*"/y },
6523
+ { type: "string", re: /b?'(?:\\.|[^'\\])'/y },
6524
+ // Attributes such as #[derive(Debug)] read as annotations.
6525
+ { type: "builtin", re: /#!?\[[^\]]*\]/y },
6526
+ { type: "number", re: /\b0[xXbo][\da-fA-F_]+\b|\b\d[\d_]*(?:\.[\d_]+)?(?:[eE][+-]?\d+)?(?:[iuf](?:8|16|32|64|128|size))?\b/y },
6527
+ {
6528
+ type: "keyword",
6529
+ re: /\b(?:as|async|await|break|const|continue|crate|dyn|else|enum|extern|fn|for|if|impl|in|let|loop|match|mod|move|mut|pub|ref|return|self|Self|static|struct|super|trait|type|unsafe|use|where|while)\b/y
6530
+ },
6531
+ {
6532
+ type: "builtin",
6533
+ re: /\b(?:bool|char|f32|f64|i8|i16|i32|i64|i128|isize|str|u8|u16|u32|u64|u128|usize|String|Vec|Option|Result|Some|None|Ok|Err|Box|Rc|Arc|true|false)\b/y
6534
+ },
6535
+ // Lifetimes such as 'a must not be read as an unterminated char literal.
6536
+ { type: "operator", re: /'[a-z_]\w*\b/y },
6537
+ { type: "operator", re: /=>|->|::|\.\.=?|&&|\|\||<<=?|>>=?|[-+*/%&|^<>!=]=?|[?@]/y },
6538
+ { type: "punctuation", re: /[[\]{}(),;:.#]/y },
6539
+ { type: "identifier", re: /\b[A-Za-z_]\w*!?\b/y }
6540
+ ];
6541
+ }
6542
+ });
6543
+
5709
6544
  // core/highlight/registry.js
5710
6545
  function normalizeLanguage(name) {
5711
6546
  const n = (name || "").trim().toLowerCase();
@@ -5725,11 +6560,14 @@
5725
6560
  md: "markdown",
5726
6561
  plaintext: "text",
5727
6562
  "plain-text": "text",
5728
- yml: "text",
5729
- yaml: "text",
6563
+ yml: "yaml",
5730
6564
  toml: "text",
5731
6565
  ini: "text",
5732
- php8: "php"
6566
+ php8: "php",
6567
+ py: "python",
6568
+ python3: "python",
6569
+ golang: "go",
6570
+ rs: "rust"
5733
6571
  };
5734
6572
  return map[n] || n;
5735
6573
  }
@@ -5785,7 +6623,15 @@
5785
6623
  markup: () => Promise.resolve().then(() => (init_html(), html_exports)),
5786
6624
  console: () => Promise.resolve().then(() => (init_plain(), plain_exports)),
5787
6625
  php: () => Promise.resolve().then(() => (init_php(), php_exports)),
5788
- blade: () => Promise.resolve().then(() => (init_blade(), blade_exports))
6626
+ blade: () => Promise.resolve().then(() => (init_blade(), blade_exports)),
6627
+ python: () => Promise.resolve().then(() => (init_python(), python_exports)),
6628
+ py: () => Promise.resolve().then(() => (init_python(), python_exports)),
6629
+ yaml: () => Promise.resolve().then(() => (init_yaml(), yaml_exports)),
6630
+ yml: () => Promise.resolve().then(() => (init_yaml(), yaml_exports)),
6631
+ go: () => Promise.resolve().then(() => (init_go(), go_exports)),
6632
+ golang: () => Promise.resolve().then(() => (init_go(), go_exports)),
6633
+ rust: () => Promise.resolve().then(() => (init_rust(), rust_exports)),
6634
+ rs: () => Promise.resolve().then(() => (init_rust(), rust_exports))
5789
6635
  };
5790
6636
  registerLanguage("js", lexJs);
5791
6637
  }
@@ -6308,12 +7154,823 @@
6308
7154
  }
6309
7155
  });
6310
7156
 
7157
+ // components/velin-data-table.js
7158
+ var velin_data_table_exports = {};
7159
+ __export(velin_data_table_exports, {
7160
+ default: () => velin_data_table_default
7161
+ });
7162
+ function sortValue(cell, type) {
7163
+ const raw = cell?.dataset?.sortValue ?? cell?.textContent ?? "";
7164
+ const text2 = raw.trim();
7165
+ if (type === "number") {
7166
+ const num = Number.parseFloat(text2.replace(/[^\d.,-]/g, "").replace(",", "."));
7167
+ return Number.isNaN(num) ? Number.NEGATIVE_INFINITY : num;
7168
+ }
7169
+ if (type === "date") {
7170
+ const time = Date.parse(text2);
7171
+ return Number.isNaN(time) ? Number.NEGATIVE_INFINITY : time;
7172
+ }
7173
+ return text2.toLowerCase();
7174
+ }
7175
+ var SORT_TYPES, warnedMissingName, VelinDataTable, velin_data_table_default;
7176
+ var init_velin_data_table = __esm({
7177
+ "components/velin-data-table.js"() {
7178
+ init_a11y_utils();
7179
+ SORT_TYPES = /* @__PURE__ */ new Set(["text", "number", "date"]);
7180
+ warnedMissingName = false;
7181
+ VelinDataTable = class extends HTMLElement {
7182
+ static get observedAttributes() {
7183
+ return ["page-size", "filter-input", "empty-text", "label", "editable"];
7184
+ }
7185
+ constructor() {
7186
+ super();
7187
+ this._table = null;
7188
+ this._page = 1;
7189
+ this._query = "";
7190
+ this._sortIndex = -1;
7191
+ this._sortDirection = "ascending";
7192
+ this._filterEl = null;
7193
+ this._pagination = null;
7194
+ this._emptyRow = null;
7195
+ this._filterTimer = null;
7196
+ this._onFilterInput = this._onFilterInput.bind(this);
7197
+ }
7198
+ connectedCallback() {
7199
+ this.classList.add("velin-data-table");
7200
+ requestAnimationFrame(() => this._init());
7201
+ }
7202
+ disconnectedCallback() {
7203
+ if (this._filterEl) this._filterEl.removeEventListener("input", this._onFilterInput);
7204
+ if (this._filterTimer) clearTimeout(this._filterTimer);
7205
+ }
7206
+ attributeChangedCallback(name, previous, next) {
7207
+ if (previous === next || !this._table) return;
7208
+ if (name === "page-size") {
7209
+ this._page = 1;
7210
+ this._render();
7211
+ } else if (name === "filter-input") {
7212
+ this._bindFilterInput();
7213
+ } else if (name === "label") {
7214
+ this._ensureAccessibleName();
7215
+ }
7216
+ }
7217
+ // ── Public API ─────────────────────────────────────────────────────────────
7218
+ /** @returns {HTMLTableRowElement[]} */
7219
+ get rows() {
7220
+ const body = this._table?.tBodies?.[0];
7221
+ return body ? [...body.rows].filter((row) => row !== this._emptyRow) : [];
7222
+ }
7223
+ /** Rows matching the current filter, across all pages. */
7224
+ get matchingRows() {
7225
+ return this.rows.filter((row) => !row.dataset.velinFiltered);
7226
+ }
7227
+ /** Rows visible on the current page. */
7228
+ get visibleRows() {
7229
+ return this.matchingRows.filter((row) => !row.hidden);
7230
+ }
7231
+ get page() {
7232
+ return this._page;
7233
+ }
7234
+ get pageSize() {
7235
+ const size = Number.parseInt(this.getAttribute("page-size") || "", 10);
7236
+ return Number.isFinite(size) && size > 0 ? size : 0;
7237
+ }
7238
+ get pageCount() {
7239
+ const size = this.pageSize;
7240
+ if (!size) return 1;
7241
+ return Math.max(1, Math.ceil(this.matchingRows.length / size));
7242
+ }
7243
+ /**
7244
+ * @param {number} index Column index
7245
+ * @param {'ascending' | 'descending'} [direction]
7246
+ */
7247
+ sort(index, direction) {
7248
+ const headers = this._headers();
7249
+ const header = headers[index];
7250
+ if (!header) return;
7251
+ const type = this._sortType(header);
7252
+ if (type === "none") return;
7253
+ this._sortDirection = direction || (this._sortIndex === index && this._sortDirection === "ascending" ? "descending" : "ascending");
7254
+ this._sortIndex = index;
7255
+ const factor = this._sortDirection === "ascending" ? 1 : -1;
7256
+ const body = this._table.tBodies[0];
7257
+ const sorted = this.rows.slice().sort((a, b) => {
7258
+ const av = sortValue(a.cells[index], type);
7259
+ const bv = sortValue(b.cells[index], type);
7260
+ if (av < bv) return -1 * factor;
7261
+ if (av > bv) return 1 * factor;
7262
+ return 0;
7263
+ });
7264
+ for (const row of sorted) body.appendChild(row);
7265
+ this._syncSortState();
7266
+ this._page = 1;
7267
+ this._render();
7268
+ const label = header.dataset.sortLabel || header.textContent.trim();
7269
+ announce(`${label} sorted ${this._sortDirection}`);
7270
+ this.dispatchEvent(new CustomEvent("velin-data-table-sort", {
7271
+ bubbles: true,
7272
+ detail: { index, direction: this._sortDirection, column: label }
7273
+ }));
7274
+ }
7275
+ /** @param {string} query */
7276
+ filter(query) {
7277
+ this._query = String(query || "").trim().toLowerCase();
7278
+ for (const row of this.rows) {
7279
+ const match = !this._query || this._rowText(row).includes(this._query);
7280
+ if (match) delete row.dataset.velinFiltered;
7281
+ else row.dataset.velinFiltered = "true";
7282
+ }
7283
+ this._page = 1;
7284
+ this._render();
7285
+ const count = this.matchingRows.length;
7286
+ announce(count === 1 ? "1 row matches" : `${count} rows match`);
7287
+ this.dispatchEvent(new CustomEvent("velin-data-table-filter", {
7288
+ bubbles: true,
7289
+ detail: { query: this._query, count }
7290
+ }));
7291
+ }
7292
+ /** @param {number} page */
7293
+ goToPage(page) {
7294
+ const target = Math.min(Math.max(1, Math.trunc(page) || 1), this.pageCount);
7295
+ if (target === this._page) return;
7296
+ this._page = target;
7297
+ this._render();
7298
+ announce(`Page ${this._page} of ${this.pageCount}`);
7299
+ this.dispatchEvent(new CustomEvent("velin-data-table-page", {
7300
+ bubbles: true,
7301
+ detail: { page: this._page, pageCount: this.pageCount }
7302
+ }));
7303
+ }
7304
+ // ── Setup ──────────────────────────────────────────────────────────────────
7305
+ _init() {
7306
+ this._table = this.querySelector("table");
7307
+ if (!this._table || !this._table.tBodies.length) return;
7308
+ this._ensureAccessibleName();
7309
+ this._setupSorting();
7310
+ this._setupEditable();
7311
+ this._bindFilterInput();
7312
+ this._render();
7313
+ }
7314
+ _headers() {
7315
+ const headRow = this._table.tHead?.rows?.[0];
7316
+ return headRow ? [...headRow.cells] : [];
7317
+ }
7318
+ /** @param {HTMLTableCellElement} header */
7319
+ _sortType(header) {
7320
+ const declared = (header.dataset.sort || "").toLowerCase();
7321
+ if (declared === "none") return "none";
7322
+ if (SORT_TYPES.has(declared)) return declared;
7323
+ return this.hasAttribute("sortable") ? "text" : "none";
7324
+ }
7325
+ /**
7326
+ * A table without a name is unusable with a screen reader, so surface it
7327
+ * instead of failing silently (WCAG 1.3.1 / 2.4.6).
7328
+ */
7329
+ _ensureAccessibleName() {
7330
+ const table = this._table;
7331
+ if (!table) return;
7332
+ const label = this.getAttribute("label");
7333
+ const hasName = table.caption?.textContent.trim() || table.getAttribute("aria-label")?.trim() || table.getAttribute("aria-labelledby")?.trim();
7334
+ if (!hasName && label) {
7335
+ table.setAttribute("aria-label", label);
7336
+ return;
7337
+ }
7338
+ if (!hasName && !warnedMissingName) {
7339
+ warnedMissingName = true;
7340
+ console.warn("[velinstyle] <velin-data-table> needs a <caption>, aria-label, or a label attribute.");
7341
+ }
7342
+ }
7343
+ /** Sortable headers get a real button so keyboard and AT support come for free. */
7344
+ _setupSorting() {
7345
+ this._headers().forEach((header, index) => {
7346
+ if (this._sortType(header) === "none") return;
7347
+ if (header.querySelector(".velin-data-table__sort")) return;
7348
+ const label = header.textContent.trim();
7349
+ header.dataset.sortLabel = label;
7350
+ header.setAttribute("aria-sort", "none");
7351
+ header.classList.add("velin-data-table__th");
7352
+ const button = document.createElement("button");
7353
+ button.type = "button";
7354
+ button.className = "velin-data-table__sort";
7355
+ button.textContent = label;
7356
+ const icon = document.createElement("span");
7357
+ icon.className = "velin-data-table__sort-icon";
7358
+ icon.setAttribute("aria-hidden", "true");
7359
+ button.appendChild(icon);
7360
+ button.addEventListener("click", () => this.sort(index));
7361
+ header.textContent = "";
7362
+ header.appendChild(button);
7363
+ });
7364
+ }
7365
+ /**
7366
+ * Inline edit: double-click or Enter on focused cell with data-editable / host editable.
7367
+ * Enter saves, Escape cancels. Dispatches velin-data-table-edit.
7368
+ */
7369
+ _setupEditable() {
7370
+ if (!this.hasAttribute("editable") && !this.querySelector("[data-editable]")) return;
7371
+ const body = this._table.tBodies[0];
7372
+ if (!body || body.dataset.velinEditBound) return;
7373
+ body.dataset.velinEditBound = "true";
7374
+ body.addEventListener("dblclick", (e) => {
7375
+ const cell = e.target.closest("td");
7376
+ if (!cell || !body.contains(cell)) return;
7377
+ if (!this.hasAttribute("editable") && !cell.hasAttribute("data-editable")) return;
7378
+ this._beginEdit(cell);
7379
+ });
7380
+ body.addEventListener("keydown", (e) => {
7381
+ const cell = e.target.closest("td");
7382
+ if (!cell || e.target !== cell) return;
7383
+ if (e.key === "Enter" || e.key === "F2") {
7384
+ if (!this.hasAttribute("editable") && !cell.hasAttribute("data-editable")) return;
7385
+ e.preventDefault();
7386
+ this._beginEdit(cell);
7387
+ }
7388
+ });
7389
+ for (const cell of body.querySelectorAll("td[data-editable], td")) {
7390
+ if (!this.hasAttribute("editable") && !cell.hasAttribute("data-editable")) continue;
7391
+ if (!cell.hasAttribute("tabindex")) cell.setAttribute("tabindex", "0");
7392
+ }
7393
+ }
7394
+ /** @param {HTMLTableCellElement} cell */
7395
+ _beginEdit(cell) {
7396
+ if (cell.querySelector("input")) return;
7397
+ const previous = cell.textContent.trim();
7398
+ const input = document.createElement("input");
7399
+ input.className = "velin-input velin-data-table__edit";
7400
+ input.value = previous;
7401
+ input.setAttribute("aria-label", "Edit cell");
7402
+ cell.textContent = "";
7403
+ cell.appendChild(input);
7404
+ input.focus();
7405
+ input.select();
7406
+ const commit = (save) => {
7407
+ const next = save ? input.value.trim() : previous;
7408
+ cell.textContent = next;
7409
+ if (save && next !== previous) {
7410
+ this.dispatchEvent(new CustomEvent("velin-data-table-edit", {
7411
+ bubbles: true,
7412
+ detail: {
7413
+ cell,
7414
+ row: cell.parentElement,
7415
+ previous,
7416
+ value: next,
7417
+ columnIndex: cell.cellIndex
7418
+ }
7419
+ }));
7420
+ announce("Cell updated");
7421
+ }
7422
+ };
7423
+ input.addEventListener("keydown", (e) => {
7424
+ if (e.key === "Enter") {
7425
+ e.preventDefault();
7426
+ commit(true);
7427
+ } else if (e.key === "Escape") {
7428
+ e.preventDefault();
7429
+ commit(false);
7430
+ }
7431
+ });
7432
+ input.addEventListener("blur", () => commit(true));
7433
+ }
7434
+ _syncSortState() {
7435
+ this._headers().forEach((header, index) => {
7436
+ if (this._sortType(header) === "none") return;
7437
+ const active = index === this._sortIndex;
7438
+ header.setAttribute("aria-sort", active ? this._sortDirection : "none");
7439
+ header.querySelector(".velin-data-table__sort")?.classList.toggle("velin-data-table__sort--active", active);
7440
+ });
7441
+ }
7442
+ _bindFilterInput() {
7443
+ if (this._filterEl) this._filterEl.removeEventListener("input", this._onFilterInput);
7444
+ const selector = this.getAttribute("filter-input");
7445
+ this._filterEl = selector ? document.querySelector(selector) : null;
7446
+ if (this._filterEl) this._filterEl.addEventListener("input", this._onFilterInput);
7447
+ }
7448
+ _onFilterInput(event) {
7449
+ if (this._filterTimer) clearTimeout(this._filterTimer);
7450
+ const value = event.target.value;
7451
+ this._filterTimer = setTimeout(() => this.filter(value), 150);
7452
+ }
7453
+ /** @param {HTMLTableRowElement} row */
7454
+ _rowText(row) {
7455
+ const scoped = [...row.cells].filter((cell) => cell.hasAttribute("data-filter"));
7456
+ const cells = scoped.length ? scoped : [...row.cells];
7457
+ return cells.map((cell) => cell.textContent || "").join(" ").toLowerCase();
7458
+ }
7459
+ // ── Rendering ──────────────────────────────────────────────────────────────
7460
+ _render() {
7461
+ const size = this.pageSize;
7462
+ this._page = Math.min(this._page, this.pageCount);
7463
+ const matching = this.matchingRows;
7464
+ const start = size ? (this._page - 1) * size : 0;
7465
+ const end = size ? start + size : matching.length;
7466
+ for (const row of this.rows) {
7467
+ const index = matching.indexOf(row);
7468
+ row.hidden = index === -1 || index < start || index >= end;
7469
+ }
7470
+ this._renderEmptyState(matching.length === 0);
7471
+ this._renderPagination();
7472
+ }
7473
+ /** @param {boolean} isEmpty */
7474
+ _renderEmptyState(isEmpty) {
7475
+ if (!isEmpty) {
7476
+ this._emptyRow?.remove();
7477
+ this._emptyRow = null;
7478
+ return;
7479
+ }
7480
+ if (this._emptyRow?.isConnected) return;
7481
+ const columns = this._headers().length || 1;
7482
+ const row = document.createElement("tr");
7483
+ row.className = "velin-data-table__empty";
7484
+ const cell = document.createElement("td");
7485
+ cell.colSpan = columns;
7486
+ cell.textContent = this.getAttribute("empty-text") || "No matching rows";
7487
+ row.appendChild(cell);
7488
+ this._table.tBodies[0].appendChild(row);
7489
+ this._emptyRow = row;
7490
+ }
7491
+ _renderPagination() {
7492
+ if (!this.pageSize || this.pageCount <= 1) {
7493
+ this._pagination?.remove();
7494
+ this._pagination = null;
7495
+ return;
7496
+ }
7497
+ if (!this._pagination?.isConnected) {
7498
+ const nav = document.createElement("nav");
7499
+ nav.className = "velin-data-table__pagination";
7500
+ nav.setAttribute("aria-label", this.getAttribute("pagination-label") || "Table pagination");
7501
+ const previous2 = document.createElement("button");
7502
+ previous2.type = "button";
7503
+ previous2.className = "velin-btn velin-btn--outline velin-btn--sm";
7504
+ previous2.dataset.velinPage = "previous";
7505
+ previous2.textContent = this.getAttribute("previous-text") || "Previous";
7506
+ previous2.addEventListener("click", () => this.goToPage(this._page - 1));
7507
+ const status2 = document.createElement("p");
7508
+ status2.className = "velin-data-table__page-status";
7509
+ status2.dataset.velinPage = "status";
7510
+ const next2 = document.createElement("button");
7511
+ next2.type = "button";
7512
+ next2.className = "velin-btn velin-btn--outline velin-btn--sm";
7513
+ next2.dataset.velinPage = "next";
7514
+ next2.textContent = this.getAttribute("next-text") || "Next";
7515
+ next2.addEventListener("click", () => this.goToPage(this._page + 1));
7516
+ nav.append(previous2, status2, next2);
7517
+ this.appendChild(nav);
7518
+ this._pagination = nav;
7519
+ }
7520
+ const status = this._pagination.querySelector('[data-velin-page="status"]');
7521
+ if (status) status.textContent = `Page ${this._page} of ${this.pageCount}`;
7522
+ const previous = this._pagination.querySelector('[data-velin-page="previous"]');
7523
+ const next = this._pagination.querySelector('[data-velin-page="next"]');
7524
+ if (previous) previous.disabled = this._page <= 1;
7525
+ if (next) next.disabled = this._page >= this.pageCount;
7526
+ }
7527
+ };
7528
+ customElements.define("velin-data-table", VelinDataTable);
7529
+ velin_data_table_default = VelinDataTable;
7530
+ }
7531
+ });
7532
+
7533
+ // components/velin-file-dropzone.js
7534
+ var velin_file_dropzone_exports = {};
7535
+ __export(velin_file_dropzone_exports, {
7536
+ default: () => velin_file_dropzone_default
7537
+ });
7538
+ var styles28, VelinFileDropzone, velin_file_dropzone_default;
7539
+ var init_velin_file_dropzone = __esm({
7540
+ "components/velin-file-dropzone.js"() {
7541
+ init_sanitize();
7542
+ styles28 = `
7543
+ :host { display: block; }
7544
+ .zone {
7545
+ border: 2px dashed var(--velin-color-border, #cbd5e1);
7546
+ border-radius: var(--velin-radius-md, 0.5rem);
7547
+ padding: var(--velin-space-6, 1.5rem);
7548
+ background: var(--velin-color-surface-dim, var(--velin-color-bg-subtle, #f8fafc));
7549
+ color: var(--velin-color-text, #111);
7550
+ text-align: center;
7551
+ transition: border-color 150ms ease, background 150ms ease;
7552
+ }
7553
+ :host([dragging]) .zone {
7554
+ border-color: var(--velin-color-primary, #2563eb);
7555
+ background: var(--velin-color-primary-subtle, #eff6ff);
7556
+ }
7557
+ .hint { margin: 0 0 var(--velin-space-3, 0.75rem); color: var(--velin-color-text-muted, #64748b); }
7558
+ .browse {
7559
+ display: inline-flex; align-items: center; justify-content: center;
7560
+ min-block-size: 2.75rem; padding-inline: var(--velin-space-4, 1rem);
7561
+ border-radius: var(--velin-radius-md, 0.5rem);
7562
+ border: none; cursor: pointer;
7563
+ background: var(--velin-color-primary, #2563eb);
7564
+ color: var(--velin-color-on-primary, #fff);
7565
+ font: inherit;
7566
+ }
7567
+ .browse:focus-visible { outline: 2px solid var(--velin-color-focus, #2563eb); outline-offset: 2px; }
7568
+ input[type="file"] {
7569
+ position: absolute; inline-size: 1px; block-size: 1px; padding: 0; margin: -1px;
7570
+ overflow: hidden; clip: rect(0,0,0,0); border: 0;
7571
+ }
7572
+ .list { list-style: none; padding: 0; margin: var(--velin-space-4, 1rem) 0 0; text-align: start; }
7573
+ .list li {
7574
+ display: flex; justify-content: space-between; gap: var(--velin-space-3, 0.75rem);
7575
+ padding: var(--velin-space-2, 0.5rem) 0;
7576
+ border-block-end: 1px solid var(--velin-color-border, #e2e8f0);
7577
+ font-size: var(--velin-text-sm, 0.875rem);
7578
+ }
7579
+ .progress {
7580
+ margin-block-start: var(--velin-space-3, 0.75rem);
7581
+ block-size: 0.5rem; border-radius: 999px;
7582
+ background: var(--velin-color-border, #e2e8f0); overflow: hidden;
7583
+ }
7584
+ .bar {
7585
+ block-size: 100%; inline-size: 0%;
7586
+ background: var(--velin-color-primary, #2563eb);
7587
+ transition: inline-size 150ms ease;
7588
+ }
7589
+ .status { margin: var(--velin-space-2, 0.5rem) 0 0; font-size: var(--velin-text-sm, 0.875rem); }
7590
+ .status[data-tone="error"] { color: var(--velin-color-danger, #b91c1c); }
7591
+ `;
7592
+ VelinFileDropzone = class extends HTMLElement {
7593
+ static get observedAttributes() {
7594
+ return ["accept", "multiple", "label", "progress"];
7595
+ }
7596
+ constructor() {
7597
+ super();
7598
+ this.attachShadow({ mode: "open" });
7599
+ this._files = [];
7600
+ }
7601
+ connectedCallback() {
7602
+ this._render();
7603
+ }
7604
+ attributeChangedCallback() {
7605
+ if (this.shadowRoot?.querySelector(".zone")) this._render();
7606
+ }
7607
+ get files() {
7608
+ return this._files.slice();
7609
+ }
7610
+ _emitFiles(fileList) {
7611
+ const accept = this.getAttribute("accept");
7612
+ const files = [...fileList];
7613
+ if (accept) {
7614
+ const parts = accept.split(",").map((s) => s.trim().toLowerCase());
7615
+ const bad = files.find((f) => {
7616
+ const name = f.name.toLowerCase();
7617
+ const type = (f.type || "").toLowerCase();
7618
+ return !parts.some((p) => p.startsWith(".") ? name.endsWith(p) : type === p || type.startsWith(p.replace("/*", "/")));
7619
+ });
7620
+ if (bad) {
7621
+ this._setStatus(`File type not allowed: ${bad.name}`, "error");
7622
+ this.dispatchEvent(new CustomEvent("velin-error", { bubbles: true, detail: { message: "accept", file: bad } }));
7623
+ return;
7624
+ }
7625
+ }
7626
+ this._files = files;
7627
+ this._setStatus(files.length ? `${files.length} file(s) ready` : "", "ok");
7628
+ this.dispatchEvent(new CustomEvent("velin-files", { bubbles: true, detail: { files } }));
7629
+ this._renderList();
7630
+ }
7631
+ _setStatus(text2, tone = "ok") {
7632
+ const el = this.shadowRoot?.querySelector(".status");
7633
+ if (!el) return;
7634
+ el.textContent = text2;
7635
+ el.dataset.tone = tone;
7636
+ }
7637
+ _renderList() {
7638
+ const ul = this.shadowRoot?.querySelector(".list");
7639
+ if (!ul) return;
7640
+ ul.innerHTML = this._files.map((f) => `<li><span>${escapeHTML(f.name)}</span><span>${Math.round(f.size / 1024)} KB</span></li>`).join("");
7641
+ }
7642
+ _render() {
7643
+ const label = escapeHTML(this.getAttribute("label") || "Upload files");
7644
+ const accept = this.getAttribute("accept") || "";
7645
+ const multiple = this.hasAttribute("multiple");
7646
+ const progress = Math.max(0, Math.min(100, Number(this.getAttribute("progress") || 0)));
7647
+ this.shadowRoot.innerHTML = `
7648
+ <style>${styles28}</style>
7649
+ <div class="zone" role="group" aria-label="${label}">
7650
+ <p class="hint">Drag and drop files here, or browse. Client-side only \u2014 wire <code>velin-files</code> to your upload API.</p>
7651
+ <button type="button" class="browse">Browse files</button>
7652
+ <input type="file" ${accept ? `accept="${escapeHTML(accept)}"` : ""} ${multiple ? "multiple" : ""} />
7653
+ <div class="progress" hidden="${progress <= 0 ? "true" : "false"}" aria-hidden="${progress <= 0 ? "true" : "false"}">
7654
+ <div class="bar" style="inline-size:${progress}%"></div>
7655
+ </div>
7656
+ <p class="status" role="status" aria-live="polite"></p>
7657
+ <ul class="list"></ul>
7658
+ </div>
7659
+ `;
7660
+ const input = this.shadowRoot.querySelector("input");
7661
+ const browse = this.shadowRoot.querySelector(".browse");
7662
+ const zone = this.shadowRoot.querySelector(".zone");
7663
+ browse.addEventListener("click", () => input.click());
7664
+ input.addEventListener("change", () => {
7665
+ if (input.files?.length) this._emitFiles(input.files);
7666
+ });
7667
+ ["dragenter", "dragover"].forEach((ev) => {
7668
+ zone.addEventListener(ev, (e) => {
7669
+ e.preventDefault();
7670
+ this.setAttribute("dragging", "");
7671
+ });
7672
+ });
7673
+ ["dragleave", "drop"].forEach((ev) => {
7674
+ zone.addEventListener(ev, (e) => {
7675
+ e.preventDefault();
7676
+ this.removeAttribute("dragging");
7677
+ });
7678
+ });
7679
+ zone.addEventListener("drop", (e) => {
7680
+ const list = e.dataTransfer?.files;
7681
+ if (list?.length) this._emitFiles(list);
7682
+ });
7683
+ this._renderList();
7684
+ }
7685
+ };
7686
+ customElements.define("velin-file-dropzone", VelinFileDropzone);
7687
+ velin_file_dropzone_default = VelinFileDropzone;
7688
+ }
7689
+ });
7690
+
7691
+ // components/velin-form-summary.js
7692
+ var velin_form_summary_exports = {};
7693
+ __export(velin_form_summary_exports, {
7694
+ default: () => velin_form_summary_default
7695
+ });
7696
+ function escapeSelector(value) {
7697
+ const text2 = String(value ?? "");
7698
+ if (typeof CSS !== "undefined" && typeof CSS.escape === "function") return CSS.escape(text2);
7699
+ return text2.replace(/[^\w-]/g, (char) => `\\${char}`);
7700
+ }
7701
+ function fieldLabel(field) {
7702
+ const explicit = field.getAttribute("data-error-label");
7703
+ if (explicit) return explicit;
7704
+ const ariaLabel = field.getAttribute("aria-label");
7705
+ if (ariaLabel?.trim()) return ariaLabel.trim();
7706
+ const labelledBy = field.getAttribute("aria-labelledby");
7707
+ if (labelledBy) {
7708
+ const text2 = labelledBy.split(/\s+/).map((id) => field.ownerDocument.getElementById(id)?.textContent?.trim() || "").filter(Boolean).join(" ");
7709
+ if (text2) return text2;
7710
+ }
7711
+ if (field.id) {
7712
+ const label = field.ownerDocument.querySelector(`label[for="${escapeSelector(field.id)}"]`);
7713
+ if (label?.textContent.trim()) return label.textContent.trim();
7714
+ }
7715
+ const wrapping = field.closest("label");
7716
+ if (wrapping?.textContent.trim()) return wrapping.textContent.trim();
7717
+ return field.name || "This field";
7718
+ }
7719
+ function fieldMessage(field) {
7720
+ return field.getAttribute("data-error-message")?.trim() || field.validationMessage || "Invalid value";
7721
+ }
7722
+ function addDescribedBy(field, id) {
7723
+ const ids = (field.getAttribute("aria-describedby") || "").split(/\s+/).filter(Boolean);
7724
+ if (!ids.includes(id)) {
7725
+ ids.push(id);
7726
+ field.setAttribute("aria-describedby", ids.join(" "));
7727
+ }
7728
+ }
7729
+ function removeDescribedBy(field, id) {
7730
+ const ids = (field.getAttribute("aria-describedby") || "").split(/\s+/).filter(Boolean);
7731
+ const next = ids.filter((value) => value !== id);
7732
+ if (next.length) field.setAttribute("aria-describedby", next.join(" "));
7733
+ else field.removeAttribute("aria-describedby");
7734
+ }
7735
+ var FIELD_SELECTOR, IGNORED_TYPES, fieldIdCounter, VelinFormSummary, velin_form_summary_default;
7736
+ var init_velin_form_summary = __esm({
7737
+ "components/velin-form-summary.js"() {
7738
+ init_a11y_utils();
7739
+ FIELD_SELECTOR = "input, select, textarea";
7740
+ IGNORED_TYPES = /* @__PURE__ */ new Set(["submit", "reset", "button", "image", "hidden"]);
7741
+ fieldIdCounter = 0;
7742
+ VelinFormSummary = class extends HTMLElement {
7743
+ static get observedAttributes() {
7744
+ return ["for", "heading"];
7745
+ }
7746
+ constructor() {
7747
+ super();
7748
+ this._form = null;
7749
+ this._panel = null;
7750
+ this._errors = [];
7751
+ this._onSubmit = this._onSubmit.bind(this);
7752
+ this._onFieldChange = this._onFieldChange.bind(this);
7753
+ this._onReset = this._onReset.bind(this);
7754
+ }
7755
+ connectedCallback() {
7756
+ this.classList.add("velin-form-summary");
7757
+ requestAnimationFrame(() => this._bindForm());
7758
+ }
7759
+ disconnectedCallback() {
7760
+ this._unbindForm();
7761
+ }
7762
+ attributeChangedCallback(name, previous, next) {
7763
+ if (previous === next) return;
7764
+ if (name === "for" && this.isConnected) {
7765
+ this._unbindForm();
7766
+ this._bindForm();
7767
+ } else if (name === "heading" && this._panel) {
7768
+ const heading = this._panel.querySelector(".velin-form-summary__heading");
7769
+ if (heading) heading.textContent = this.headingText;
7770
+ }
7771
+ }
7772
+ // ── Public API ─────────────────────────────────────────────────────────────
7773
+ get form() {
7774
+ return this._form;
7775
+ }
7776
+ /** @returns {{ field: HTMLElement, label: string, message: string }[]} */
7777
+ get errors() {
7778
+ return this._errors.slice();
7779
+ }
7780
+ get headingText() {
7781
+ return this.getAttribute("heading") || "There is a problem";
7782
+ }
7783
+ /** Validate the form and render the summary. @returns {boolean} valid */
7784
+ validate() {
7785
+ if (!this._form) return true;
7786
+ const errors = [];
7787
+ for (const field of this._fields()) {
7788
+ if (field.checkValidity()) {
7789
+ this._clearFieldError(field);
7790
+ continue;
7791
+ }
7792
+ const error = { field, label: fieldLabel(field), message: fieldMessage(field) };
7793
+ this._markFieldError(field, error.message);
7794
+ errors.push(error);
7795
+ }
7796
+ this._errors = errors;
7797
+ this._render();
7798
+ return errors.length === 0;
7799
+ }
7800
+ /** Remove the summary and all field error state. */
7801
+ clear() {
7802
+ for (const field of this._fields()) this._clearFieldError(field);
7803
+ this._errors = [];
7804
+ this._render();
7805
+ }
7806
+ /** Move focus to the first field with an error. */
7807
+ focusFirstError() {
7808
+ const first = this._errors[0];
7809
+ if (first) this._focusField(first.field);
7810
+ }
7811
+ // ── Form wiring ────────────────────────────────────────────────────────────
7812
+ _bindForm() {
7813
+ const id = this.getAttribute("for");
7814
+ this._form = id ? this.ownerDocument.getElementById(id) : this.closest("form");
7815
+ if (!this._form) return;
7816
+ if (!this.hasAttribute("native-validation")) this._form.noValidate = true;
7817
+ this._form.addEventListener("submit", this._onSubmit);
7818
+ this._form.addEventListener("reset", this._onReset);
7819
+ this._form.addEventListener("input", this._onFieldChange);
7820
+ this._form.addEventListener("change", this._onFieldChange);
7821
+ }
7822
+ _unbindForm() {
7823
+ if (!this._form) return;
7824
+ this._form.removeEventListener("submit", this._onSubmit);
7825
+ this._form.removeEventListener("reset", this._onReset);
7826
+ this._form.removeEventListener("input", this._onFieldChange);
7827
+ this._form.removeEventListener("change", this._onFieldChange);
7828
+ this._form = null;
7829
+ }
7830
+ /** @returns {HTMLElement[]} */
7831
+ _fields() {
7832
+ if (!this._form) return [];
7833
+ const seenRadioNames = /* @__PURE__ */ new Set();
7834
+ return [...this._form.querySelectorAll(FIELD_SELECTOR)].filter((field) => {
7835
+ if (IGNORED_TYPES.has(field.type)) return false;
7836
+ if (field.disabled || field.hasAttribute("data-error-ignore")) return false;
7837
+ if (typeof field.checkValidity !== "function") return false;
7838
+ if (field.type === "radio" && field.name) {
7839
+ if (seenRadioNames.has(field.name)) return false;
7840
+ seenRadioNames.add(field.name);
7841
+ }
7842
+ return true;
7843
+ });
7844
+ }
7845
+ _onSubmit(event) {
7846
+ if (this.validate()) return;
7847
+ event.preventDefault();
7848
+ this._announceErrors();
7849
+ this._focusPanel();
7850
+ this.dispatchEvent(new CustomEvent("velin-form-invalid", {
7851
+ bubbles: true,
7852
+ detail: { errors: this.errors.map(({ label, message }) => ({ label, message })) }
7853
+ }));
7854
+ }
7855
+ _onReset() {
7856
+ requestAnimationFrame(() => this.clear());
7857
+ }
7858
+ /** Re-validate a single field once it already had an error, never before. */
7859
+ _onFieldChange(event) {
7860
+ const field = event.target;
7861
+ if (!field || !this._errors.some((error) => error.field === field)) return;
7862
+ if (!field.checkValidity()) return;
7863
+ this._clearFieldError(field);
7864
+ this._errors = this._errors.filter((error) => error.field !== field);
7865
+ this._render();
7866
+ if (this._errors.length === 0) {
7867
+ this.dispatchEvent(new CustomEvent("velin-form-valid", { bubbles: true }));
7868
+ }
7869
+ }
7870
+ // ── Field state ────────────────────────────────────────────────────────────
7871
+ /** @param {HTMLElement} field */
7872
+ _errorId(field) {
7873
+ if (!field.id) field.id = `velin-field-${++fieldIdCounter}`;
7874
+ return `${field.id}-error`;
7875
+ }
7876
+ /**
7877
+ * @param {HTMLElement} field
7878
+ * @param {string} message
7879
+ */
7880
+ _markFieldError(field, message) {
7881
+ const errorId = this._errorId(field);
7882
+ field.setAttribute("aria-invalid", "true");
7883
+ let holder = this.ownerDocument.getElementById(errorId);
7884
+ if (!holder) {
7885
+ holder = this._form.querySelector(`[data-velin-error-for="${escapeSelector(field.name || field.id)}"]`);
7886
+ }
7887
+ if (!holder) {
7888
+ holder = this.ownerDocument.createElement("p");
7889
+ holder.dataset.velinErrorGenerated = "true";
7890
+ field.insertAdjacentElement("afterend", holder);
7891
+ }
7892
+ holder.id = errorId;
7893
+ holder.classList.add("velin-field-error");
7894
+ holder.textContent = message;
7895
+ addDescribedBy(field, errorId);
7896
+ }
7897
+ /** @param {HTMLElement} field */
7898
+ _clearFieldError(field) {
7899
+ if (!field.id) return;
7900
+ const errorId = `${field.id}-error`;
7901
+ field.removeAttribute("aria-invalid");
7902
+ removeDescribedBy(field, errorId);
7903
+ const holder = this.ownerDocument.getElementById(errorId);
7904
+ if (!holder) return;
7905
+ if (holder.dataset.velinErrorGenerated) holder.remove();
7906
+ else holder.textContent = "";
7907
+ }
7908
+ /** @param {HTMLElement} field */
7909
+ _focusField(field) {
7910
+ const target = field.type === "radio" && field.name ? this._form.querySelector(`input[type="radio"][name="${escapeSelector(field.name)}"]`) || field : field;
7911
+ target.focus();
7912
+ this.dispatchEvent(new CustomEvent("velin-form-error-focus", {
7913
+ bubbles: true,
7914
+ detail: { name: target.name || target.id }
7915
+ }));
7916
+ }
7917
+ // ── Summary panel ──────────────────────────────────────────────────────────
7918
+ _render() {
7919
+ if (!this._errors.length) {
7920
+ this._panel?.remove();
7921
+ this._panel = null;
7922
+ this.hidden = true;
7923
+ return;
7924
+ }
7925
+ this.hidden = false;
7926
+ if (!this._panel?.isConnected) {
7927
+ const panel = this.ownerDocument.createElement("div");
7928
+ panel.className = "velin-form-summary__panel velin-alert velin-alert--danger";
7929
+ panel.setAttribute("role", "alert");
7930
+ panel.tabIndex = -1;
7931
+ const heading = this.ownerDocument.createElement("p");
7932
+ heading.className = "velin-form-summary__heading";
7933
+ heading.textContent = this.headingText;
7934
+ const list2 = this.ownerDocument.createElement("ul");
7935
+ list2.className = "velin-form-summary__list";
7936
+ panel.append(heading, list2);
7937
+ this.appendChild(panel);
7938
+ this._panel = panel;
7939
+ }
7940
+ const list = this._panel.querySelector(".velin-form-summary__list");
7941
+ list.textContent = "";
7942
+ for (const error of this._errors) {
7943
+ const item = this.ownerDocument.createElement("li");
7944
+ const link = this.ownerDocument.createElement("a");
7945
+ link.href = `#${this._errorId(error.field).replace(/-error$/, "")}`;
7946
+ link.textContent = `${error.label}: ${error.message}`;
7947
+ link.addEventListener("click", (event) => {
7948
+ event.preventDefault();
7949
+ this._focusField(error.field);
7950
+ });
7951
+ item.appendChild(link);
7952
+ list.appendChild(item);
7953
+ }
7954
+ }
7955
+ _focusPanel() {
7956
+ this._panel?.focus();
7957
+ }
7958
+ _announceErrors() {
7959
+ const count = this._errors.length;
7960
+ announce(count === 1 ? "1 field needs attention" : `${count} fields need attention`, "assertive");
7961
+ }
7962
+ };
7963
+ customElements.define("velin-form-summary", VelinFormSummary);
7964
+ velin_form_summary_default = VelinFormSummary;
7965
+ }
7966
+ });
7967
+
6311
7968
  // components/velin-live-dot.js
6312
7969
  var velin_live_dot_exports = {};
6313
7970
  __export(velin_live_dot_exports, {
6314
7971
  default: () => velin_live_dot_default
6315
7972
  });
6316
- var STATUS_COLORS, styles28, KEYFRAMES_FALLBACK, VelinLiveDot, velin_live_dot_default;
7973
+ var STATUS_COLORS, styles29, KEYFRAMES_FALLBACK, VelinLiveDot, velin_live_dot_default;
6317
7974
  var init_velin_live_dot = __esm({
6318
7975
  "components/velin-live-dot.js"() {
6319
7976
  init_a11y_utils();
@@ -6324,7 +7981,7 @@
6324
7981
  error: "var(--velin-color-danger, oklch(60% 0.2 25))",
6325
7982
  muted: "var(--velin-color-border, oklch(85% 0.01 240))"
6326
7983
  };
6327
- styles28 = `
7984
+ styles29 = `
6328
7985
  :host {
6329
7986
  display: inline-flex;
6330
7987
  align-items: center;
@@ -6374,7 +8031,7 @@
6374
8031
  this.setAttribute("aria-label", label);
6375
8032
  this.style.setProperty("--velin-live-color", color);
6376
8033
  this.shadowRoot.innerHTML = `
6377
- <style>${styles28}${KEYFRAMES_FALLBACK}</style>
8034
+ <style>${styles29}${KEYFRAMES_FALLBACK}</style>
6378
8035
  <span class="dot" aria-hidden="true"></span><slot></slot>
6379
8036
  `;
6380
8037
  }
@@ -6992,7 +8649,7 @@
6992
8649
  return;
6993
8650
  }
6994
8651
  if (!this._indexLoaded) await this._loadIndex();
6995
- const { groups } = this._search.query(q, {
8652
+ const { groups } = await this._search.query(q, {
6996
8653
  minChars: this.minChars,
6997
8654
  fuzzy: this.fuzzy,
6998
8655
  categories: this.categories || void 0,
@@ -7123,11 +8780,11 @@
7123
8780
  cipher: Array.from(new Uint8Array(cipher))
7124
8781
  });
7125
8782
  }
7126
- var styles29, VelinSecureField, velin_secure_field_default;
8783
+ var styles30, VelinSecureField, velin_secure_field_default;
7127
8784
  var init_velin_secure_field = __esm({
7128
8785
  "components/velin-secure-field.js"() {
7129
8786
  init_sanitize();
7130
- styles29 = `
8787
+ styles30 = `
7131
8788
  :host { display: block; }
7132
8789
  label { display: block; font-size: var(--velin-text-sm); margin-block-end: var(--velin-space-1); }
7133
8790
  input {
@@ -7177,7 +8834,7 @@
7177
8834
  const input = this.shadowRoot?.querySelector("input");
7178
8835
  if (input && this._onChange) input.removeEventListener("change", this._onChange);
7179
8836
  this.shadowRoot.innerHTML = `
7180
- <style>${styles29}</style>
8837
+ <style>${styles30}</style>
7181
8838
  <label for="${id}">${escapeHTML(label)}</label>
7182
8839
  <input id="${id}" type="${type}" autocomplete="${autocomplete}" part="input" />
7183
8840
  <p class="velin-field-help">Demo-only client encoding \u2014 use HTTPS and server-side crypto for real secrets.</p>
@@ -7397,6 +9054,7 @@
7397
9054
  "velin-accordion": () => Promise.resolve().then(() => (init_velin_accordion(), velin_accordion_exports)),
7398
9055
  "velin-announcer": () => Promise.resolve().then(() => (init_velin_announcer(), velin_announcer_exports)),
7399
9056
  "velin-bottom-nav": () => Promise.resolve().then(() => (init_velin_bottom_nav(), velin_bottom_nav_exports)),
9057
+ "velin-calendar": () => Promise.resolve().then(() => (init_velin_calendar(), velin_calendar_exports)),
7400
9058
  "velin-carousel": () => Promise.resolve().then(() => (init_velin_carousel(), velin_carousel_exports)),
7401
9059
  "velin-code-block": () => Promise.resolve().then(() => (init_velin_code_block(), velin_code_block_exports)),
7402
9060
  "velin-collapse": () => Promise.resolve().then(() => (init_velin_collapse(), velin_collapse_exports)),
@@ -7405,10 +9063,13 @@
7405
9063
  "velin-copy": () => Promise.resolve().then(() => (init_velin_copy(), velin_copy_exports)),
7406
9064
  "velin-countdown": () => Promise.resolve().then(() => (init_velin_countdown(), velin_countdown_exports)),
7407
9065
  "velin-counter": () => Promise.resolve().then(() => (init_velin_counter(), velin_counter_exports)),
9066
+ "velin-data-table": () => Promise.resolve().then(() => (init_velin_data_table(), velin_data_table_exports)),
7408
9067
  "velin-dialog": () => Promise.resolve().then(() => (init_velin_dialog(), velin_dialog_exports)),
7409
9068
  "velin-drawer": () => Promise.resolve().then(() => (init_velin_drawer(), velin_drawer_exports)),
7410
9069
  "velin-dropdown": () => Promise.resolve().then(() => (init_velin_dropdown(), velin_dropdown_exports)),
7411
9070
  "velin-email": () => Promise.resolve().then(() => (init_velin_email(), velin_email_exports)),
9071
+ "velin-file-dropzone": () => Promise.resolve().then(() => (init_velin_file_dropzone(), velin_file_dropzone_exports)),
9072
+ "velin-form-summary": () => Promise.resolve().then(() => (init_velin_form_summary(), velin_form_summary_exports)),
7412
9073
  "velin-icon": () => Promise.resolve().then(() => (init_velin_icon(), velin_icon_exports)),
7413
9074
  "velin-lightbox": () => Promise.resolve().then(() => (init_velin_lightbox(), velin_lightbox_exports)),
7414
9075
  "velin-live-dot": () => Promise.resolve().then(() => (init_velin_live_dot(), velin_live_dot_exports)),
@@ -8024,6 +9685,10 @@
8024
9685
  init_velin_counter();
8025
9686
  init_velin_live_dot();
8026
9687
  init_velin_code_block();
9688
+ init_velin_data_table();
9689
+ init_velin_form_summary();
9690
+ init_velin_calendar();
9691
+ init_velin_file_dropzone();
8027
9692
  init_velin_search();
8028
9693
  init_velin_search();
8029
9694
 
@@ -8246,5 +9911,5 @@
8246
9911
  /*! Bundled license information:
8247
9912
 
8248
9913
  dompurify/dist/purify.es.mjs:
8249
- (*! @license DOMPurify 3.4.5 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.5/LICENSE *)
9914
+ (*! @license DOMPurify 3.4.12 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.12/LICENSE *)
8250
9915
  */