@viglet/viglet-design-system 2026.2.57 → 2026.3.3

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 (274) hide show
  1. package/README.md +277 -7
  2. package/dist/assets.cjs +1 -0
  3. package/dist/assets.es.js +2 -0
  4. package/dist/badge-locale-BxI9TwkE.js +579 -0
  5. package/dist/badge-locale-sIsBdP_e.cjs +7 -0
  6. package/dist/bento/bento-actions-menu.d.ts +42 -0
  7. package/dist/bento/bento-actions-menu.d.ts.map +1 -0
  8. package/dist/bento/bento-back-to-top.d.ts +11 -0
  9. package/dist/bento/bento-back-to-top.d.ts.map +1 -0
  10. package/dist/bento/bento-command-palette.d.ts +28 -0
  11. package/dist/bento/bento-command-palette.d.ts.map +1 -0
  12. package/dist/bento/bento-count-tile.d.ts +18 -0
  13. package/dist/bento/bento-count-tile.d.ts.map +1 -0
  14. package/dist/bento/bento-empty-state.d.ts +35 -0
  15. package/dist/bento/bento-empty-state.d.ts.map +1 -0
  16. package/dist/bento/bento-entity-shell.d.ts +123 -0
  17. package/dist/bento/bento-entity-shell.d.ts.map +1 -0
  18. package/dist/bento/bento-entity-tile.d.ts +40 -0
  19. package/dist/bento/bento-entity-tile.d.ts.map +1 -0
  20. package/dist/bento/bento-form-hero.d.ts +91 -0
  21. package/dist/bento/bento-form-hero.d.ts.map +1 -0
  22. package/dist/bento/bento-form-section.d.ts +31 -0
  23. package/dist/bento/bento-form-section.d.ts.map +1 -0
  24. package/dist/bento/bento-hero-icon-picker.d.ts +35 -0
  25. package/dist/bento/bento-hero-icon-picker.d.ts.map +1 -0
  26. package/dist/bento/bento-hero.d.ts +62 -0
  27. package/dist/bento/bento-hero.d.ts.map +1 -0
  28. package/dist/bento/bento-inline-edit.d.ts +52 -0
  29. package/dist/bento/bento-inline-edit.d.ts.map +1 -0
  30. package/dist/bento/bento-layout.d.ts +52 -0
  31. package/dist/bento/bento-layout.d.ts.map +1 -0
  32. package/dist/bento/bento-list-page.d.ts +124 -0
  33. package/dist/bento/bento-list-page.d.ts.map +1 -0
  34. package/dist/bento/bento-nav-rail.d.ts +32 -0
  35. package/dist/bento/bento-nav-rail.d.ts.map +1 -0
  36. package/dist/bento/bento-nav.d.ts +69 -0
  37. package/dist/bento/bento-nav.d.ts.map +1 -0
  38. package/dist/bento/bento-save-bar.d.ts +36 -0
  39. package/dist/bento/bento-save-bar.d.ts.map +1 -0
  40. package/dist/bento/bento-scroll-fade.d.ts +20 -0
  41. package/dist/bento/bento-scroll-fade.d.ts.map +1 -0
  42. package/dist/bento/bento-scroll-save-bar.d.ts +22 -0
  43. package/dist/bento/bento-scroll-save-bar.d.ts.map +1 -0
  44. package/dist/bento/bento-section-chrome.d.ts +60 -0
  45. package/dist/bento/bento-section-chrome.d.ts.map +1 -0
  46. package/dist/bento/bento-section.d.ts +27 -0
  47. package/dist/bento/bento-section.d.ts.map +1 -0
  48. package/dist/bento/bento-shortcuts-dialog.d.ts +15 -0
  49. package/dist/bento/bento-shortcuts-dialog.d.ts.map +1 -0
  50. package/dist/bento/bento-status-marker.d.ts +26 -0
  51. package/dist/bento/bento-status-marker.d.ts.map +1 -0
  52. package/dist/bento/bento-tile.d.ts +42 -0
  53. package/dist/bento/bento-tile.d.ts.map +1 -0
  54. package/dist/bento/bento-tones.d.ts +33 -0
  55. package/dist/bento/bento-tones.d.ts.map +1 -0
  56. package/dist/bento/bento-user-menu.d.ts +31 -0
  57. package/dist/bento/bento-user-menu.d.ts.map +1 -0
  58. package/dist/bento/index.d.ts +59 -0
  59. package/dist/bento/index.d.ts.map +1 -0
  60. package/dist/bento.cjs +1 -0
  61. package/dist/bento.css +442 -0
  62. package/dist/bento.es.js +1501 -0
  63. package/dist/breadcrumb.context-CTnZ4rD0.cjs +1 -0
  64. package/dist/breadcrumb.context-h2A2IEJ_.js +928 -0
  65. package/dist/components/app-footer.d.ts +1 -1
  66. package/dist/components/app-footer.d.ts.map +1 -1
  67. package/dist/components/app-switcher.d.ts +1 -1
  68. package/dist/components/app-switcher.d.ts.map +1 -1
  69. package/dist/components/backend-status.d.ts +2 -2
  70. package/dist/components/backend-status.d.ts.map +1 -1
  71. package/dist/components/badge-locale.d.ts.map +1 -1
  72. package/dist/components/error-boundary.d.ts.map +1 -1
  73. package/dist/components/language-select.d.ts +1 -1
  74. package/dist/components/language-select.d.ts.map +1 -1
  75. package/dist/components/language-switcher.d.ts +1 -1
  76. package/dist/components/language-switcher.d.ts.map +1 -1
  77. package/dist/components/login/login.d.ts +11 -11
  78. package/dist/components/login/login.d.ts.map +1 -1
  79. package/dist/components/mode-toggle.d.ts +2 -2
  80. package/dist/components/mode-toggle.d.ts.map +1 -1
  81. package/dist/components/nav-secondary.d.ts +1 -1
  82. package/dist/components/nav-secondary.d.ts.map +1 -1
  83. package/dist/components/router/blank-slate.d.ts +10 -0
  84. package/dist/components/router/blank-slate.d.ts.map +1 -1
  85. package/dist/components/router/gradient-button-link.d.ts +1 -1
  86. package/dist/components/router/gradient-button-link.d.ts.map +1 -1
  87. package/dist/components/router/grid.list.d.ts +13 -0
  88. package/dist/components/router/grid.list.d.ts.map +1 -1
  89. package/dist/components/router/internal.sidebar.d.ts +27 -2
  90. package/dist/components/router/internal.sidebar.d.ts.map +1 -1
  91. package/dist/components/router/loading-provider.d.ts +7 -2
  92. package/dist/components/router/loading-provider.d.ts.map +1 -1
  93. package/dist/components/router/nav-main.d.ts +11 -1
  94. package/dist/components/router/nav-main.d.ts.map +1 -1
  95. package/dist/components/router/nav-user.d.ts +15 -1
  96. package/dist/components/router/nav-user.d.ts.map +1 -1
  97. package/dist/components/router/page-content.d.ts +10 -0
  98. package/dist/components/router/page-content.d.ts.map +1 -1
  99. package/dist/components/router/page-header.d.ts +13 -0
  100. package/dist/components/router/page-header.d.ts.map +1 -1
  101. package/dist/components/router/page.d.ts +13 -0
  102. package/dist/components/router/page.d.ts.map +1 -1
  103. package/dist/components/router/sticky.page.header.d.ts +14 -0
  104. package/dist/components/router/sticky.page.header.d.ts.map +1 -1
  105. package/dist/components/router/sub.page.d.ts +15 -11
  106. package/dist/components/router/sub.page.d.ts.map +1 -1
  107. package/dist/components/router/sub.page.header.d.ts +13 -0
  108. package/dist/components/router/sub.page.header.d.ts.map +1 -1
  109. package/dist/components/startup-first/startup-first.d.ts +11 -11
  110. package/dist/components/startup-first/startup-first.d.ts.map +1 -1
  111. package/dist/components/theme-provider.d.ts +1 -1
  112. package/dist/components/theme-provider.d.ts.map +1 -1
  113. package/dist/components/ui/avatar.d.ts +3 -3
  114. package/dist/components/ui/avatar.d.ts.map +1 -1
  115. package/dist/components/ui/badge.d.ts +1 -1
  116. package/dist/components/ui/badge.d.ts.map +1 -1
  117. package/dist/components/ui/breadcrumb.d.ts +7 -7
  118. package/dist/components/ui/breadcrumb.d.ts.map +1 -1
  119. package/dist/components/ui/button.d.ts +1 -1
  120. package/dist/components/ui/button.d.ts.map +1 -1
  121. package/dist/components/ui/card.d.ts +7 -7
  122. package/dist/components/ui/card.d.ts.map +1 -1
  123. package/dist/components/ui/checkbox.d.ts +1 -1
  124. package/dist/components/ui/checkbox.d.ts.map +1 -1
  125. package/dist/components/ui/dialog.d.ts +10 -10
  126. package/dist/components/ui/dialog.d.ts.map +1 -1
  127. package/dist/components/ui/drawer.d.ts +10 -10
  128. package/dist/components/ui/drawer.d.ts.map +1 -1
  129. package/dist/components/ui/dropdown-menu.d.ts +15 -15
  130. package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
  131. package/dist/components/ui/floating-formulas-bg.d.ts +8 -1
  132. package/dist/components/ui/floating-formulas-bg.d.ts.map +1 -1
  133. package/dist/components/ui/form-actions.d.ts +4 -4
  134. package/dist/components/ui/form-actions.d.ts.map +1 -1
  135. package/dist/components/ui/form.d.ts +8 -8
  136. package/dist/components/ui/form.d.ts.map +1 -1
  137. package/dist/components/ui/glass-card.d.ts +1 -1
  138. package/dist/components/ui/glass-card.d.ts.map +1 -1
  139. package/dist/components/ui/gradient-button.d.ts +1 -1
  140. package/dist/components/ui/gradient-button.d.ts.map +1 -1
  141. package/dist/components/ui/gradient-switch.d.ts +1 -1
  142. package/dist/components/ui/gradient-switch.d.ts.map +1 -1
  143. package/dist/components/ui/icon-picker-dialog.d.ts +35 -0
  144. package/dist/components/ui/icon-picker-dialog.d.ts.map +1 -0
  145. package/dist/components/ui/index.d.ts +1 -0
  146. package/dist/components/ui/index.d.ts.map +1 -1
  147. package/dist/components/ui/input.d.ts +1 -1
  148. package/dist/components/ui/input.d.ts.map +1 -1
  149. package/dist/components/ui/label.d.ts +1 -1
  150. package/dist/components/ui/label.d.ts.map +1 -1
  151. package/dist/components/ui/navigation-menu.d.ts +8 -8
  152. package/dist/components/ui/navigation-menu.d.ts.map +1 -1
  153. package/dist/components/ui/pagination.d.ts +5 -5
  154. package/dist/components/ui/pagination.d.ts.map +1 -1
  155. package/dist/components/ui/popover.d.ts +7 -7
  156. package/dist/components/ui/popover.d.ts.map +1 -1
  157. package/dist/components/ui/progress.d.ts +1 -1
  158. package/dist/components/ui/progress.d.ts.map +1 -1
  159. package/dist/components/ui/pulse-ring.d.ts +1 -1
  160. package/dist/components/ui/pulse-ring.d.ts.map +1 -1
  161. package/dist/components/ui/resizable.d.ts +3 -3
  162. package/dist/components/ui/resizable.d.ts.map +1 -1
  163. package/dist/components/ui/section-card.d.ts +7 -7
  164. package/dist/components/ui/section-card.d.ts.map +1 -1
  165. package/dist/components/ui/select.d.ts +10 -10
  166. package/dist/components/ui/select.d.ts.map +1 -1
  167. package/dist/components/ui/separator.d.ts +1 -1
  168. package/dist/components/ui/separator.d.ts.map +1 -1
  169. package/dist/components/ui/sheet.d.ts +8 -8
  170. package/dist/components/ui/sheet.d.ts.map +1 -1
  171. package/dist/components/ui/sidebar.d.ts +23 -23
  172. package/dist/components/ui/sidebar.d.ts.map +1 -1
  173. package/dist/components/ui/skeleton.d.ts +1 -1
  174. package/dist/components/ui/skeleton.d.ts.map +1 -1
  175. package/dist/components/ui/slider.d.ts +1 -1
  176. package/dist/components/ui/slider.d.ts.map +1 -1
  177. package/dist/components/ui/sonner.d.ts +1 -1
  178. package/dist/components/ui/sonner.d.ts.map +1 -1
  179. package/dist/components/ui/stepper.d.ts +3 -3
  180. package/dist/components/ui/stepper.d.ts.map +1 -1
  181. package/dist/components/ui/sticky-save-bar.d.ts +2 -2
  182. package/dist/components/ui/sticky-save-bar.d.ts.map +1 -1
  183. package/dist/components/ui/switch.d.ts +1 -1
  184. package/dist/components/ui/switch.d.ts.map +1 -1
  185. package/dist/components/ui/table.d.ts +8 -8
  186. package/dist/components/ui/table.d.ts.map +1 -1
  187. package/dist/components/ui/tabs.d.ts +4 -4
  188. package/dist/components/ui/tabs.d.ts.map +1 -1
  189. package/dist/components/ui/textarea.d.ts +1 -1
  190. package/dist/components/ui/textarea.d.ts.map +1 -1
  191. package/dist/components/ui/toggle-group.d.ts +2 -2
  192. package/dist/components/ui/toggle-group.d.ts.map +1 -1
  193. package/dist/components/ui/toggle.d.ts +1 -1
  194. package/dist/components/ui/toggle.d.ts.map +1 -1
  195. package/dist/components/ui/tooltip.d.ts +4 -4
  196. package/dist/components/ui/tooltip.d.ts.map +1 -1
  197. package/dist/components/ui/user-avatar.d.ts +1 -1
  198. package/dist/components/ui/user-avatar.d.ts.map +1 -1
  199. package/dist/components/ui/user-menu.d.ts +1 -1
  200. package/dist/components/ui/user-menu.d.ts.map +1 -1
  201. package/dist/components/viglet-app-switcher.d.ts +1 -1
  202. package/dist/components/viglet-app-switcher.d.ts.map +1 -1
  203. package/dist/contexts/breadcrumb.context.d.ts +1 -1
  204. package/dist/contexts/breadcrumb.context.d.ts.map +1 -1
  205. package/dist/contexts/user.context.d.ts +25 -1
  206. package/dist/contexts/user.context.d.ts.map +1 -1
  207. package/dist/exports.json +404 -0
  208. package/dist/floating-formulas-bg.cjs +1 -0
  209. package/dist/floating-formulas-bg.css +21 -0
  210. package/dist/floating-formulas-bg.es.js +83 -56
  211. package/dist/fonts/inter-cyrillic-ext-wght-normal.woff2 +0 -0
  212. package/dist/fonts/inter-cyrillic-wght-normal.woff2 +0 -0
  213. package/dist/fonts/inter-greek-ext-wght-normal.woff2 +0 -0
  214. package/dist/fonts/inter-greek-wght-normal.woff2 +0 -0
  215. package/dist/fonts/inter-latin-ext-wght-normal.woff2 +0 -0
  216. package/dist/fonts/inter-latin-wght-normal.woff2 +0 -0
  217. package/dist/fonts/inter-vietnamese-wght-normal.woff2 +0 -0
  218. package/dist/fonts/plus-jakarta-sans-cyrillic-ext-wght-normal.woff2 +0 -0
  219. package/dist/fonts/plus-jakarta-sans-latin-ext-wght-normal.woff2 +0 -0
  220. package/dist/fonts/plus-jakarta-sans-latin-wght-normal.woff2 +0 -0
  221. package/dist/fonts/plus-jakarta-sans-vietnamese-wght-normal.woff2 +0 -0
  222. package/dist/fonts.css +126 -0
  223. package/dist/hooks/use-grid-adapter.d.ts.map +1 -1
  224. package/dist/hooks/use-mobile.d.ts +10 -0
  225. package/dist/hooks/use-mobile.d.ts.map +1 -1
  226. package/dist/hooks/use-sub-page-breadcrumb.d.ts.map +1 -1
  227. package/dist/i18n/index.d.ts +136 -0
  228. package/dist/i18n/index.d.ts.map +1 -1
  229. package/dist/i18n/locales/en/bento.json.d.ts +51 -0
  230. package/dist/i18n/locales/en/common.json.d.ts +7 -1
  231. package/dist/i18n/locales/en/forms.json.d.ts +15 -1
  232. package/dist/i18n/locales/en/nav.json.d.ts +3 -1
  233. package/dist/i18n/locales/pt/bento.json.d.ts +51 -0
  234. package/dist/i18n/locales/pt/common.json.d.ts +7 -1
  235. package/dist/i18n/locales/pt/forms.json.d.ts +15 -1
  236. package/dist/i18n/locales/pt/nav.json.d.ts +3 -1
  237. package/dist/i18n-DoNdMLEd.cjs +1 -0
  238. package/dist/{i18n-C94rDRaM.js → i18n-DuNITbWa.js} +155 -27
  239. package/dist/{i18n.cjs.js → i18n.cjs} +1 -1
  240. package/dist/i18n.es.js +1 -1
  241. package/dist/index.cjs +1 -0
  242. package/dist/index.d.ts +0 -1
  243. package/dist/index.d.ts.map +1 -1
  244. package/dist/index.es.js +512 -770
  245. package/dist/lib/axios.d.ts.map +1 -1
  246. package/dist/lib/export-xlsx.d.ts +7 -1
  247. package/dist/lib/export-xlsx.d.ts.map +1 -1
  248. package/dist/lib/react-markers.d.ts +17 -0
  249. package/dist/lib/react-markers.d.ts.map +1 -0
  250. package/dist/lib/utils.d.ts +22 -0
  251. package/dist/lib/utils.d.ts.map +1 -1
  252. package/dist/loading-provider-1mgM91iY.cjs +1 -0
  253. package/dist/loading-provider-CMnOuvdR.js +224 -0
  254. package/dist/preset.css +103 -3
  255. package/dist/products-DL_YYXkV.cjs +1 -0
  256. package/dist/products-Du9KB6Xl.js +9 -0
  257. package/dist/{chunk-CMqjfN_6.cjs → rolldown-runtime-BocRIvOZ.cjs} +1 -1
  258. package/dist/router.cjs +1 -0
  259. package/dist/router.es.js +361 -558
  260. package/dist/user.context-kPAqKN4o.js +512 -0
  261. package/dist/user.context-r50nSWER.cjs +1 -0
  262. package/dist/viglet-design-system.css +1 -1
  263. package/dist/vite/boot-loader.d.ts +14 -4
  264. package/dist/vite/boot-loader.d.ts.map +1 -1
  265. package/dist/{vite.cjs.js → vite.cjs} +8 -8
  266. package/dist/vite.es.js +29 -16
  267. package/package.json +67 -23
  268. package/scripts/check-duplicates.mjs +198 -0
  269. package/dist/breadcrumb.context-BF0x4--Z.cjs +0 -7
  270. package/dist/breadcrumb.context-CCiYXxir.js +0 -1501
  271. package/dist/floating-formulas-bg.cjs.js +0 -1
  272. package/dist/i18n-CmYeG6uh.cjs +0 -1
  273. package/dist/index.cjs.js +0 -1
  274. package/dist/router.cjs.js +0 -1
