@viglet/viglet-design-system 2026.3.2 → 2026.3.4

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 (184) hide show
  1. package/README.md +334 -8
  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 +1502 -0
  63. package/dist/breadcrumb.context-CGqjI0-U.js +914 -0
  64. package/dist/breadcrumb.context-DYeZEC65.cjs +1 -0
  65. package/dist/components/app-switcher.d.ts +2 -0
  66. package/dist/components/app-switcher.d.ts.map +1 -1
  67. package/dist/components/badge-locale.d.ts.map +1 -1
  68. package/dist/components/error-boundary.d.ts.map +1 -1
  69. package/dist/components/language-select.d.ts.map +1 -1
  70. package/dist/components/router/blank-slate.d.ts +10 -0
  71. package/dist/components/router/blank-slate.d.ts.map +1 -1
  72. package/dist/components/router/grid.list.d.ts +13 -0
  73. package/dist/components/router/grid.list.d.ts.map +1 -1
  74. package/dist/components/router/internal.sidebar.d.ts +39 -3
  75. package/dist/components/router/internal.sidebar.d.ts.map +1 -1
  76. package/dist/components/router/loading-provider.d.ts +6 -1
  77. package/dist/components/router/loading-provider.d.ts.map +1 -1
  78. package/dist/components/router/nav-main.d.ts +10 -0
  79. package/dist/components/router/nav-main.d.ts.map +1 -1
  80. package/dist/components/router/nav-user.d.ts +13 -0
  81. package/dist/components/router/nav-user.d.ts.map +1 -1
  82. package/dist/components/router/page-content.d.ts +10 -0
  83. package/dist/components/router/page-content.d.ts.map +1 -1
  84. package/dist/components/router/page-header.d.ts +13 -0
  85. package/dist/components/router/page-header.d.ts.map +1 -1
  86. package/dist/components/router/page.d.ts +13 -0
  87. package/dist/components/router/page.d.ts.map +1 -1
  88. package/dist/components/router/sticky.page.header.d.ts +14 -0
  89. package/dist/components/router/sticky.page.header.d.ts.map +1 -1
  90. package/dist/components/router/sub.page.d.ts +31 -11
  91. package/dist/components/router/sub.page.d.ts.map +1 -1
  92. package/dist/components/router/sub.page.header.d.ts +13 -0
  93. package/dist/components/router/sub.page.header.d.ts.map +1 -1
  94. package/dist/components/startup-first/startup-first.d.ts.map +1 -1
  95. package/dist/components/theme-provider.d.ts +37 -2
  96. package/dist/components/theme-provider.d.ts.map +1 -1
  97. package/dist/components/ui/floating-formulas-bg.d.ts +8 -1
  98. package/dist/components/ui/floating-formulas-bg.d.ts.map +1 -1
  99. package/dist/components/ui/form.d.ts +2 -2
  100. package/dist/components/ui/form.d.ts.map +1 -1
  101. package/dist/components/ui/gradient-button.d.ts.map +1 -1
  102. package/dist/components/ui/gradient-switch.d.ts.map +1 -1
  103. package/dist/components/ui/icon-picker-dialog.d.ts +35 -0
  104. package/dist/components/ui/icon-picker-dialog.d.ts.map +1 -0
  105. package/dist/components/ui/index.d.ts +1 -0
  106. package/dist/components/ui/index.d.ts.map +1 -1
  107. package/dist/components/ui/section-card.d.ts +3 -3
  108. package/dist/components/ui/section-card.d.ts.map +1 -1
  109. package/dist/components/ui/sidebar.d.ts.map +1 -1
  110. package/dist/components/ui/slider.d.ts +1 -1
  111. package/dist/components/ui/slider.d.ts.map +1 -1
  112. package/dist/components/viglet-app-switcher.d.ts +3 -0
  113. package/dist/components/viglet-app-switcher.d.ts.map +1 -1
  114. package/dist/contexts/user.context.d.ts +24 -0
  115. package/dist/contexts/user.context.d.ts.map +1 -1
  116. package/dist/exports.json +404 -0
  117. package/dist/floating-formulas-bg.cjs +1 -0
  118. package/dist/floating-formulas-bg.css +21 -0
  119. package/dist/floating-formulas-bg.es.js +84 -56
  120. package/dist/fonts/inter-cyrillic-ext-wght-normal.woff2 +0 -0
  121. package/dist/fonts/inter-cyrillic-wght-normal.woff2 +0 -0
  122. package/dist/fonts/inter-greek-ext-wght-normal.woff2 +0 -0
  123. package/dist/fonts/inter-greek-wght-normal.woff2 +0 -0
  124. package/dist/fonts/inter-latin-ext-wght-normal.woff2 +0 -0
  125. package/dist/fonts/inter-latin-wght-normal.woff2 +0 -0
  126. package/dist/fonts/inter-vietnamese-wght-normal.woff2 +0 -0
  127. package/dist/fonts/plus-jakarta-sans-cyrillic-ext-wght-normal.woff2 +0 -0
  128. package/dist/fonts/plus-jakarta-sans-latin-ext-wght-normal.woff2 +0 -0
  129. package/dist/fonts/plus-jakarta-sans-latin-wght-normal.woff2 +0 -0
  130. package/dist/fonts/plus-jakarta-sans-vietnamese-wght-normal.woff2 +0 -0
  131. package/dist/fonts.css +126 -0
  132. package/dist/hooks/use-grid-adapter.d.ts.map +1 -1
  133. package/dist/hooks/use-mobile.d.ts +10 -0
  134. package/dist/hooks/use-mobile.d.ts.map +1 -1
  135. package/dist/hooks/use-sub-page-breadcrumb.d.ts.map +1 -1
  136. package/dist/i18n/index.d.ts +136 -0
  137. package/dist/i18n/index.d.ts.map +1 -1
  138. package/dist/i18n/locales/en/bento.json.d.ts +51 -0
  139. package/dist/i18n/locales/en/common.json.d.ts +7 -1
  140. package/dist/i18n/locales/en/forms.json.d.ts +15 -1
  141. package/dist/i18n/locales/en/nav.json.d.ts +3 -1
  142. package/dist/i18n/locales/pt/bento.json.d.ts +51 -0
  143. package/dist/i18n/locales/pt/common.json.d.ts +7 -1
  144. package/dist/i18n/locales/pt/forms.json.d.ts +15 -1
  145. package/dist/i18n/locales/pt/nav.json.d.ts +3 -1
  146. package/dist/i18n-DoNdMLEd.cjs +1 -0
  147. package/dist/{i18n-C94rDRaM.js → i18n-DuNITbWa.js} +155 -27
  148. package/dist/i18n.cjs +1 -0
  149. package/dist/i18n.es.js +2 -1
  150. package/dist/index.cjs +1 -0
  151. package/dist/index.d.ts +0 -1
  152. package/dist/index.d.ts.map +1 -1
  153. package/dist/index.es.js +493 -750
  154. package/dist/lib/axios.d.ts.map +1 -1
  155. package/dist/lib/export-xlsx.d.ts +7 -1
  156. package/dist/lib/export-xlsx.d.ts.map +1 -1
  157. package/dist/lib/react-markers.d.ts +17 -0
  158. package/dist/lib/react-markers.d.ts.map +1 -0
  159. package/dist/lib/utils.d.ts +22 -0
  160. package/dist/lib/utils.d.ts.map +1 -1
  161. package/dist/loading-provider-1mgM91iY.cjs +1 -0
  162. package/dist/loading-provider-CMnOuvdR.js +224 -0
  163. package/dist/preset.css +103 -3
  164. package/dist/products-DL_YYXkV.cjs +1 -0
  165. package/dist/products-Du9KB6Xl.js +9 -0
  166. package/dist/{rolldown-runtime-CMqjfN_6.cjs → rolldown-runtime-BocRIvOZ.cjs} +1 -1
  167. package/dist/router.cjs +1 -0
  168. package/dist/router.es.js +334 -504
  169. package/dist/user.context-kPAqKN4o.js +512 -0
  170. package/dist/user.context-r50nSWER.cjs +1 -0
  171. package/dist/viglet-design-system.css +1 -1
  172. package/dist/vite/boot-loader.d.ts +21 -7
  173. package/dist/vite/boot-loader.d.ts.map +1 -1
  174. package/dist/{vite.cjs.js → vite.cjs} +8 -8
  175. package/dist/vite.es.js +29 -16
  176. package/package.json +66 -22
  177. package/scripts/check-duplicates.mjs +198 -0
  178. package/dist/breadcrumb.context-C7VCfh49.js +0 -1501
  179. package/dist/breadcrumb.context-Td6Tjc_z.cjs +0 -7
  180. package/dist/floating-formulas-bg.cjs.js +0 -1
  181. package/dist/i18n-DN_2YzjB.cjs +0 -1
  182. package/dist/i18n.cjs.js +0 -1
  183. package/dist/index.cjs.js +0 -1
  184. 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,15 +55,20 @@ 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
  }
