@birdapi/velinstyle 1.1.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 (308) hide show
  1. package/README.de.md +87 -162
  2. package/README.md +188 -119
  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-attributes.js +126 -10
  27. package/cli/docgen/extract-cli.js +3 -2
  28. package/cli/docgen/extract-components.js +61 -2
  29. package/cli/docgen/extract-utilities.js +10 -10
  30. package/cli/doctor.js +119 -0
  31. package/cli/index.js +531 -44
  32. package/cli/meta.js +84 -1
  33. package/cli/perf-audit.js +15 -2
  34. package/cli/prompt-engine.js +243 -0
  35. package/cli/review.js +268 -0
  36. package/cli/scaffold.js +119 -4
  37. package/cli/scanner-rules-data.js +7 -0
  38. package/cli/scanner.js +357 -4
  39. package/cli/serve.js +81 -0
  40. package/cli/skills.js +223 -0
  41. package/cli/wc-api.js +89 -0
  42. package/cli/workflow.js +42 -0
  43. package/components/index.js +2 -0
  44. package/components/runtime/component-loaders.js +2 -0
  45. package/components/velin-accordion.js +62 -13
  46. package/components/velin-bottom-nav.js +10 -0
  47. package/components/velin-calendar.js +212 -0
  48. package/components/velin-command.js +18 -2
  49. package/components/velin-data-table.js +78 -1
  50. package/components/velin-dialog.js +4 -1
  51. package/components/velin-drawer.js +2 -4
  52. package/components/velin-file-dropzone.js +162 -0
  53. package/components/velin-icon.js +22 -1
  54. package/components/velin-lightbox.js +5 -3
  55. package/components/velin-modal.js +2 -4
  56. package/components/velin-search.js +1 -1
  57. package/components/velin-sheet.js +2 -4
  58. package/core/a11y/component-contracts.json +20 -3
  59. package/core/meta/build.js +125 -5
  60. package/core/meta/design-constraints/contact.json +9 -0
  61. package/core/meta/design-constraints/faq.json +9 -0
  62. package/core/meta/design-constraints/hero.json +13 -0
  63. package/core/meta/knowledge/components.json +622 -0
  64. package/core/meta/knowledge/tokens.json +282 -0
  65. package/core/meta/pages/registry.json +294 -0
  66. package/core/meta/schema.js +3 -1
  67. package/core/meta/sections/registry.json +185 -0
  68. package/dist/chunks/{attributes-STYCS3O5.js → attributes-A423NX5J.js} +3 -3
  69. package/dist/chunks/{attributes-353XDOAX.js → attributes-F6UPIEVP.js} +1 -1
  70. package/dist/chunks/{attributes-ADOKLKA7.js → attributes-GLYXGGAL.js} +3 -3
  71. package/dist/chunks/{attributes-TPANLMLF.js → attributes-UFTYOSAU.js} +3 -3
  72. package/dist/chunks/attributes-XGLXLR2T.js +391 -0
  73. package/dist/chunks/attributes-YF4NI2QS.js +391 -0
  74. package/dist/chunks/browser-AUABBYUK.js +1544 -0
  75. package/dist/chunks/{chunk-RSMRR27R.js → chunk-2LSKX57P.js} +12 -10
  76. package/dist/chunks/{chunk-IEHKRARD.js → chunk-2TGCVDUL.js} +11 -9
  77. package/dist/chunks/{chunk-SRL6EWHP.js → chunk-5JWGUAPC.js} +17 -13
  78. package/dist/chunks/chunk-7YSO2OQ2.js +113 -0
  79. package/dist/chunks/{chunk-QVHYL3R4.js → chunk-BHR3ZG6U.js} +6 -6
  80. package/dist/chunks/{chunk-J472ERN2.js → chunk-JMSIHEYX.js} +15 -4
  81. package/dist/chunks/{chunk-MKZP2TBH.js → chunk-L7ZGUU4D.js} +2 -2
  82. package/dist/chunks/chunk-TUGIPKW5.js +113 -0
  83. package/dist/chunks/{highlight-GJHHV4KM.js → highlight-M2ELQNYK.js} +1 -1
  84. package/dist/chunks/runtime-entry.js +1 -1
  85. package/dist/chunks/{velin-accordion-FBIPAEXG.js → velin-accordion-WSY6BMLA.js} +64 -10
  86. package/dist/chunks/{velin-bottom-nav-7GEMPIM6.js → velin-bottom-nav-5RVRNGIP.js} +25 -0
  87. package/dist/chunks/{velin-bottom-nav-N3DFQR66.js → velin-bottom-nav-N5F4UO2I.js} +26 -1
  88. package/dist/chunks/velin-calendar-OFE6THUD.js +210 -0
  89. package/dist/chunks/velin-calendar-PGVNJTXK.js +217 -0
  90. package/dist/chunks/velin-calendar-XC7JRDSH.js +217 -0
  91. package/dist/chunks/{velin-code-block-CIAURDRJ.js → velin-code-block-ZGWG7JI5.js} +1 -1
  92. package/dist/chunks/{velin-combobox-NXS4QB4R.js → velin-combobox-56V2ZWT5.js} +1 -1
  93. package/dist/chunks/{velin-command-ONKO2A3J.js → velin-command-2HO2EKR6.js} +29 -2
  94. package/dist/chunks/{velin-command-TMLSRGIU.js → velin-command-ZJSLPK7B.js} +30 -3
  95. package/dist/chunks/{velin-copy-NYDBOKXT.js → velin-copy-XDQSV2XU.js} +1 -1
  96. package/dist/chunks/{velin-countdown-4ZVDGK4R.js → velin-countdown-V4Z5MA63.js} +1 -1
  97. package/dist/chunks/velin-data-table-UVR42UUM.js +372 -0
  98. package/dist/chunks/{velin-dialog-GTTZI564.js → velin-dialog-3YK2RPRV.js} +9 -2
  99. package/dist/chunks/{velin-dialog-ELFOZIXZ.js → velin-dialog-S24KAP4S.js} +3 -1
  100. package/dist/chunks/{velin-drawer-BE6TYN3H.js → velin-drawer-5PNU624J.js} +2 -4
  101. package/dist/chunks/{velin-drawer-DLL2GLT6.js → velin-drawer-CCM6S25D.js} +3 -5
  102. package/dist/chunks/{velin-email-RBX6ZR4S.js → velin-email-TNFONXPW.js} +1 -1
  103. package/dist/chunks/velin-file-dropzone-QRGF2JNA.js +154 -0
  104. package/dist/chunks/velin-file-dropzone-XIZMJ3NB.js +154 -0
  105. package/dist/chunks/{velin-icon-7M76DOPB.js → velin-icon-A6NCXGUK.js} +59 -17
  106. package/dist/chunks/{velin-icon-ZTSKUOVN.js → velin-icon-EC4B5XCM.js} +18 -5
  107. package/dist/chunks/{velin-lightbox-6E7CAATP.js → velin-lightbox-FFK4TUUP.js} +7 -6
  108. package/dist/chunks/{velin-lightbox-NXA3U2FM.js → velin-lightbox-P725FLTY.js} +5 -4
  109. package/dist/chunks/{velin-menubar-J66JXRWW.js → velin-menubar-6I2LM5HL.js} +1 -1
  110. package/dist/chunks/{velin-modal-5HYNNNUI.js → velin-modal-ICTMGRSU.js} +7 -8
  111. package/dist/chunks/{velin-modal-AEOTZ67O.js → velin-modal-JBNODH7D.js} +2 -4
  112. package/dist/chunks/{velin-popover-T72HK3DF.js → velin-popover-WZG2GHBL.js} +25 -4
  113. package/dist/chunks/{velin-rating-N4DI2XNL.js → velin-rating-QBBYRRBU.js} +1 -1
  114. package/dist/chunks/{velin-search-7B4KJ6TX.js → velin-search-B3L6J2RP.js} +2 -2
  115. package/dist/chunks/{velin-search-2XRQFBCS.js → velin-search-L3UG6NDQ.js} +5 -4
  116. package/dist/chunks/{velin-secure-field-6PV7RZDJ.js → velin-secure-field-DGYMP7OK.js} +1 -1
  117. package/dist/chunks/{velin-segmented-control-GYD5BIXQ.js → velin-segmented-control-P74GJPAR.js} +1 -1
  118. package/dist/chunks/{velin-sheet-R2Y67X6H.js → velin-sheet-M5GZZD7G.js} +2 -4
  119. package/dist/chunks/{velin-sheet-OUK6572T.js → velin-sheet-X7RXIWKV.js} +3 -5
  120. package/dist/chunks/{velin-stepper-X54WGHDG.js → velin-stepper-4YQJUHPC.js} +12 -5
  121. package/dist/chunks/{velin-toast-OMMOA2DK.js → velin-toast-UG5LWTG2.js} +5 -1
  122. package/dist/chunks/{velin-tooltip-BDHGRTQB.js → velin-tooltip-ZDH5SDLU.js} +1 -1
  123. package/dist/llms.txt +29 -7
  124. package/dist/search-index.json +156 -10
  125. package/dist/velin-agent.json +3973 -28
  126. package/dist/velinstyle-components.iife.js +1211 -284
  127. package/dist/velinstyle-components.js +1213 -284
  128. package/dist/velinstyle-components.min.js +266 -118
  129. package/dist/velinstyle.css +252 -67
  130. package/dist/velinstyle.d.ts +2 -0
  131. package/dist/velinstyle.min.css +1 -1
  132. package/package.json +20 -6
  133. package/packages/skill-engine/README.md +24 -0
  134. package/packages/skill-engine/package.json +7 -0
  135. package/packages/skill-engine/src/catalog.js +29 -0
  136. package/packages/skill-engine/src/index.js +5 -0
  137. package/packages/skill-engine/src/install.js +69 -0
  138. package/packages/skill-engine/src/registry.js +27 -0
  139. package/packages/skill-engine/src/resolve.js +68 -0
  140. package/packages/skill-engine/src/validate.js +40 -0
  141. package/packages/velinstyle-skills/README.md +6 -0
  142. package/packages/velinstyle-skills/adapter.json +8 -0
  143. package/packages/velinstyle-skills/bundles/landing-starter.bundle.yaml +12 -0
  144. package/packages/velinstyle-skills/bundles/review-suite.bundle.yaml +11 -0
  145. package/packages/velinstyle-skills/catalog.json +1004 -0
  146. package/packages/velinstyle-skills/graphs/component-ship.graph.yaml +14 -0
  147. package/packages/velinstyle-skills/graphs/landingpage.graph.yaml +21 -0
  148. package/packages/velinstyle-skills/graphs/release-gate.graph.yaml +15 -0
  149. package/packages/velinstyle-skills/index.js +22 -0
  150. package/packages/velinstyle-skills/package.json +7 -0
  151. package/packages/velinstyle-skills/packs/accessibility.pack.yaml +9 -0
  152. package/packages/velinstyle-skills/packs/design.pack.yaml +10 -0
  153. package/packages/velinstyle-skills/packs/documentation.pack.yaml +8 -0
  154. package/packages/velinstyle-skills/packs/enterprise.pack.yaml +10 -0
  155. package/packages/velinstyle-skills/packs/frontend.pack.yaml +10 -0
  156. package/packages/velinstyle-skills/packs/review.pack.yaml +9 -0
  157. package/packages/velinstyle-skills/projects/admin-panel.project.yaml +12 -0
  158. package/packages/velinstyle-skills/projects/blog.project.yaml +11 -0
  159. package/packages/velinstyle-skills/projects/documentation-portal.project.yaml +13 -0
  160. package/packages/velinstyle-skills/projects/ecommerce-shop.project.yaml +12 -0
  161. package/packages/velinstyle-skills/projects/landing-page.project.yaml +12 -0
  162. package/packages/velinstyle-skills/projects/laravel-dashboard.project.yaml +12 -0
  163. package/packages/velinstyle-skills/projects/marketing-website.project.yaml +14 -0
  164. package/packages/velinstyle-skills/projects/portfolio.project.yaml +12 -0
  165. package/packages/velinstyle-skills/registry.json +142 -0
  166. package/packages/velinstyle-skills/scripts/generate-assets.mjs +163 -0
  167. package/packages/velinstyle-skills/skills/velin-a11y-focus-keyboard/SKILL.md +57 -0
  168. package/packages/velinstyle-skills/skills/velin-a11y-page-checklist/SKILL.md +57 -0
  169. package/packages/velinstyle-skills/skills/velin-a11y-scan-fix/SKILL.md +57 -0
  170. package/packages/velinstyle-skills/skills/velin-ai-agent-briefing/SKILL.md +57 -0
  171. package/packages/velinstyle-skills/skills/velin-ai-plan-scaffold-review/SKILL.md +57 -0
  172. package/packages/velinstyle-skills/skills/velin-ai-skill-authoring/SKILL.md +57 -0
  173. package/packages/velinstyle-skills/skills/velin-components-accessibility-map/SKILL.md +57 -0
  174. package/packages/velinstyle-skills/skills/velin-components-api-contract/SKILL.md +57 -0
  175. package/packages/velinstyle-skills/skills/velin-components-recipe/SKILL.md +57 -0
  176. package/packages/velinstyle-skills/skills/velin-components-select-compose/SKILL.md +57 -0
  177. package/packages/velinstyle-skills/skills/velin-design-hero-compose/SKILL.md +57 -0
  178. package/packages/velinstyle-skills/skills/velin-design-section-rhythm/SKILL.md +57 -0
  179. package/packages/velinstyle-skills/skills/velin-design-surface-roles/SKILL.md +57 -0
  180. package/packages/velinstyle-skills/skills/velin-dev-component-wire/SKILL.md +57 -0
  181. package/packages/velinstyle-skills/skills/velin-dev-page-scaffold/SKILL.md +57 -0
  182. package/packages/velinstyle-skills/skills/velin-dev-plan-first/SKILL.md +57 -0
  183. package/packages/velinstyle-skills/skills/velin-docs-component-guide/SKILL.md +57 -0
  184. package/packages/velinstyle-skills/skills/velin-docs-generate-api/SKILL.md +57 -0
  185. package/packages/velinstyle-skills/skills/velin-dx-doctor-setup/SKILL.md +57 -0
  186. package/packages/velinstyle-skills/skills/velin-dx-project-init/SKILL.md +57 -0
  187. package/packages/velinstyle-skills/skills/velin-migration-legacy-wc/SKILL.md +57 -0
  188. package/packages/velinstyle-skills/skills/velin-migration-prefix-classes/SKILL.md +57 -0
  189. package/packages/velinstyle-skills/skills/velin-motion-reduced-safe/SKILL.md +57 -0
  190. package/packages/velinstyle-skills/skills/velin-motion-reveal-feedback/SKILL.md +57 -0
  191. package/packages/velinstyle-skills/skills/velin-perf-audit-fix/SKILL.md +57 -0
  192. package/packages/velinstyle-skills/skills/velin-perf-runtime-tree-shake/SKILL.md +57 -0
  193. package/packages/velinstyle-skills/skills/velin-release-changelog-sync/SKILL.md +57 -0
  194. package/packages/velinstyle-skills/skills/velin-release-meta-rebuild/SKILL.md +57 -0
  195. package/packages/velinstyle-skills/skills/velin-review-conversion-hero/SKILL.md +57 -0
  196. package/packages/velinstyle-skills/skills/velin-review-page-gate/SKILL.md +57 -0
  197. package/packages/velinstyle-skills/skills/velin-review-seo-meta/SKILL.md +57 -0
  198. package/packages/velinstyle-skills/skills/velin-security-sanitize-html/SKILL.md +57 -0
  199. package/packages/velinstyle-skills/skills/velin-security-scan-pii/SKILL.md +57 -0
  200. package/packages/velinstyle-skills/skills/velin-testing-a11y-suite/SKILL.md +57 -0
  201. package/packages/velinstyle-skills/skills/velin-testing-visual-smoke/SKILL.md +57 -0
  202. package/packages/velinstyle-skills/skills/velin-themes-apply-token/SKILL.md +57 -0
  203. package/packages/velinstyle-skills/skills/velin-themes-contrast-check/SKILL.md +57 -0
  204. package/packages/velinstyle-skills/skills/velin-utilities-layout-apply/SKILL.md +57 -0
  205. package/packages/velinstyle-skills/skills/velin-utilities-prefix-fix/SKILL.md +57 -0
  206. package/packages/velinstyle-skills/skills/velin-utilities-token-map/SKILL.md +57 -0
  207. package/packages/velinstyle-skills/templates/admin-panel.template.yaml +12 -0
  208. package/packages/velinstyle-skills/templates/blog.template.yaml +12 -0
  209. package/packages/velinstyle-skills/templates/documentation-portal.template.yaml +12 -0
  210. package/packages/velinstyle-skills/templates/ecommerce-shop.template.yaml +13 -0
  211. package/packages/velinstyle-skills/templates/landing-page.template.yaml +12 -0
  212. package/packages/velinstyle-skills/templates/laravel-dashboard.template.yaml +11 -0
  213. package/packages/velinstyle-skills/templates/marketing-website.template.yaml +13 -0
  214. package/packages/velinstyle-skills/templates/portfolio.template.yaml +12 -0
  215. package/schemas/component-knowledge.schema.json +33 -0
  216. package/schemas/design-constraint.schema.json +15 -0
  217. package/schemas/design-intelligence.schema.json +19 -0
  218. package/schemas/design-rule.schema.json +19 -0
  219. package/schemas/page-template.schema.json +24 -0
  220. package/schemas/review-report.schema.json +36 -0
  221. package/schemas/section.schema.json +22 -0
  222. package/schemas/skill-bundle.schema.json +15 -0
  223. package/schemas/skill-graph.schema.json +25 -0
  224. package/schemas/skill-pack.schema.json +14 -0
  225. package/schemas/skill-project.schema.json +16 -0
  226. package/schemas/skill-record.schema.json +27 -0
  227. package/schemas/skill-registry.schema.json +17 -0
  228. package/schemas/skill-template.schema.json +17 -0
  229. package/schemas/token-graph.schema.json +27 -0
  230. package/schemas/utility.schema.json +166 -0
  231. package/src/components/alert.css +22 -1
  232. package/src/components/calendar-dropzone.css +5 -0
  233. package/src/layout/app-shell.css +33 -0
  234. package/src/tokens/shadow.css +13 -0
  235. package/src/utilities/border.css +2 -0
  236. package/src/utilities/display.css +1 -24
  237. package/src/utilities/responsive.css +16 -1
  238. package/src/utilities/spacing.css +32 -0
  239. package/src/velinstyle.css +2 -0
  240. package/dist/chunks/attributes-3BZLI7XF.js +0 -364
  241. package/dist/chunks/attributes-4N5JP4CG.js +0 -364
  242. package/dist/chunks/attributes-5Q2WXGRC.js +0 -374
  243. package/dist/chunks/attributes-AIR7SVLK.js +0 -364
  244. package/dist/chunks/attributes-BGRN5XZO.js +0 -364
  245. package/dist/chunks/attributes-BVSQAYLR.js +0 -364
  246. package/dist/chunks/attributes-CDYFUY7Y.js +0 -364
  247. package/dist/chunks/attributes-JMYJZBPE.js +0 -364
  248. package/dist/chunks/attributes-R5JY3X4O.js +0 -374
  249. package/dist/chunks/attributes-TNSPQTHX.js +0 -374
  250. package/dist/chunks/chunk-2354NEBP.js +0 -109
  251. package/dist/chunks/chunk-2VNA7G7S.js +0 -109
  252. package/dist/chunks/chunk-AQLRJSGG.js +0 -109
  253. package/dist/chunks/chunk-EWPSDMQT.js +0 -109
  254. package/dist/chunks/chunk-LA7264K4.js +0 -85
  255. package/dist/chunks/chunk-QTJJ3DPO.js +0 -109
  256. package/dist/chunks/chunk-RZQVWFHW.js +0 -109
  257. package/dist/chunks/chunk-U4XHMZ7D.js +0 -109
  258. package/dist/chunks/chunk-V5YIUA6G.js +0 -109
  259. package/dist/chunks/chunk-XIET2VSZ.js +0 -109
  260. package/dist/chunks/chunk-XYLJ2GCB.js +0 -205
  261. package/dist/chunks/chunk-ZPSPKUYD.js +0 -109
  262. package/dist/chunks/highlight-XTHRKBA3.js +0 -37
  263. package/dist/chunks/velin-bottom-nav-2N2TYWOF.js +0 -86
  264. package/dist/chunks/velin-carousel-Y5VCOABZ.js +0 -213
  265. package/dist/chunks/velin-code-block-ASUBTAQV.js +0 -119
  266. package/dist/chunks/velin-code-block-TSUKPY4C.js +0 -123
  267. package/dist/chunks/velin-code-block-Y6CM726Q.js +0 -119
  268. package/dist/chunks/velin-combobox-2KDTCQ7N.js +0 -155
  269. package/dist/chunks/velin-command-M7RNHUPR.js +0 -136
  270. package/dist/chunks/velin-copy-T5KDTDXP.js +0 -81
  271. package/dist/chunks/velin-countdown-5XSTN3BH.js +0 -101
  272. package/dist/chunks/velin-counter-ISCTEAHI.js +0 -118
  273. package/dist/chunks/velin-dialog-D2GUHSCD.js +0 -144
  274. package/dist/chunks/velin-dialog-IJLFDXAJ.js +0 -144
  275. package/dist/chunks/velin-dialog-TNS3WXMB.js +0 -144
  276. package/dist/chunks/velin-drawer-MJS6WCF7.js +0 -130
  277. package/dist/chunks/velin-email-N5B5ZYTE.js +0 -90
  278. package/dist/chunks/velin-icon-KZO3IEHG.js +0 -164
  279. package/dist/chunks/velin-lightbox-I75U6JF4.js +0 -140
  280. package/dist/chunks/velin-lightbox-QOTB4PRL.js +0 -140
  281. package/dist/chunks/velin-lightbox-UCD6N7HF.js +0 -140
  282. package/dist/chunks/velin-live-dot-XHUZCIDZ.js +0 -67
  283. package/dist/chunks/velin-menubar-SFEXJE56.js +0 -86
  284. package/dist/chunks/velin-modal-2HOBXE6F.js +0 -176
  285. package/dist/chunks/velin-modal-GLI2XZZB.js +0 -176
  286. package/dist/chunks/velin-popover-EXINRIGJ.js +0 -145
  287. package/dist/chunks/velin-popover-OXX7MRZ6.js +0 -145
  288. package/dist/chunks/velin-popover-YKCCROJG.js +0 -145
  289. package/dist/chunks/velin-rating-XRQJV4LO.js +0 -91
  290. package/dist/chunks/velin-scroll-top-F2FCZNYD.js +0 -58
  291. package/dist/chunks/velin-search-4YHJWYSS.js +0 -556
  292. package/dist/chunks/velin-search-LWHY23Q5.js +0 -474
  293. package/dist/chunks/velin-secure-field-H7FSBUJW.js +0 -95
  294. package/dist/chunks/velin-segmented-control-PQX6LKOJ.js +0 -109
  295. package/dist/chunks/velin-sheet-3OGI4HD7.js +0 -119
  296. package/dist/chunks/velin-sparkline-GVEANJVW.js +0 -184
  297. package/dist/chunks/velin-sparkline-KDA3GQAN.js +0 -162
  298. package/dist/chunks/velin-stepper-2ZIHLKGM.js +0 -141
  299. package/dist/chunks/velin-stepper-FYEUEWE6.js +0 -141
  300. package/dist/chunks/velin-stepper-wc-QU3KRVQR.js +0 -133
  301. package/dist/chunks/velin-theme-toggle-BNNS7WLV.js +0 -285
  302. package/dist/chunks/velin-theme-toggle-NO7V4TWI.js +0 -285
  303. package/dist/chunks/velin-theme-toggle-QELCJM2Q.js +0 -291
  304. package/dist/chunks/velin-toast-RCZ4CSUC.js +0 -109
  305. package/dist/chunks/velin-toast-WEUBTJG6.js +0 -109
  306. package/dist/chunks/velin-tooltip-664IOHZA.js +0 -112
  307. package/dist/chunks/velin-tooltip-wc-LXDVT6DE.js +0 -103
  308. package/dist/chunks/worker-client-R6DBYPFT.js +0 -46