@@ -20,15 +20,24 @@ export interface VigletBootLoaderOptions {
20
20
  title: string;
21
21
  /** Uppercase subtitle under the title, e.g. `"Enterprise Search Intelligence"`. */
22
22
  subtitle: string;
23
- /** Accent hex color (light mode), e.g. `"#4169E1"`. */
23
+ /**
24
+ * Accent hex color (light mode), e.g. `"#4169E1"` or `"#41E"`.
25
+ *
26
+ * Hex only, and enforced: the value is used raw in the gradients and parsed
27
+ * into an `r, g, b` triplet for the rgba tints, so anything the parser cannot
28
+ * read would render the loader in two different accents. A named colour or an
29
+ * `rgb()`/`hsl()` string throws rather than half-applying.
30
+ */
24
31
  color: string;
25
- /** Accent hex color in dark mode. Defaults to `color`. */
32
+ /** Accent hex color in dark mode, under the same rule as `color`. Defaults to `color`. */
26
33
  colorDark?: string;
27
34
  /**
28
35
  * CSS class prefix applied to every rule and keyframe to avoid collisions
29
36
  * (rings, mark, molecules, gas, progress, etc). Default: `"viglet"`.
30
37
  *
31
- * Must be a valid CSS identifier fragment — letters, digits and `-`.
38
+ * Must be a CSS identifier — a letter, then letters, digits, `-` or `_`. It is
39
+ * interpolated into every selector and keyframe name, so a value outside that
40
+ * throws instead of emitting rules of its own.
32
41
  */
33
42
  prefix?: string;
34
43
  /** `localStorage` key used to detect the stored theme. Default: `"vite-ui-theme"`. */
@@ -46,7 +55,8 @@ export interface VigletBootLoaderOptions {
46
55
  * it inside `<div id="root">` in your `index.html` (or `marketing.html`).
47
56
  *
48
57
  * Default: `"<!--viglet-boot-loader-->"`. If not found, the plugin falls
49
- * back to injecting right after the opening `<div id="root">` tag.
58
+ * back to injecting right after the opening `<div id="root">` tag, and warns
59
+ * on the console when the page offers neither.
50
60
  */
51
61
  placeholder?: string;
52
62
  }
@@ -1 +1 @@
1
- {"version":3,"file":"boot-loader.d.ts","sourceRoot":"","sources":["../../src/vite/boot-loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA4B,MAAM,EAAE,MAAM,MAAM,CAAC;AAE7D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,uBAAuB;IACtC,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,MAAM,CAsCzE"}
1
+ {"version":3,"file":"boot-loader.d.ts","sourceRoot":"","sources":["../../src/vite/boot-loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA4B,MAAM,EAAE,MAAM,MAAM,CAAC;AAE7D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,uBAAuB;IACtC,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,0FAA0F;IAC1F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,MAAM,CAsEzE"}
@@ -1,4 +1,4 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){let{title:a,subtitle:o,color:s,colorDark:c=s,prefix:l=`viglet`,storageKey:u=`vite-ui-theme`,testFlag:d=`__VIGLET_LOADING_TEST__`,placeholder:f=`<!--viglet-boot-loader-->`}=e,p=t(s)??`37, 99, 235`,m={pfx:l,title:a,subtitle:o,color:s,colorDark:c,rgb:p,rgbDark:t(c)??p,storageKey:u,testFlag:d},h=n(m),g=r(m),_=i(m);return{name:`viglet-boot-loader`,transformIndexHtml(e){return{html:e.includes(f)?e.replace(f,_):e.replace(/(<div\b[^>]*\bid=["']root["'][^>]*>)/,e=>`${e}\n${_}\n`),tags:[{tag:`style`,attrs:{"data-viglet-boot-loader":``},children:h,injectTo:`head`},{tag:`script`,children:g,injectTo:`head`}]}}}}function t(e){if(!e)return null;let t=/^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(e.trim());if(t){let[,e,n,r]=t;return`${Number.parseInt(e+e,16)}, ${Number.parseInt(n+n,16)}, ${Number.parseInt(r+r,16)}`}let n=/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(e.trim());if(n){let[,e,t,r]=n;return`${Number.parseInt(e,16)}, ${Number.parseInt(t,16)}, ${Number.parseInt(r,16)}`}return null}function n({pfx:e,color:t,colorDark:n,rgb:r}){return`
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){let{title:n,subtitle:o,color:s,colorDark:c=s,prefix:l=`viglet`,storageKey:u=`vite-ui-theme`,testFlag:d=`__VIGLET_LOADING_TEST__`,placeholder:f=`<!--viglet-boot-loader-->`}=e,p=t(s,`color`),m=t(c,`colorDark`);if(!/^[a-zA-Z][\w-]*$/.test(l))throw Error(`vigletBootLoader: prefix must start with a letter and hold only letters, digits, "-" or "_" — received ${JSON.stringify(l)}.`);let h={pfx:l,title:n,subtitle:o,color:s,colorDark:c,rgb:p,rgbDark:m,storageKey:u,testFlag:d},g=r(h),_=i(h),v=a(h);return{name:`viglet-boot-loader`,transformIndexHtml(e){let t;if(e.includes(f))t=e.replace(f,()=>v);else{let n=/(<div\b[^>]*\bid=["']root["'][^>]*>)/;n.test(e)?t=e.replace(n,e=>`${e}\n${v}\n`):(console.warn(`[viglet-boot-loader] no injection point found: the HTML holds neither the ${f} placeholder nor a <div id="root">, so no boot loader was injected.`),t=e)}return{html:t,tags:[{tag:`style`,attrs:{"data-viglet-boot-loader":``},children:g,injectTo:`head`},{tag:`script`,children:_,injectTo:`head`}]}}}}function t(e,t){let r=n(e);if(r)return r;throw Error(`vigletBootLoader: ${t} must be a hex colour like "#4169E1" or "#41E" — received ${JSON.stringify(e)}. Named CSS colours and rgb()/hsl() cannot be read for the loader's rgba tints.`)}function n(e){if(!e)return null;let t=/^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(e.trim());if(t){let[,e,n,r]=t;return`${Number.parseInt(e+e,16)}, ${Number.parseInt(n+n,16)}, ${Number.parseInt(r+r,16)}`}let n=/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(e.trim());if(n){let[,e,t,r]=n;return`${Number.parseInt(e,16)}, ${Number.parseInt(t,16)}, ${Number.parseInt(r,16)}`}return null}function r({pfx:e,color:t,colorDark:n,rgb:r}){return`
2
2
  html, body { margin: 0; padding: 0; height: 100%; }
3
3
  /* Boot-loader paints body only for the pre-React loading screen. It must
4
4
  NOT own "body { color }": that rule is unlayered, so it beats the app's
@@ -140,7 +140,7 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e)
140
140
  border-radius: 999px; box-shadow: 0 4px 12px rgba(220, 38, 38, 0.35);
141
141
  z-index: 10000;
142
142
  }
143
- `.trim()}function r({pfx:e,storageKey:t,testFlag:n}){return`
143
+ `.trim()}function i({pfx:e,storageKey:t,testFlag:n}){return`
144
144
  (function () {
145
145
  try {
146
146
  var stored = localStorage.getItem(${JSON.stringify(t)});
@@ -166,14 +166,14 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e)
166
166
  }
167
167
  } catch (e) { /* ignore */ }
168
168
  })();
169
- `.trim()}function i({pfx:e,title:t,subtitle:n}){return`
170
- <div id="${e}-boot-loader" role="status" aria-live="polite" aria-label="Loading ${a(t)}">
169
+ `.trim()}function a({pfx:e,title:t,subtitle:n}){return`
170
+ <div id="${e}-boot-loader" role="status" aria-live="polite" aria-label="Loading ${o(t)}">
171
171
  <div class="${e}-boot-stage">
172
172
  <div class="${e}-boot-ring"></div>
173
173
  <div class="${e}-boot-ring delay-1"></div>
174
174
  <div class="${e}-boot-ring delay-2"></div>
175
175
  <div class="${e}-boot-ring delay-3"></div>
176
- <div class="${e}-boot-logo-mark" aria-label="${a(t)}">
176
+ <div class="${e}-boot-logo-mark" aria-label="${o(t)}">
177
177
  <svg class="${e}-boot-molecules" viewBox="0 0 100 100" aria-hidden="true">
178
178
  <g class="${e}-boot-molecule m-1">
179
179
  <line x1="28" y1="50" x2="72" y2="50" />
@@ -223,9 +223,9 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e)
223
223
  </div>
224
224
  </div>
225
225
  <div class="${e}-boot-text">
226
- <div class="${e}-boot-title">${a(t)}</div>
227
- <div class="${e}-boot-subtitle">${a(n)}</div>
226
+ <div class="${e}-boot-title">${o(t)}</div>
227
+ <div class="${e}-boot-subtitle">${o(n)}</div>
228
228
  </div>
229
229
  <div class="${e}-boot-progress" aria-hidden="true"></div>
230
230
  </div>
231
- `.trim()}function a(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`).replace(/'/g,`&#39;`)}exports.vigletBootLoader=e;
231
+ `.trim()}function o(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`).replace(/'/g,`&#39;`)}exports.vigletBootLoader=e;
package/dist/vite.es.js CHANGED
@@ -1,36 +1,49 @@
1
1
  //#region src/vite/boot-loader.ts
2
2
  function e(e) {
3
- let { title: a, subtitle: o, color: s, colorDark: c = s, prefix: l = "viglet", storageKey: u = "vite-ui-theme", testFlag: d = "__VIGLET_LOADING_TEST__", placeholder: f = "<!--viglet-boot-loader-->" } = e, p = t(s) ?? "37, 99, 235", m = {
3
+ let { title: n, subtitle: o, color: s, colorDark: c = s, prefix: l = "viglet", storageKey: u = "vite-ui-theme", testFlag: d = "__VIGLET_LOADING_TEST__", placeholder: f = "<!--viglet-boot-loader-->" } = e, p = t(s, "color"), m = t(c, "colorDark");
4
+ if (!/^[a-zA-Z][\w-]*$/.test(l)) throw Error(`vigletBootLoader: prefix must start with a letter and hold only letters, digits, "-" or "_" — received ${JSON.stringify(l)}.`);
5
+ let h = {
4
6
  pfx: l,
5
- title: a,
7
+ title: n,
6
8
  subtitle: o,
7
9
  color: s,
8
10
  colorDark: c,
9
11
  rgb: p,
10
- rgbDark: t(c) ?? p,
12
+ rgbDark: m,
11
13
  storageKey: u,
12
14
  testFlag: d
13
- }, h = n(m), g = r(m), _ = i(m);
15
+ }, g = r(h), _ = i(h), v = a(h);
14
16
  return {
15
17
  name: "viglet-boot-loader",
16
18
  transformIndexHtml(e) {
19
+ let t;
20
+ if (e.includes(f)) t = e.replace(f, () => v);
21
+ else {
22
+ let n = /(<div\b[^>]*\bid=["']root["'][^>]*>)/;
23
+ n.test(e) ? t = e.replace(n, (e) => `${e}\n${v}\n`) : (console.warn(`[viglet-boot-loader] no injection point found: the HTML holds neither the ${f} placeholder nor a <div id="root">, so no boot loader was injected.`), t = e);
24
+ }
17
25
  return {
18
- html: e.includes(f) ? e.replace(f, _) : e.replace(/(<div\b[^>]*\bid=["']root["'][^>]*>)/, (e) => `${e}\n${_}\n`),
26
+ html: t,
19
27
  tags: [{
20
28
  tag: "style",
21
29
  attrs: { "data-viglet-boot-loader": "" },
22
- children: h,
30
+ children: g,
23
31
  injectTo: "head"
24
32
  }, {
25
33
  tag: "script",
26
- children: g,
34
+ children: _,
27
35
  injectTo: "head"
28
36
  }]
29
37
  };
30
38
  }
31
39
  };
32
40
  }
33
- function t(e) {
41
+ function t(e, t) {
42
+ let r = n(e);
43
+ if (r) return r;
44
+ throw Error(`vigletBootLoader: ${t} must be a hex colour like "#4169E1" or "#41E" — received ${JSON.stringify(e)}. Named CSS colours and rgb()/hsl() cannot be read for the loader's rgba tints.`);
45
+ }
46
+ function n(e) {
34
47
  if (!e) return null;
35
48
  let t = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(e.trim());
36
49
  if (t) {
@@ -44,7 +57,7 @@ function t(e) {
44
57
  }
45
58
  return null;
46
59
  }
47
- function n({ pfx: e, color: t, colorDark: n, rgb: r }) {
60
+ function r({ pfx: e, color: t, colorDark: n, rgb: r }) {
48
61
  return `
49
62
  html, body { margin: 0; padding: 0; height: 100%; }
50
63
  /* Boot-loader paints body only for the pre-React loading screen. It must
@@ -189,7 +202,7 @@ function n({ pfx: e, color: t, colorDark: n, rgb: r }) {
189
202
  }
190
203
  `.trim();
191
204
  }
192
- function r({ pfx: e, storageKey: t, testFlag: n }) {
205
+ function i({ pfx: e, storageKey: t, testFlag: n }) {
193
206
  return `
194
207
  (function () {
195
208
  try {
@@ -218,15 +231,15 @@ function r({ pfx: e, storageKey: t, testFlag: n }) {
218
231
  })();
219
232
  `.trim();
220
233
  }
221
- function i({ pfx: e, title: t, subtitle: n }) {
234
+ function a({ pfx: e, title: t, subtitle: n }) {
222
235
  return `
223
- <div id="${e}-boot-loader" role="status" aria-live="polite" aria-label="Loading ${a(t)}">
236
+ <div id="${e}-boot-loader" role="status" aria-live="polite" aria-label="Loading ${o(t)}">
224
237
  <div class="${e}-boot-stage">
225
238
  <div class="${e}-boot-ring"></div>
226
239
  <div class="${e}-boot-ring delay-1"></div>
227
240
  <div class="${e}-boot-ring delay-2"></div>
228
241
  <div class="${e}-boot-ring delay-3"></div>
229
- <div class="${e}-boot-logo-mark" aria-label="${a(t)}">
242
+ <div class="${e}-boot-logo-mark" aria-label="${o(t)}">
230
243
  <svg class="${e}-boot-molecules" viewBox="0 0 100 100" aria-hidden="true">
231
244
  <g class="${e}-boot-molecule m-1">
232
245
  <line x1="28" y1="50" x2="72" y2="50" />
@@ -276,14 +289,14 @@ function i({ pfx: e, title: t, subtitle: n }) {
276
289
  </div>
277
290
  </div>
278
291
  <div class="${e}-boot-text">
279
- <div class="${e}-boot-title">${a(t)}</div>
280
- <div class="${e}-boot-subtitle">${a(n)}</div>
292
+ <div class="${e}-boot-title">${o(t)}</div>
293
+ <div class="${e}-boot-subtitle">${o(n)}</div>
281
294
  </div>
282
295
  <div class="${e}-boot-progress" aria-hidden="true"></div>
283
296
  </div>
284
297
  `.trim();
285
298
  }
286
- function a(e) {
299
+ function o(e) {
287
300
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
288
301
  }
289
302
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viglet/viglet-design-system",
3
- "version": "2026.2.57",
3
+ "version": "2026.3.3",
4
4
  "type": "module",
5
5
  "description": "Viglet Design System - Shared components, hooks, utilities and design tokens for Viglet products",
6
6
  "license": "Apache-2.0",
@@ -12,54 +12,64 @@
12
12
  "access": "public",
13
13
  "registry": "https://registry.npmjs.org"
14
14
  },
15
- "main": "./dist/index.cjs.js",
15
+ "main": "./dist/index.cjs",
16
16
  "module": "./dist/index.es.js",
17
17
  "types": "./dist/index.d.ts",
18
18
  "exports": {
19
19
  ".": {
20
20
  "types": "./dist/index.d.ts",
21
21
  "import": "./dist/index.es.js",
22
- "require": "./dist/index.cjs.js"
22
+ "require": "./dist/index.cjs"
23
23
  },
24
+ "./bento": {
25
+ "types": "./dist/bento/index.d.ts",
26
+ "import": "./dist/bento.es.js",
27
+ "require": "./dist/bento.cjs"
28
+ },
29
+ "./bento.css": "./dist/bento.css",
30
+ "./assets": {
31
+ "types": "./dist/assets/products/index.d.ts",
32
+ "import": "./dist/assets.es.js",
33
+ "require": "./dist/assets.cjs"
34
+ },
35
+ "./exports.json": "./dist/exports.json",
24
36
  "./styles": "./dist/viglet-design-system.css",
37
+ "./fonts": "./dist/fonts.css",
38
+ "./fonts.css": "./dist/fonts.css",
25
39
  "./preset": "./dist/preset.css",
26
40
  "./preset.css": "./dist/preset.css",
27
41
  "./i18n": {
28
42
  "types": "./dist/i18n/index.d.ts",
29
43
  "import": "./dist/i18n.es.js",
30
- "require": "./dist/i18n.cjs.js"
44
+ "require": "./dist/i18n.cjs"
31
45
  },
32
46
  "./router": {
33
47
  "types": "./dist/router.d.ts",
34
48
  "import": "./dist/router.es.js",
35
- "require": "./dist/router.cjs.js"
49
+ "require": "./dist/router.cjs"
36
50
  },
37
51
  "./floating-formulas-bg": {
38
52
  "types": "./dist/components/ui/floating-formulas-bg.d.ts",
39
53
  "import": "./dist/floating-formulas-bg.es.js",
40
- "require": "./dist/floating-formulas-bg.cjs.js"
54
+ "require": "./dist/floating-formulas-bg.cjs"
41
55
  },
42
56
  "./floating-formulas-bg.css": "./dist/floating-formulas-bg.css",
43
57
  "./vite": {
44
58
  "types": "./dist/vite/index.d.ts",
45
59
  "import": "./dist/vite.es.js",
46
- "require": "./dist/vite.cjs.js"
60
+ "require": "./dist/vite.cjs"
47
61
  }
48
62
  },
63
+ "bin": {
64
+ "viglet-ds-check-duplicates": "./scripts/check-duplicates.mjs"
65
+ },
49
66
  "files": [
50
- "dist"
67
+ "dist",
68
+ "scripts/check-duplicates.mjs"
51
69
  ],
52
70
  "sideEffects": [
53
71
  "**/*.css"
54
72
  ],
55
- "scripts": {
56
- "dev": "vite build --watch",
57
- "build": "tsc -b && node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && vite build && node -e \"require('fs').cpSync('src/styles/preset.css','dist/preset.css')\"",
58
- "lint": "eslint .",
59
- "storybook": "storybook dev -p 6006",
60
- "build-storybook": "storybook build",
61
- "prepublishOnly": "npm run build"
62
- },
63
73
  "peerDependencies": {
64
74
  "i18next": ">=25.0.0",
65
75
  "i18next-browser-languagedetector": ">=8.0.0",
@@ -76,6 +86,9 @@
76
86
  }
77
87
  },
78
88
  "dependencies": {
89
+ "@dnd-kit/core": "^6.3.1",
90
+ "@dnd-kit/sortable": "^10.0.0",
91
+ "@dnd-kit/utilities": "^3.2.2",
79
92
  "@fontsource-variable/inter": "^5.2.8",
80
93
  "@fontsource-variable/plus-jakarta-sans": "^5.2.8",
81
94
  "@iconify/react": "^6.0.2",
@@ -98,7 +111,7 @@
98
111
  "@radix-ui/react-toggle-group": "^1.1.11",
99
112
  "@radix-ui/react-tooltip": "^1.2.8",
100
113
  "@tabler/icons-react": "^3.40.0",
101
- "@tanstack/react-table": "^8.21.3",
114
+ "@tanstack/react-table": "^9.1.2",
102
115
  "axios": "^1.13.6",
103
116
  "class-variance-authority": "^0.7.1",
104
117
  "clsx": "^2.1.1",
@@ -116,28 +129,59 @@
116
129
  "@storybook/addon-a11y": "^10.3.5",
117
130
  "@storybook/addon-docs": "^10.3.5",
118
131
  "@storybook/addon-themes": "^10.3.5",
132
+ "@storybook/addon-vitest": "10.5.10",
119
133
  "@storybook/react-vite": "^10.3.5",
120
134
  "@tailwindcss/typography": "^0.5.19",
121
135
  "@tailwindcss/vite": "^4.2.2",
122
- "@types/node": "^25.5.0",
136
+ "@testing-library/jest-dom": "^7.0.1",
137
+ "@testing-library/react": "^16.3.2",
138
+ "@testing-library/user-event": "^14.6.6",
139
+ "@types/node": "^26.0.1",
123
140
  "@types/react": "^19.2.14",
124
141
  "@types/react-dom": "^19.2.3",
142
+ "@types/semver": "^7.8.0",
125
143
  "@vitejs/plugin-react": "^6.0.1",
144
+ "@vitest/browser": "^4.1.11",
145
+ "@vitest/browser-playwright": "^4.1.11",
126
146
  "eslint": "^9.39.0",
127
147
  "eslint-plugin-react-hooks": "^7.0.1",
128
148
  "eslint-plugin-react-refresh": "^0.5.2",
129
149
  "globals": "^17.4.0",
150
+ "i18next": "^26.4.0",
151
+ "i18next-browser-languagedetector": "^8.2.1",
152
+ "jsdom": "^30.0.1",
153
+ "next-themes": "^0.4.6",
154
+ "playwright": "^1.62.1",
155
+ "react-i18next": "^17.0.12",
130
156
  "react-router-dom": "^7.0.0",
157
+ "semver": "^7.8.5",
158
+ "sonner": "^2.0.8",
131
159
  "storybook": "^10.3.5",
132
160
  "tailwindcss": "^4.1.11",
133
161
  "tw-animate-css": "^1.4.0",
134
- "typescript": "~6.0.2",
162
+ "typescript": "~6.0.3",
135
163
  "typescript-eslint": "^8.58.0",
136
164
  "vite": "^8.0.8",
137
- "vite-plugin-dts": "^5.0.0"
165
+ "vite-plugin-dts": "^5.0.0",
166
+ "vitest": "^4.1.11"
138
167
  },
139
168
  "engines": {
140
- "node": "^24.0.0",
141
- "npm": "^11.0.0"
169
+ "node": ">=26",
170
+ "pnpm": ">=11"
171
+ },
172
+ "scripts": {
173
+ "dev": "vite build --watch",
174
+ "build": "tsc -b && node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && vite build && node -e \"require('fs').cpSync('src/styles/preset.css','dist/preset.css')\" && node scripts/build-fonts.mjs && node scripts/emit-exports.mjs && node scripts/check-dist.mjs && node scripts/check-exports.mjs && node scripts/check-size.mjs",
175
+ "check:dist": "node scripts/check-dist.mjs",
176
+ "check:size": "node scripts/check-size.mjs",
177
+ "typecheck": "tsc -b --force",
178
+ "lint": "eslint . --max-warnings 0",
179
+ "test": "vitest run",
180
+ "test:watch": "vitest",
181
+ "use:local": "node scripts/use-local.mjs",
182
+ "storybook": "storybook dev -p 6006",
183
+ "build-storybook": "storybook build",
184
+ "build:fonts": "node scripts/build-fonts.mjs",
185
+ "check:exports": "node scripts/check-exports.mjs"
142
186
  }
143
- }
187
+ }
@@ -0,0 +1,198 @@
1
+ #!/usr/bin/env node
2
+ // VDS5 — fail a consumer's build when it declares a component this package
3
+ // already exports, and name the import that replaces it.
4
+ //
5
+ // Run it from a product checkout, in that product's CI:
6
+ //
7
+ // viglet-ds-check-duplicates # scan ./src
8
+ // viglet-ds-check-duplicates src app # scan these directories
9
+ // viglet-ds-check-duplicates --json # machine-readable findings
10
+ // viglet-ds-check-duplicates --warn # report, but exit 0
11
+ // viglet-ds-check-duplicates --manifest p # read the export list from p
12
+ //
13
+ // Reporting that a duplicate exists is not the useful half. A local copy is
14
+ // written because the author did not know the shared one existed, so the finding
15
+ // has to end in the line they would have written instead — which is why the
16
+ // manifest records the specifier per entry point rather than just a list of
17
+ // names.
18
+ //
19
+ // A one-line re-export shim is the sanctioned pattern and is not a duplicate:
20
+ // files that re-export from this package are skipped.
21
+ //
22
+ // Some collisions are deliberate — Shio's AppFooter renders Shio's own build
23
+ // version and merely shares a name. Keep those by writing, anywhere in the file:
24
+ //
25
+ // // viglet-ds-allow-duplicate AppFooter -- renders Shio's build version
26
+ //
27
+ // The reason is the point. A gate with no way to say "this one is mine" is a
28
+ // gate somebody deletes.
29
+
30
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs"
31
+ import { createRequire } from "node:module"
32
+ import { dirname, join, relative, resolve } from "node:path"
33
+ import { fileURLToPath } from "node:url"
34
+
35
+ const PACKAGE_NAME = "@viglet/viglet-design-system"
36
+ const EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".mts", ".mjs"])
37
+ const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "build", "out", "coverage", ".next"])
38
+
39
+ const args = process.argv.slice(2)
40
+ const asJson = args.includes("--json")
41
+ const warnOnly = args.includes("--warn")
42
+ const manifestFlag = args.indexOf("--manifest")
43
+ const manifestPath = manifestFlag === -1 ? null : args[manifestFlag + 1]
44
+ const roots = args.filter(
45
+ (a, i) => !a.startsWith("--") && i !== manifestFlag + 1,
46
+ )
47
+
48
+ function loadManifest() {
49
+ // An explicit path wins: it is how this repository's own tests pin a fixture,
50
+ // and how a monorepo checks against a version other than the hoisted one.
51
+ if (manifestPath) {
52
+ if (!existsSync(manifestPath)) {
53
+ console.error(`check-duplicates: no manifest at ${manifestPath}`)
54
+ process.exit(1)
55
+ }
56
+ return JSON.parse(readFileSync(manifestPath, "utf8"))
57
+ }
58
+ // Installed in the consumer.
59
+ const require = createRequire(join(process.cwd(), "package.json"))
60
+ try {
61
+ return require(`${PACKAGE_NAME}/exports.json`)
62
+ } catch {
63
+ /* fall through */
64
+ }
65
+ // Running from this repository against its own dist.
66
+ const local = resolve(dirname(fileURLToPath(import.meta.url)), "..", "dist", "exports.json")
67
+ if (existsSync(local)) return JSON.parse(readFileSync(local, "utf8"))
68
+ return null
69
+ }
70
+
71
+ const manifest = loadManifest()
72
+ if (!manifest) {
73
+ console.error(
74
+ `check-duplicates: could not read ${PACKAGE_NAME}/exports.json.\n` +
75
+ `Install ${PACKAGE_NAME} (>= the version that ships the manifest) and re-run.`,
76
+ )
77
+ process.exit(1)
78
+ }
79
+
80
+ // name -> the specifier that exports it. The root entry wins when a name appears
81
+ // in more than one, since that is the import a product would write.
82
+ const exported = new Map()
83
+ for (const [subpath, entry] of Object.entries(manifest.entries)) {
84
+ for (const name of [...entry.values, ...entry.types]) {
85
+ const isType = entry.types.includes(name)
86
+ if (!exported.has(name) || subpath === ".") {
87
+ exported.set(name, { specifier: entry.specifier, isType })
88
+ }
89
+ }
90
+ }
91
+
92
+ function collect(dir, files = []) {
93
+ let dirents
94
+ try {
95
+ dirents = readdirSync(dir, { withFileTypes: true })
96
+ } catch {
97
+ return files
98
+ }
99
+ for (const entry of dirents) {
100
+ const full = join(dir, entry.name)
101
+ if (entry.isDirectory()) {
102
+ if (!SKIP_DIRS.has(entry.name) && !entry.name.startsWith(".")) collect(full, files)
103
+ } else if (EXTENSIONS.has(entry.name.slice(entry.name.lastIndexOf(".")))) {
104
+ files.push(full)
105
+ }
106
+ }
107
+ return files
108
+ }
109
+
110
+ // `export const X`, `export function X`, `export class X`, `export type X`,
111
+ // `export interface X`, and the names inside a local `export { A, B as C }`.
112
+ const DECLARED =
113
+ /^\s*export\s+(?:declare\s+)?(?:default\s+)?(?:abstract\s+)?(?:const|let|var|function\*?|class|type|interface|enum)\s+([A-Za-z_$][\w$]*)/gm
114
+ const EXPORT_LIST = /^\s*export\s+(?:type\s+)?\{([^}]*)\}\s*(?!from)[;\n]/gm
115
+
116
+ function declaredNames(source) {
117
+ const names = new Map()
118
+ for (const match of source.matchAll(DECLARED)) {
119
+ names.set(match[1], lineOf(source, match.index))
120
+ }
121
+ for (const match of source.matchAll(EXPORT_LIST)) {
122
+ for (const clause of match[1].split(",")) {
123
+ // `A as B` exports B; a bare `A` exports A.
124
+ const name = clause.includes(" as ")
125
+ ? clause.split(" as ").pop().trim()
126
+ : clause.trim()
127
+ if (/^[A-Za-z_$][\w$]*$/.test(name) && name !== "default") {
128
+ names.set(name, lineOf(source, match.index))
129
+ }
130
+ }
131
+ }
132
+ return names
133
+ }
134
+
135
+ function lineOf(source, index) {
136
+ return source.slice(0, index).split("\n").length
137
+ }
138
+
139
+ const scanRoots = (roots.length ? roots : ["src"]).map((r) => resolve(r))
140
+ const missing = scanRoots.filter((r) => !existsSync(r) || !statSync(r).isDirectory())
141
+ if (missing.length === scanRoots.length) {
142
+ console.error(
143
+ `check-duplicates: nothing to scan (${scanRoots.map((r) => relative(process.cwd(), r)).join(", ")}).\n` +
144
+ "Pass the source directories explicitly: viglet-ds-check-duplicates <dir> [dir]",
145
+ )
146
+ process.exit(1)
147
+ }
148
+
149
+ const findings = []
150
+ let scanned = 0
151
+ for (const root of scanRoots) {
152
+ for (const file of collect(root)) {
153
+ const source = readFileSync(file, "utf8")
154
+ scanned++
155
+ // A re-export shim is the sanctioned pattern, not a duplicate.
156
+ if (source.includes(`from "${PACKAGE_NAME}`) || source.includes(`from '${PACKAGE_NAME}`)) {
157
+ if (/^\s*export\s+(?:\*|\{)/m.test(source)) continue
158
+ }
159
+ const allowed = new Set(
160
+ [...source.matchAll(/viglet-ds-allow-duplicate\s+([A-Za-z_$][\w$]*)/g)].map((m) => m[1]),
161
+ )
162
+ for (const [name, line] of declaredNames(source)) {
163
+ const match = exported.get(name)
164
+ if (!match || allowed.has(name)) continue
165
+ findings.push({
166
+ file: relative(process.cwd(), file).replace(/\\/g, "/"),
167
+ line,
168
+ name,
169
+ replacement: match.isType
170
+ ? `import type { ${name} } from "${match.specifier}"`
171
+ : `import { ${name} } from "${match.specifier}"`,
172
+ })
173
+ }
174
+ }
175
+ }
176
+
177
+ if (asJson) {
178
+ console.log(JSON.stringify({ scanned, findings }, null, 2))
179
+ } else if (findings.length === 0) {
180
+ console.log(
181
+ `check-duplicates: ${scanned} file(s) scanned, no local copy of a ${PACKAGE_NAME} export.`,
182
+ )
183
+ } else {
184
+ console.error(
185
+ `\ncheck-duplicates: ${findings.length} declaration(s) already exported by ${PACKAGE_NAME}@${manifest.version}\n`,
186
+ )
187
+ for (const finding of findings) {
188
+ console.error(` ${finding.file}:${finding.line} declares ${finding.name}`)
189
+ console.error(` replace it with: ${finding.replacement}\n`)
190
+ }
191
+ console.error(
192
+ "A local copy that drifts is the failure the design system exists to prevent.\n" +
193
+ "If the product genuinely needs a variation, wrap the shared component rather\n" +
194
+ "than re-declaring its name.\n",
195
+ )
196
+ }
197
+
198
+ process.exit(findings.length > 0 && !warnOnly ? 1 : 0)