53
63
  /**
54
64
  * Vite plugin that injects the shared Viglet boot loader into `index.html` at
55
- * build/dev time. Every product (Turing, Shio, Dumont, Cloud Marketing)
56
- * consumes this plugin so the loader visuals stay identical across the
57
- * ecosystem and there's a single place to evolve the design.
65
+ * build/dev time, so the loader visuals stay identical across the ecosystem and
66
+ * there is a single place to evolve the design.
67
+ *
68
+ * Four of the six consumers take it: Turing, Shio, Dumont and the Cloud Home.
69
+ * The Cloud Console and Schools are Next applications and have no Vite build to
70
+ * plug into — they render their own first paint, which is what a server-rendered
71
+ * page does instead of a boot loader.
58
72
  *
59
73
  * What the plugin injects:
60
74
  *
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;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.3.2",
3
+ "version": "2026.3.4",
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",
136
+ "@testing-library/jest-dom": "^7.0.1",
137
+ "@testing-library/react": "^16.3.2",
138
+ "@testing-library/user-event": "^14.6.6",
122
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
+ // VDS74 — the exemption is a name's, not a file's: `declaredNames` already
21
+ // ignores an `export { … } from` clause, so a shim reports nothing without a
22
+ // skip, while a name the file *declares* is reported whatever else that file
23
+ // also re-exports. Skipping the file instead let any real copy sitting beside a
24
+ // stray re-export through, which is the case this gate exists to catch.
25
+ //
26
+ // Some collisions are deliberate — Shio's AppFooter renders Shio's own build
27
+ // version and merely shares a name. Keep those by writing, anywhere in the file:
28
+ //
29
+ // // viglet-ds-allow-duplicate AppFooter -- renders Shio's build version
30
+ //
31
+ // The reason is the point. A gate with no way to say "this one is mine" is a
32
+ // gate somebody deletes.
33
+
34
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs"
35
+ import { createRequire } from "node:module"
36
+ import { dirname, join, relative, resolve } from "node:path"
37
+ import { fileURLToPath } from "node:url"
38
+
39
+ const PACKAGE_NAME = "@viglet/viglet-design-system"
40
+ const EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".mts", ".mjs"])
41
+ const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "build", "out", "coverage", ".next"])
42
+
43
+ const args = process.argv.slice(2)
44
+ const asJson = args.includes("--json")
45
+ const warnOnly = args.includes("--warn")
46
+ const manifestFlag = args.indexOf("--manifest")
47
+ const manifestPath = manifestFlag === -1 ? null : args[manifestFlag + 1]
48
+ const roots = args.filter(
49
+ (a, i) => !a.startsWith("--") && i !== manifestFlag + 1,
50
+ )
51
+
52
+ function loadManifest() {
53
+ // An explicit path wins: it is how this repository's own tests pin a fixture,
54
+ // and how a monorepo checks against a version other than the hoisted one.
55
+ if (manifestPath) {
56
+ if (!existsSync(manifestPath)) {
57
+ console.error(`check-duplicates: no manifest at ${manifestPath}`)
58
+ process.exit(1)
59
+ }
60
+ return JSON.parse(readFileSync(manifestPath, "utf8"))
61
+ }
62
+ // Installed in the consumer.
63
+ const require = createRequire(join(process.cwd(), "package.json"))
64
+ try {
65
+ return require(`${PACKAGE_NAME}/exports.json`)
66
+ } catch {
67
+ /* fall through */
68
+ }
69
+ // Running from this repository against its own dist.
70
+ const local = resolve(dirname(fileURLToPath(import.meta.url)), "..", "dist", "exports.json")
71
+ if (existsSync(local)) return JSON.parse(readFileSync(local, "utf8"))
72
+ return null
73
+ }
74
+
75
+ const manifest = loadManifest()
76
+ if (!manifest) {
77
+ console.error(
78
+ `check-duplicates: could not read ${PACKAGE_NAME}/exports.json.\n` +
79
+ `Install ${PACKAGE_NAME} (>= the version that ships the manifest) and re-run.`,
80
+ )
81
+ process.exit(1)
82
+ }
83
+
84
+ // name -> the specifier that exports it. The root entry wins when a name appears
85
+ // in more than one, since that is the import a product would write.
86
+ const exported = new Map()
87
+ for (const [subpath, entry] of Object.entries(manifest.entries)) {
88
+ for (const name of [...entry.values, ...entry.types]) {
89
+ const isType = entry.types.includes(name)
90
+ if (!exported.has(name) || subpath === ".") {
91
+ exported.set(name, { specifier: entry.specifier, isType })
92
+ }
93
+ }
94
+ }
95
+
96
+ function collect(dir, files = []) {
97
+ let dirents
98
+ try {
99
+ dirents = readdirSync(dir, { withFileTypes: true })
100
+ } catch {
101
+ return files
102
+ }
103
+ for (const entry of dirents) {
104
+ const full = join(dir, entry.name)
105
+ if (entry.isDirectory()) {
106
+ if (!SKIP_DIRS.has(entry.name) && !entry.name.startsWith(".")) collect(full, files)
107
+ } else if (EXTENSIONS.has(entry.name.slice(entry.name.lastIndexOf(".")))) {
108
+ files.push(full)
109
+ }
110
+ }
111
+ return files
112
+ }
113
+
114
+ // `export const X`, `export function X`, `export class X`, `export type X`,
115
+ // `export interface X`, and the names inside a local `export { A, B as C }`.
116
+ const DECLARED =
117
+ /^\s*export\s+(?:declare\s+)?(?:default\s+)?(?:abstract\s+)?(?:const|let|var|function\*?|class|type|interface|enum)\s+([A-Za-z_$][\w$]*)/gm
118
+ const EXPORT_LIST = /^\s*export\s+(?:type\s+)?\{([^}]*)\}\s*(?!from)[;\n]/gm
119
+
120
+ function declaredNames(source) {
121
+ const names = new Map()
122
+ for (const match of source.matchAll(DECLARED)) {
123
+ names.set(match[1], lineOf(source, match.index))
124
+ }
125
+ for (const match of source.matchAll(EXPORT_LIST)) {
126
+ for (const clause of match[1].split(",")) {
127
+ // `A as B` exports B; a bare `A` exports A.
128
+ const name = clause.includes(" as ")
129
+ ? clause.split(" as ").pop().trim()
130
+ : clause.trim()
131
+ if (/^[A-Za-z_$][\w$]*$/.test(name) && name !== "default") {
132
+ names.set(name, lineOf(source, match.index))
133
+ }
134
+ }
135
+ }
136
+ return names
137
+ }
138
+
139
+ function lineOf(source, index) {
140
+ return source.slice(0, index).split("\n").length
141
+ }
142
+
143
+ const scanRoots = (roots.length ? roots : ["src"]).map((r) => resolve(r))
144
+ const missing = scanRoots.filter((r) => !existsSync(r) || !statSync(r).isDirectory())
145
+ if (missing.length === scanRoots.length) {
146
+ console.error(
147
+ `check-duplicates: nothing to scan (${scanRoots.map((r) => relative(process.cwd(), r)).join(", ")}).\n` +
148
+ "Pass the source directories explicitly: viglet-ds-check-duplicates <dir> [dir]",
149
+ )
150
+ process.exit(1)
151
+ }
152
+
153
+ const findings = []
154
+ let scanned = 0
155
+ for (const root of scanRoots) {
156
+ for (const file of collect(root)) {
157
+ const source = readFileSync(file, "utf8")
158
+ scanned++
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)