@@ -0,0 +1,212 @@
1
+ import { escapeHTML } from './sanitize.js';
2
+
3
+ const styles = `
4
+ :host { display: block; max-inline-size: 100%; }
5
+ .cal {
6
+ box-sizing: border-box;
7
+ border: 1px solid var(--velin-color-border, #ddd);
8
+ border-radius: var(--velin-radius-md, 0.5rem);
9
+ background: var(--velin-color-surface, #fff);
10
+ color: var(--velin-color-text, #111);
11
+ padding: var(--velin-space-3, 0.75rem);
12
+ max-inline-size: min(20rem, 100%);
13
+ inline-size: 100%;
14
+ }
15
+ .head {
16
+ display: flex; align-items: center; justify-content: space-between;
17
+ gap: var(--velin-space-2, 0.5rem); margin-block-end: var(--velin-space-3, 0.75rem);
18
+ }
19
+ .title { font-weight: var(--velin-weight-semibold, 600); margin: 0; font-size: var(--velin-text-base, 1rem); }
20
+ .nav {
21
+ display: inline-flex; align-items: center; justify-content: center;
22
+ flex-shrink: 0;
23
+ min-inline-size: 2.5rem; min-block-size: 2.5rem;
24
+ border: 1px solid var(--velin-color-border, #ddd);
25
+ border-radius: var(--velin-radius-sm, 0.25rem);
26
+ background: var(--velin-color-surface-bright, #fff);
27
+ color: inherit; cursor: pointer;
28
+ }
29
+ .nav:focus-visible { outline: 2px solid var(--velin-color-focus, #2563eb); outline-offset: 2px; }
30
+ .grid {
31
+ display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 0.25rem;
32
+ }
33
+ .dow {
34
+ text-align: center; font-size: var(--velin-text-xs, 0.75rem);
35
+ color: var(--velin-color-text-muted, #64748b); padding-block: 0.25rem;
36
+ }
37
+ .day {
38
+ inline-size: 100%;
39
+ min-inline-size: 0;
40
+ min-block-size: 2.25rem;
41
+ aspect-ratio: 1;
42
+ border: none; border-radius: var(--velin-radius-sm, 0.25rem);
43
+ background: transparent; color: inherit; cursor: pointer;
44
+ font: inherit;
45
+ padding: 0;
46
+ }
47
+ .day[aria-disabled="true"] { opacity: 0.35; cursor: not-allowed; }
48
+ .day[aria-selected="true"] {
49
+ background: var(--velin-color-primary, #2563eb);
50
+ color: var(--velin-color-on-primary, #fff);
51
+ }
52
+ .day:not([aria-disabled="true"]):hover {
53
+ background: var(--velin-color-primary-subtle, #eff6ff);
54
+ }
55
+ .day:focus-visible { outline: 2px solid var(--velin-color-focus, #2563eb); outline-offset: 1px; }
56
+ .out { color: var(--velin-color-text-muted, #94a3b8); }
57
+ `;
58
+
59
+ function iso(d) {
60
+ const y = d.getFullYear();
61
+ const m = String(d.getMonth() + 1).padStart(2, '0');
62
+ const day = String(d.getDate()).padStart(2, '0');
63
+ return `${y}-${m}-${day}`;
64
+ }
65
+
66
+ function parseISO(v) {
67
+ if (!v) return null;
68
+ const m = String(v).match(/^(\d{4})-(\d{2})-(\d{2})$/);
69
+ if (!m) return null;
70
+ const d = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
71
+ return Number.isNaN(d.getTime()) ? null : d;
72
+ }
73
+
74
+ class VelinCalendar extends HTMLElement {
75
+ static get observedAttributes() {
76
+ return ['value', 'min', 'max', 'label'];
77
+ }
78
+
79
+ constructor() {
80
+ super();
81
+ this.attachShadow({ mode: 'open' });
82
+ this._view = new Date();
83
+ this._view.setDate(1);
84
+ this._onKey = this._onKey.bind(this);
85
+ }
86
+
87
+ connectedCallback() {
88
+ const selected = parseISO(this.getAttribute('value')) || new Date();
89
+ this._view = new Date(selected.getFullYear(), selected.getMonth(), 1);
90
+ this._render();
91
+ }
92
+
93
+ attributeChangedCallback(name, prev, next) {
94
+ if (prev === next || !this.shadowRoot?.querySelector('.cal')) return;
95
+ if (name === 'value' && next) {
96
+ const d = parseISO(next);
97
+ if (d) this._view = new Date(d.getFullYear(), d.getMonth(), 1);
98
+ }
99
+ this._render();
100
+ }
101
+
102
+ get value() {
103
+ return this.getAttribute('value') || '';
104
+ }
105
+
106
+ set value(v) {
107
+ if (v) this.setAttribute('value', v);
108
+ else this.removeAttribute('value');
109
+ }
110
+
111
+ _inRange(d) {
112
+ const min = parseISO(this.getAttribute('min'));
113
+ const max = parseISO(this.getAttribute('max'));
114
+ if (min && d < min) return false;
115
+ if (max && d > max) return false;
116
+ return true;
117
+ }
118
+
119
+ _select(d) {
120
+ if (!this._inRange(d)) return;
121
+ const v = iso(d);
122
+ this.setAttribute('value', v);
123
+ this.dispatchEvent(new CustomEvent('velin-change', { bubbles: true, detail: { value: v, date: d } }));
124
+ this._render();
125
+ }
126
+
127
+ _shiftMonth(delta) {
128
+ this._view = new Date(this._view.getFullYear(), this._view.getMonth() + delta, 1);
129
+ this._render();
130
+ }
131
+
132
+ _onKey(e) {
133
+ const selected = parseISO(this.getAttribute('value')) || new Date();
134
+ let next = new Date(selected);
135
+ switch (e.key) {
136
+ case 'ArrowLeft': next.setDate(next.getDate() - 1); break;
137
+ case 'ArrowRight': next.setDate(next.getDate() + 1); break;
138
+ case 'ArrowUp': next.setDate(next.getDate() - 7); break;
139
+ case 'ArrowDown': next.setDate(next.getDate() + 7); break;
140
+ case 'Home': next = new Date(next.getFullYear(), next.getMonth(), 1); break;
141
+ case 'End': next = new Date(next.getFullYear(), next.getMonth() + 1, 0); break;
142
+ case 'PageUp': next = new Date(next.getFullYear(), next.getMonth() - 1, next.getDate()); break;
143
+ case 'PageDown': next = new Date(next.getFullYear(), next.getMonth() + 1, next.getDate()); break;
144
+ case 'Enter':
145
+ case ' ':
146
+ e.preventDefault();
147
+ this._select(selected);
148
+ return;
149
+ default:
150
+ return;
151
+ }
152
+ e.preventDefault();
153
+ if (!this._inRange(next)) return;
154
+ this._view = new Date(next.getFullYear(), next.getMonth(), 1);
155
+ this.setAttribute('value', iso(next));
156
+ this.dispatchEvent(new CustomEvent('velin-change', { bubbles: true, detail: { value: iso(next), date: next } }));
157
+ this._render();
158
+ this.shadowRoot.querySelector(`[data-iso="${iso(next)}"]`)?.focus();
159
+ }
160
+
161
+ _render() {
162
+ const label = escapeHTML(this.getAttribute('label') || 'Choose date');
163
+ const selected = parseISO(this.getAttribute('value'));
164
+ const y = this._view.getFullYear();
165
+ const m = this._view.getMonth();
166
+ const title = this._view.toLocaleString(undefined, { month: 'long', year: 'numeric' });
167
+ const start = new Date(y, m, 1);
168
+ const startDow = (start.getDay() + 6) % 7; // Monday-first
169
+ const daysInMonth = new Date(y, m + 1, 0).getDate();
170
+ const dows = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'];
171
+
172
+ let cells = dows.map((d) => `<div class="dow" aria-hidden="true">${d}</div>`).join('');
173
+ for (let i = 0; i < startDow; i += 1) {
174
+ const d = new Date(y, m, -startDow + i + 1);
175
+ cells += `<button type="button" class="day out" tabindex="-1" data-iso="${iso(d)}" aria-label="${iso(d)}">${d.getDate()}</button>`;
176
+ }
177
+ for (let day = 1; day <= daysInMonth; day += 1) {
178
+ const d = new Date(y, m, day);
179
+ const id = iso(d);
180
+ const sel = selected && iso(selected) === id;
181
+ const disabled = !this._inRange(d);
182
+ 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>`;
183
+ }
184
+
185
+ this.shadowRoot.innerHTML = `
186
+ <style>${styles}</style>
187
+ <div class="cal" role="group" aria-label="${label}">
188
+ <div class="head">
189
+ <button type="button" class="nav" data-nav="-1" aria-label="Previous month">‹</button>
190
+ <p class="title" id="cal-title">${escapeHTML(title)}</p>
191
+ <button type="button" class="nav" data-nav="1" aria-label="Next month">›</button>
192
+ </div>
193
+ <div class="grid" role="grid" aria-labelledby="cal-title">${cells}</div>
194
+ </div>
195
+ `;
196
+
197
+ this.shadowRoot.querySelectorAll('[data-nav]').forEach((btn) => {
198
+ btn.addEventListener('click', () => this._shiftMonth(Number(btn.getAttribute('data-nav'))));
199
+ });
200
+ this.shadowRoot.querySelectorAll('.day').forEach((btn) => {
201
+ btn.addEventListener('click', () => {
202
+ if (btn.getAttribute('aria-disabled') === 'true') return;
203
+ const d = parseISO(btn.getAttribute('data-iso'));
204
+ if (d) this._select(d);
205
+ });
206
+ btn.addEventListener('keydown', this._onKey);
207
+ });
208
+ }
209
+ }
210
+
211
+ customElements.define('velin-calendar', VelinCalendar);
212
+ export default VelinCalendar;
@@ -11,9 +11,14 @@ const styles = `
11
11
  padding: 10vh var(--velin-space-4, 1rem) var(--velin-space-4, 1rem);
12
12
  background: var(--velin-color-overlay, rgba(0,0,0,0.4));
13
13
  opacity: 0; visibility: hidden;
14
+ pointer-events: none;
14
15
  transition: opacity 150ms ease, visibility 150ms ease;
15
16
  }
16
- :host([open]) .overlay { opacity: 1; visibility: visible; }
17
+ :host([open]) .overlay {
18
+ opacity: 1;
19
+ visibility: visible;
20
+ pointer-events: auto;
21
+ }
17
22
  .panel {
18
23
  inline-size: min(32rem, 100%);
19
24
  background: var(--velin-color-surface-bright, #fff);
@@ -66,6 +71,7 @@ class VelinCommand extends HTMLElement {
66
71
  </div>
67
72
  `;
68
73
  this.shadowRoot.querySelector('.search').addEventListener('input', this._onInput);
74
+ this.shadowRoot.querySelector('.overlay')?.addEventListener('click', this._onOverlayClick);
69
75
  this.shadowRoot.querySelector('slot')?.addEventListener('slotchange', () => this._filter(''));
70
76
  this._filter('');
71
77
  }
@@ -79,6 +85,14 @@ class VelinCommand extends HTMLElement {
79
85
  this.removeAttribute('open');
80
86
  this.dispatchEvent(new CustomEvent('velin-close', { bubbles: true }));
81
87
  }
88
+ toggle() {
89
+ if (this.hasAttribute('open')) this.close();
90
+ else this.open();
91
+ }
92
+
93
+ _onOverlayClick = (event) => {
94
+ if (event.target === event.currentTarget) this.close();
95
+ };
82
96
 
83
97
  _open() {
84
98
  this._prev = saveFocus();
@@ -92,7 +106,7 @@ class VelinCommand extends HTMLElement {
92
106
 
93
107
  _close() {
94
108
  document.removeEventListener('keydown', this._onKey);
95
- clearBackgroundInert();
109
+ clearBackgroundInert(this);
96
110
  restoreFocus(this._prev);
97
111
  const input = this.shadowRoot.querySelector('.search');
98
112
  if (input) input.value = '';
@@ -135,6 +149,8 @@ class VelinCommand extends HTMLElement {
135
149
 
136
150
  disconnectedCallback() {
137
151
  document.removeEventListener('keydown', this._onKey);
152
+ this.shadowRoot?.querySelector('.overlay')?.removeEventListener('click', this._onOverlayClick);
153
+ if (this.hasAttribute('open')) clearBackgroundInert(this);
138
154
  }
139
155
  }
140
156
 
@@ -29,7 +29,7 @@ function sortValue(cell, type) {
29
29
 
30
30
  class VelinDataTable extends HTMLElement {
31
31
  static get observedAttributes() {
32
- return ['page-size', 'filter-input', 'empty-text', 'label'];
32
+ return ['page-size', 'filter-input', 'empty-text', 'label', 'editable'];
33
33
  }
34
34
 
35
35
  constructor() {
@@ -180,6 +180,7 @@ class VelinDataTable extends HTMLElement {
180
180
 
181
181
  this._ensureAccessibleName();
182
182
  this._setupSorting();
183
+ this._setupEditable();
183
184
  this._bindFilterInput();
184
185
  this._render();
185
186
  }
@@ -246,6 +247,82 @@ class VelinDataTable extends HTMLElement {
246
247
  });
247
248
  }
248
249
 
250
+ /**
251
+ * Inline edit: double-click or Enter on focused cell with data-editable / host editable.
252
+ * Enter saves, Escape cancels. Dispatches velin-data-table-edit.
253
+ */
254
+ _setupEditable() {
255
+ if (!this.hasAttribute('editable') && !this.querySelector('[data-editable]')) return;
256
+ const body = this._table.tBodies[0];
257
+ if (!body || body.dataset.velinEditBound) return;
258
+ body.dataset.velinEditBound = 'true';
259
+
260
+ body.addEventListener('dblclick', (e) => {
261
+ const cell = e.target.closest('td');
262
+ if (!cell || !body.contains(cell)) return;
263
+ if (!this.hasAttribute('editable') && !cell.hasAttribute('data-editable')) return;
264
+ this._beginEdit(cell);
265
+ });
266
+
267
+ body.addEventListener('keydown', (e) => {
268
+ const cell = e.target.closest('td');
269
+ if (!cell || e.target !== cell) return;
270
+ if (e.key === 'Enter' || e.key === 'F2') {
271
+ if (!this.hasAttribute('editable') && !cell.hasAttribute('data-editable')) return;
272
+ e.preventDefault();
273
+ this._beginEdit(cell);
274
+ }
275
+ });
276
+
277
+ for (const cell of body.querySelectorAll('td[data-editable], td')) {
278
+ if (!this.hasAttribute('editable') && !cell.hasAttribute('data-editable')) continue;
279
+ if (!cell.hasAttribute('tabindex')) cell.setAttribute('tabindex', '0');
280
+ }
281
+ }
282
+
283
+ /** @param {HTMLTableCellElement} cell */
284
+ _beginEdit(cell) {
285
+ if (cell.querySelector('input')) return;
286
+ const previous = cell.textContent.trim();
287
+ const input = document.createElement('input');
288
+ input.className = 'velin-input velin-data-table__edit';
289
+ input.value = previous;
290
+ input.setAttribute('aria-label', 'Edit cell');
291
+ cell.textContent = '';
292
+ cell.appendChild(input);
293
+ input.focus();
294
+ input.select();
295
+
296
+ const commit = (save) => {
297
+ const next = save ? input.value.trim() : previous;
298
+ cell.textContent = next;
299
+ if (save && next !== previous) {
300
+ this.dispatchEvent(new CustomEvent('velin-data-table-edit', {
301
+ bubbles: true,
302
+ detail: {
303
+ cell,
304
+ row: cell.parentElement,
305
+ previous,
306
+ value: next,
307
+ columnIndex: cell.cellIndex,
308
+ },
309
+ }));
310
+ announce('Cell updated');
311
+ }
312
+ };
313
+
314
+ input.addEventListener('keydown', (e) => {
315
+ if (e.key === 'Enter') {
316
+ e.preventDefault();
317
+ commit(true);
318
+ } else if (e.key === 'Escape') {
319
+ e.preventDefault();
320
+ commit(false);
321
+ }
322
+ });
323
+ input.addEventListener('blur', () => commit(true));
324
+ }
325
+
249
326
  _syncSortState() {
250
327
  this._headers().forEach((header, index) => {
251
328
  if (this._sortType(header) === 'none') return;
@@ -150,7 +150,10 @@ class VelinDialog extends HTMLElement {
150
150
  }
151
151
  this._resolve(value);
152
152
  this._resolve = null;
153
- this.dispatchEvent(new CustomEvent('velin-dialog-close', { bubbles: true, detail: { value } }));
153
+ const detail = { value };
154
+ this.dispatchEvent(new CustomEvent('velin-close', { bubbles: true, detail }));
155
+ // Deprecated alias — remove after deprecation window (ADR 0012)
156
+ this.dispatchEvent(new CustomEvent('velin-dialog-close', { bubbles: true, detail }));
154
157
  }
155
158
  }
156
159
 
@@ -88,7 +88,6 @@ class VelinDrawer extends HTMLElement {
88
88
  this._prev = saveFocus();
89
89
  setBackgroundInert(this);
90
90
  document.addEventListener('keydown', this._onKey);
91
- document.body.style.overflow = 'hidden';
92
91
  requestAnimationFrame(() => {
93
92
  const f = getFocusableElements(this.shadowRoot);
94
93
  if (f.length) f[0].focus();
@@ -97,8 +96,7 @@ class VelinDrawer extends HTMLElement {
97
96
 
98
97
  _close() {
99
98
  document.removeEventListener('keydown', this._onKey);
100
- document.body.style.overflow = '';
101
- clearBackgroundInert();
99
+ clearBackgroundInert(this);
102
100
  restoreFocus(this._prev);
103
101
  }
104
102
 
@@ -109,7 +107,7 @@ class VelinDrawer extends HTMLElement {
109
107
 
110
108
  disconnectedCallback() {
111
109
  document.removeEventListener('keydown', this._onKey);
112
- document.body.style.overflow = '';
110
+ clearBackgroundInert(this);
113
111
  }
114
112
  }
115
113
 
@@ -0,0 +1,162 @@
1
+ import { escapeHTML } from './sanitize.js';
2
+
3
+ const styles = `
4
+ :host { display: block; }
5
+ .zone {
6
+ border: 2px dashed var(--velin-color-border, #cbd5e1);
7
+ border-radius: var(--velin-radius-md, 0.5rem);
8
+ padding: var(--velin-space-6, 1.5rem);
9
+ background: var(--velin-color-surface-dim, var(--velin-color-bg-subtle, #f8fafc));
10
+ color: var(--velin-color-text, #111);
11
+ text-align: center;
12
+ transition: border-color 150ms ease, background 150ms ease;
13
+ }
14
+ :host([dragging]) .zone {
15
+ border-color: var(--velin-color-primary, #2563eb);
16
+ background: var(--velin-color-primary-subtle, #eff6ff);
17
+ }
18
+ .hint { margin: 0 0 var(--velin-space-3, 0.75rem); color: var(--velin-color-text-muted, #64748b); }
19
+ .browse {
20
+ display: inline-flex; align-items: center; justify-content: center;
21
+ min-block-size: 2.75rem; padding-inline: var(--velin-space-4, 1rem);
22
+ border-radius: var(--velin-radius-md, 0.5rem);
23
+ border: none; cursor: pointer;
24
+ background: var(--velin-color-primary, #2563eb);
25
+ color: var(--velin-color-on-primary, #fff);
26
+ font: inherit;
27
+ }
28
+ .browse:focus-visible { outline: 2px solid var(--velin-color-focus, #2563eb); outline-offset: 2px; }
29
+ input[type="file"] {
30
+ position: absolute; inline-size: 1px; block-size: 1px; padding: 0; margin: -1px;
31
+ overflow: hidden; clip: rect(0,0,0,0); border: 0;
32
+ }
33
+ .list { list-style: none; padding: 0; margin: var(--velin-space-4, 1rem) 0 0; text-align: start; }
34
+ .list li {
35
+ display: flex; justify-content: space-between; gap: var(--velin-space-3, 0.75rem);
36
+ padding: var(--velin-space-2, 0.5rem) 0;
37
+ border-block-end: 1px solid var(--velin-color-border, #e2e8f0);
38
+ font-size: var(--velin-text-sm, 0.875rem);
39
+ }
40
+ .progress {
41
+ margin-block-start: var(--velin-space-3, 0.75rem);
42
+ block-size: 0.5rem; border-radius: 999px;
43
+ background: var(--velin-color-border, #e2e8f0); overflow: hidden;
44
+ }
45
+ .bar {
46
+ block-size: 100%; inline-size: 0%;
47
+ background: var(--velin-color-primary, #2563eb);
48
+ transition: inline-size 150ms ease;
49
+ }
50
+ .status { margin: var(--velin-space-2, 0.5rem) 0 0; font-size: var(--velin-text-sm, 0.875rem); }
51
+ .status[data-tone="error"] { color: var(--velin-color-danger, #b91c1c); }
52
+ `;
53
+
54
+ class VelinFileDropzone extends HTMLElement {
55
+ static get observedAttributes() {
56
+ return ['accept', 'multiple', 'label', 'progress'];
57
+ }
58
+
59
+ constructor() {
60
+ super();
61
+ this.attachShadow({ mode: 'open' });
62
+ this._files = [];
63
+ }
64
+
65
+ connectedCallback() {
66
+ this._render();
67
+ }
68
+
69
+ attributeChangedCallback() {
70
+ if (this.shadowRoot?.querySelector('.zone')) this._render();
71
+ }
72
+
73
+ get files() {
74
+ return this._files.slice();
75
+ }
76
+
77
+ _emitFiles(fileList) {
78
+ const accept = this.getAttribute('accept');
79
+ const files = [...fileList];
80
+ if (accept) {
81
+ const parts = accept.split(',').map((s) => s.trim().toLowerCase());
82
+ const bad = files.find((f) => {
83
+ const name = f.name.toLowerCase();
84
+ const type = (f.type || '').toLowerCase();
85
+ return !parts.some((p) => (p.startsWith('.') ? name.endsWith(p) : type === p || type.startsWith(p.replace('/*', '/'))));
86
+ });
87
+ if (bad) {
88
+ this._setStatus(`File type not allowed: ${bad.name}`, 'error');
89
+ this.dispatchEvent(new CustomEvent('velin-error', { bubbles: true, detail: { message: 'accept', file: bad } }));
90
+ return;
91
+ }
92
+ }
93
+ this._files = files;
94
+ this._setStatus(files.length ? `${files.length} file(s) ready` : '', 'ok');
95
+ this.dispatchEvent(new CustomEvent('velin-files', { bubbles: true, detail: { files } }));
96
+ this._renderList();
97
+ }
98
+
99
+ _setStatus(text, tone = 'ok') {
100
+ const el = this.shadowRoot?.querySelector('.status');
101
+ if (!el) return;
102
+ el.textContent = text;
103
+ el.dataset.tone = tone;
104
+ }
105
+
106
+ _renderList() {
107
+ const ul = this.shadowRoot?.querySelector('.list');
108
+ if (!ul) return;
109
+ ul.innerHTML = this._files.map((f) => `<li><span>${escapeHTML(f.name)}</span><span>${Math.round(f.size / 1024)} KB</span></li>`).join('');
110
+ }
111
+
112
+ _render() {
113
+ const label = escapeHTML(this.getAttribute('label') || 'Upload files');
114
+ const accept = this.getAttribute('accept') || '';
115
+ const multiple = this.hasAttribute('multiple');
116
+ const progress = Math.max(0, Math.min(100, Number(this.getAttribute('progress') || 0)));
117
+
118
+ this.shadowRoot.innerHTML = `
119
+ <style>${styles}</style>
120
+ <div class="zone" role="group" aria-label="${label}">
121
+ <p class="hint">Drag and drop files here, or browse. Client-side only — wire <code>velin-files</code> to your upload API.</p>
122
+ <button type="button" class="browse">Browse files</button>
123
+ <input type="file" ${accept ? `accept="${escapeHTML(accept)}"` : ''} ${multiple ? 'multiple' : ''} />
124
+ <div class="progress" hidden="${progress <= 0 ? 'true' : 'false'}" aria-hidden="${progress <= 0 ? 'true' : 'false'}">
125
+ <div class="bar" style="inline-size:${progress}%"></div>
126
+ </div>
127
+ <p class="status" role="status" aria-live="polite"></p>
128
+ <ul class="list"></ul>
129
+ </div>
130
+ `;
131
+
132
+ const input = this.shadowRoot.querySelector('input');
133
+ const browse = this.shadowRoot.querySelector('.browse');
134
+ const zone = this.shadowRoot.querySelector('.zone');
135
+ browse.addEventListener('click', () => input.click());
136
+ input.addEventListener('change', () => {
137
+ if (input.files?.length) this._emitFiles(input.files);
138
+ });
139
+
140
+ ['dragenter', 'dragover'].forEach((ev) => {
141
+ zone.addEventListener(ev, (e) => {
142
+ e.preventDefault();
143
+ this.setAttribute('dragging', '');
144
+ });
145
+ });
146
+ ['dragleave', 'drop'].forEach((ev) => {
147
+ zone.addEventListener(ev, (e) => {
148
+ e.preventDefault();
149
+ this.removeAttribute('dragging');
150
+ });
151
+ });
152
+ zone.addEventListener('drop', (e) => {
153
+ const list = e.dataTransfer?.files;
154
+ if (list?.length) this._emitFiles(list);
155
+ });
156
+
157
+ this._renderList();
158
+ }
159
+ }
160
+
161
+ customElements.define('velin-file-dropzone', VelinFileDropzone);
162
+ export default VelinFileDropzone;
@@ -32,8 +32,28 @@ function resolveProviderUrl(provider, variant) {
32
32
  }
33
33
 
34
34
  const _svgCache = new Map();
35
+ const DEFAULT_SPRITE = 'velin-icons.svg';
36
+
37
+ /**
38
+ * Resolve default sprite URL: attribute → meta/html data → VelinIcon.defaultSprite → relative fallback.
39
+ * Avoids hardcoded absolute `/dist/...` which breaks offline/vendor installs.
40
+ */
41
+ function resolveDefaultSpriteUrl() {
42
+ if (typeof document !== 'undefined') {
43
+ const meta = document.querySelector('meta[name="velin-icon-sprite"]');
44
+ const fromMeta = meta?.getAttribute('content')?.trim();
45
+ if (fromMeta) return fromMeta;
46
+ const fromHtml = document.documentElement?.getAttribute('data-velin-icon-sprite')?.trim();
47
+ if (fromHtml) return fromHtml;
48
+ }
49
+ const configured = typeof VelinIcon.defaultSprite === 'string' ? VelinIcon.defaultSprite.trim() : '';
50
+ return configured || DEFAULT_SPRITE;
51
+ }
35
52
 
36
53
  class VelinIcon extends HTMLElement {
54
+ /** @type {string} Relative or absolute sprite URL used when `sprite` attribute is omitted. */
55
+ static defaultSprite = DEFAULT_SPRITE;
56
+
37
57
  static get observedAttributes() {
38
58
  return ['name', 'size', 'label', 'provider', 'variant', 'sprite'];
39
59
  }
@@ -99,7 +119,8 @@ class VelinIcon extends HTMLElement {
99
119
  if (spriteAttr === '' || (spriteAttr == null && isLocalSymbol)) {
100
120
  href = `#${name}`;
101
121
  } else {
102
- const spriteUrl = sanitizeURL(spriteAttr || '/dist/velin-icons.svg') || '/dist/velin-icons.svg';
122
+ const fallback = resolveDefaultSpriteUrl();
123
+ const spriteUrl = sanitizeURL(spriteAttr || fallback) || fallback || DEFAULT_SPRITE;
103
124
  href = `${spriteUrl}#${name}`;
104
125
  }
105
126
  use.setAttribute('href', href);
@@ -92,7 +92,6 @@ class VelinLightbox extends HTMLElement {
92
92
  this.setAttribute('open', '');
93
93
  setBackgroundInert(this);
94
94
  this._render();
95
- document.body.style.overflow = 'hidden';
96
95
  const overlay = this.shadowRoot.querySelector('.overlay');
97
96
  overlay.removeEventListener('keydown', this._onTrapKey);
98
97
  overlay.addEventListener('keydown', this._onTrapKey);
@@ -101,8 +100,7 @@ class VelinLightbox extends HTMLElement {
101
100
 
102
101
  close() {
103
102
  this.removeAttribute('open');
104
- clearBackgroundInert();
105
- document.body.style.overflow = '';
103
+ clearBackgroundInert(this);
106
104
  if (this._previousFocus) {
107
105
  this._previousFocus.focus();
108
106
  this._previousFocus = null;
@@ -110,6 +108,10 @@ class VelinLightbox extends HTMLElement {
110
108
  this.dispatchEvent(new CustomEvent('velin-close', { bubbles: true }));
111
109
  }
112
110
 
111
+ disconnectedCallback() {
112
+ clearBackgroundInert(this);
113
+ }
114
+
113
115
  _prev() { this._index = (this._index - 1 + this._items.length) % this._items.length; this._render(); }
114
116
  _next() { this._index = (this._index + 1) % this._items.length; this._render(); }
115
117
 
@@ -146,7 +146,6 @@ class VelinModal extends HTMLElement {
146
146
  this._previouslyFocused = saveFocus();
147
147
  setBackgroundInert(this);
148
148
  document.addEventListener('keydown', this._onKeydown);
149
- document.body.style.overflow = 'hidden';
150
149
 
151
150
  requestAnimationFrame(() => {
152
151
  const focusable = getFocusableElements(this.shadowRoot);
@@ -156,8 +155,7 @@ class VelinModal extends HTMLElement {
156
155
 
157
156
  _close() {
158
157
  document.removeEventListener('keydown', this._onKeydown);
159
- document.body.style.overflow = '';
160
- clearBackgroundInert();
158
+ clearBackgroundInert(this);
161
159
  restoreFocus(this._previouslyFocused);
162
160
  }
163
161
 
@@ -171,7 +169,7 @@ class VelinModal extends HTMLElement {
171
169
 
172
170
  disconnectedCallback() {
173
171
  document.removeEventListener('keydown', this._onKeydown);
174
- document.body.style.overflow = '';
172
+ clearBackgroundInert(this);
175
173
  }
176
174
  }
177
175
 
@@ -129,7 +129,7 @@ class VelinSearch extends HTMLElement {
129
129
  return;
130
130
  }
131
131
  if (!this._indexLoaded) await this._loadIndex();
132
- const { groups } = this._search.query(q, {
132
+ const { groups } = await this._search.query(q, {
133
133
  minChars: this.minChars,
134
134
  fuzzy: this.fuzzy,
135
135
  categories: this.categories || undefined,