@viglet/viglet-design-system 2026.3.10 → 2026.3.12

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 (235) hide show
  1. package/README.md +323 -36
  2. package/consumers.json +186 -0
  3. package/dist/badge-locale-1_BmNTro.cjs +1 -0
  4. package/dist/badge-locale-Bx7m7tg2.js +501 -0
  5. package/dist/bento/bento-actions-menu.d.ts +8 -0
  6. package/dist/bento/bento-actions-menu.d.ts.map +1 -1
  7. package/dist/bento/bento-back-to-top.d.ts +9 -1
  8. package/dist/bento/bento-back-to-top.d.ts.map +1 -1
  9. package/dist/bento/bento-command-palette.d.ts +12 -0
  10. package/dist/bento/bento-command-palette.d.ts.map +1 -1
  11. package/dist/bento/bento-data-table.d.ts +81 -0
  12. package/dist/bento/bento-data-table.d.ts.map +1 -0
  13. package/dist/bento/bento-diff.d.ts +41 -0
  14. package/dist/bento/bento-diff.d.ts.map +1 -0
  15. package/dist/bento/bento-empty-state.d.ts +8 -1
  16. package/dist/bento/bento-empty-state.d.ts.map +1 -1
  17. package/dist/bento/bento-entity-shell.d.ts.map +1 -1
  18. package/dist/bento/bento-filter-bar.d.ts +60 -0
  19. package/dist/bento/bento-filter-bar.d.ts.map +1 -0
  20. package/dist/bento/bento-form-section.d.ts +10 -3
  21. package/dist/bento/bento-form-section.d.ts.map +1 -1
  22. package/dist/bento/bento-inline-edit.d.ts.map +1 -1
  23. package/dist/bento/bento-nav-rail.d.ts +13 -13
  24. package/dist/bento/bento-nav-rail.d.ts.map +1 -1
  25. package/dist/bento/bento-palette-trigger.d.ts +18 -0
  26. package/dist/bento/bento-palette-trigger.d.ts.map +1 -0
  27. package/dist/bento/bento-section-chrome.d.ts +17 -42
  28. package/dist/bento/bento-section-chrome.d.ts.map +1 -1
  29. package/dist/bento/bento-shell-shortcuts.d.ts +67 -0
  30. package/dist/bento/bento-shell-shortcuts.d.ts.map +1 -0
  31. package/dist/bento/bento-shell.d.ts +53 -0
  32. package/dist/bento/bento-shell.d.ts.map +1 -0
  33. package/dist/bento/bento-shortcuts-dialog.d.ts +9 -7
  34. package/dist/bento/bento-shortcuts-dialog.d.ts.map +1 -1
  35. package/dist/bento/bento-status-marker.d.ts +5 -2
  36. package/dist/bento/bento-status-marker.d.ts.map +1 -1
  37. package/dist/bento/bento-user-menu.d.ts +9 -8
  38. package/dist/bento/bento-user-menu.d.ts.map +1 -1
  39. package/dist/bento/bento-version-rail.d.ts +31 -0
  40. package/dist/bento/bento-version-rail.d.ts.map +1 -0
  41. package/dist/bento/index.d.ts +14 -5
  42. package/dist/bento/index.d.ts.map +1 -1
  43. package/dist/bento.cjs +5 -1
  44. package/dist/bento.css +70 -4
  45. package/dist/bento.es.js +1642 -625
  46. package/dist/catalogue.json +5349 -0
  47. package/dist/components/app-footer.d.ts +7 -0
  48. package/dist/components/app-footer.d.ts.map +1 -1
  49. package/dist/components/backend-status.d.ts +19 -11
  50. package/dist/components/backend-status.d.ts.map +1 -1
  51. package/dist/components/badge-colorful.d.ts +4 -0
  52. package/dist/components/badge-colorful.d.ts.map +1 -1
  53. package/dist/components/badge-locale.d.ts +7 -0
  54. package/dist/components/badge-locale.d.ts.map +1 -1
  55. package/dist/components/error-boundary.d.ts +15 -13
  56. package/dist/components/error-boundary.d.ts.map +1 -1
  57. package/dist/components/language-select.d.ts +8 -0
  58. package/dist/components/language-select.d.ts.map +1 -1
  59. package/dist/components/login/login.d.ts +27 -0
  60. package/dist/components/login/login.d.ts.map +1 -1
  61. package/dist/components/mode-toggle.d.ts +9 -0
  62. package/dist/components/mode-toggle.d.ts.map +1 -1
  63. package/dist/components/nav-secondary.d.ts +7 -0
  64. package/dist/components/nav-secondary.d.ts.map +1 -1
  65. package/dist/components/router/dialog.delete.d.ts +8 -0
  66. package/dist/components/router/dialog.delete.d.ts.map +1 -1
  67. package/dist/components/router/gradient-button-link.d.ts +6 -0
  68. package/dist/components/router/gradient-button-link.d.ts.map +1 -1
  69. package/dist/components/router/index.d.ts +0 -11
  70. package/dist/components/router/index.d.ts.map +1 -1
  71. package/dist/components/router/loading-provider.d.ts +4 -0
  72. package/dist/components/router/loading-provider.d.ts.map +1 -1
  73. package/dist/components/startup-first/startup-first.d.ts +29 -0
  74. package/dist/components/startup-first/startup-first.d.ts.map +1 -1
  75. package/dist/components/ui/accordion.d.ts +17 -0
  76. package/dist/components/ui/accordion.d.ts.map +1 -1
  77. package/dist/components/ui/avatar.d.ts +14 -0
  78. package/dist/components/ui/avatar.d.ts.map +1 -1
  79. package/dist/components/ui/badge.d.ts +6 -0
  80. package/dist/components/ui/badge.d.ts.map +1 -1
  81. package/dist/components/ui/breadcrumb.d.ts +25 -0
  82. package/dist/components/ui/breadcrumb.d.ts.map +1 -1
  83. package/dist/components/ui/button.d.ts +15 -1
  84. package/dist/components/ui/button.d.ts.map +1 -1
  85. package/dist/components/ui/card.d.ts +27 -0
  86. package/dist/components/ui/card.d.ts.map +1 -1
  87. package/dist/components/ui/checkbox.d.ts +4 -0
  88. package/dist/components/ui/checkbox.d.ts.map +1 -1
  89. package/dist/components/ui/dialog.d.ts +36 -0
  90. package/dist/components/ui/dialog.d.ts.map +1 -1
  91. package/dist/components/ui/drawer.d.ts +35 -0
  92. package/dist/components/ui/drawer.d.ts.map +1 -1
  93. package/dist/components/ui/dropdown-menu.d.ts +53 -0
  94. package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
  95. package/dist/components/ui/floating-formulas-bg.d.ts +8 -0
  96. package/dist/components/ui/floating-formulas-bg.d.ts.map +1 -1
  97. package/dist/components/ui/form-actions.d.ts +7 -0
  98. package/dist/components/ui/form-actions.d.ts.map +1 -1
  99. package/dist/components/ui/form-item-two-columns.d.ts +7 -0
  100. package/dist/components/ui/form-item-two-columns.d.ts.map +1 -1
  101. package/dist/components/ui/form.d.ts +26 -1
  102. package/dist/components/ui/form.d.ts.map +1 -1
  103. package/dist/components/ui/glass-card.d.ts +13 -14
  104. package/dist/components/ui/glass-card.d.ts.map +1 -1
  105. package/dist/components/ui/gradient-button.d.ts +15 -1
  106. package/dist/components/ui/gradient-button.d.ts.map +1 -1
  107. package/dist/components/ui/gradient-switch.d.ts +6 -0
  108. package/dist/components/ui/gradient-switch.d.ts.map +1 -1
  109. package/dist/components/ui/hover-card.d.ts +14 -0
  110. package/dist/components/ui/hover-card.d.ts.map +1 -1
  111. package/dist/components/ui/index.d.ts +2 -1
  112. package/dist/components/ui/index.d.ts.map +1 -1
  113. package/dist/components/ui/input.d.ts +6 -0
  114. package/dist/components/ui/input.d.ts.map +1 -1
  115. package/dist/components/ui/label.d.ts +4 -0
  116. package/dist/components/ui/label.d.ts.map +1 -1
  117. package/dist/components/ui/navigation-menu.d.ts +29 -2
  118. package/dist/components/ui/navigation-menu.d.ts.map +1 -1
  119. package/dist/components/ui/pagination.d.ts +25 -0
  120. package/dist/components/ui/pagination.d.ts.map +1 -1
  121. package/dist/components/ui/popover.d.ts +24 -0
  122. package/dist/components/ui/popover.d.ts.map +1 -1
  123. package/dist/components/ui/progress.d.ts +4 -0
  124. package/dist/components/ui/progress.d.ts.map +1 -1
  125. package/dist/components/ui/pulse-ring.d.ts +9 -11
  126. package/dist/components/ui/pulse-ring.d.ts.map +1 -1
  127. package/dist/components/ui/resizable.d.ts +11 -0
  128. package/dist/components/ui/resizable.d.ts.map +1 -1
  129. package/dist/components/ui/section-card.d.ts +9 -0
  130. package/dist/components/ui/section-card.d.ts.map +1 -1
  131. package/dist/components/ui/select.d.ts +36 -0
  132. package/dist/components/ui/select.d.ts.map +1 -1
  133. package/dist/components/ui/separator.d.ts +4 -0
  134. package/dist/components/ui/separator.d.ts.map +1 -1
  135. package/dist/components/ui/sheet.d.ts +29 -0
  136. package/dist/components/ui/sheet.d.ts.map +1 -1
  137. package/dist/components/ui/sidebar.d.ts +83 -0
  138. package/dist/components/ui/sidebar.d.ts.map +1 -1
  139. package/dist/components/ui/skeleton.d.ts +4 -0
  140. package/dist/components/ui/skeleton.d.ts.map +1 -1
  141. package/dist/components/ui/slider.d.ts +4 -0
  142. package/dist/components/ui/slider.d.ts.map +1 -1
  143. package/dist/components/ui/sonner.d.ts +6 -2
  144. package/dist/components/ui/sonner.d.ts.map +1 -1
  145. package/dist/components/ui/stepper.d.ts +8 -0
  146. package/dist/components/ui/stepper.d.ts.map +1 -1
  147. package/dist/components/ui/sticky-save-bar.d.ts +7 -0
  148. package/dist/components/ui/sticky-save-bar.d.ts.map +1 -1
  149. package/dist/components/ui/switch.d.ts +4 -0
  150. package/dist/components/ui/switch.d.ts.map +1 -1
  151. package/dist/components/ui/table.d.ts +31 -0
  152. package/dist/components/ui/table.d.ts.map +1 -1
  153. package/dist/components/ui/tabs.d.ts +16 -0
  154. package/dist/components/ui/tabs.d.ts.map +1 -1
  155. package/dist/components/ui/textarea.d.ts +4 -0
  156. package/dist/components/ui/textarea.d.ts.map +1 -1
  157. package/dist/components/ui/toggle-group.d.ts +7 -0
  158. package/dist/components/ui/toggle-group.d.ts.map +1 -1
  159. package/dist/components/ui/toggle.d.ts +4 -0
  160. package/dist/components/ui/toggle.d.ts.map +1 -1
  161. package/dist/components/ui/tooltip.d.ts +18 -0
  162. package/dist/components/ui/tooltip.d.ts.map +1 -1
  163. package/dist/components/ui/tree.d.ts +60 -0
  164. package/dist/components/ui/tree.d.ts.map +1 -0
  165. package/dist/components/ui/user-avatar.d.ts +2 -1
  166. package/dist/components/ui/user-avatar.d.ts.map +1 -1
  167. package/dist/components/ui/viglet-assistant.d.ts +71 -28
  168. package/dist/components/ui/viglet-assistant.d.ts.map +1 -1
  169. package/dist/components/ui/viglet-avatar.d.ts +14 -12
  170. package/dist/components/ui/viglet-avatar.d.ts.map +1 -1
  171. package/dist/contexts/breadcrumb.context.d.ts +7 -0
  172. package/dist/contexts/breadcrumb.context.d.ts.map +1 -1
  173. package/dist/contexts/user.context.d.ts +8 -0
  174. package/dist/contexts/user.context.d.ts.map +1 -1
  175. package/dist/exports.json +344 -28
  176. package/dist/i18n/index.d.ts +94 -0
  177. package/dist/i18n/index.d.ts.map +1 -1
  178. package/dist/i18n/locales/en/assistant.json.d.ts +4 -1
  179. package/dist/i18n/locales/en/bento.json.d.ts +45 -1
  180. package/dist/i18n/locales/pt/assistant.json.d.ts +4 -1
  181. package/dist/i18n/locales/pt/bento.json.d.ts +45 -1
  182. package/dist/i18n.cjs +1 -1
  183. package/dist/i18n.es.js +98 -4
  184. package/dist/index.cjs +1 -1
  185. package/dist/index.es.js +2094 -913
  186. package/dist/lib/busy.d.ts +26 -0
  187. package/dist/lib/busy.d.ts.map +1 -0
  188. package/dist/lib/corner-slot.d.ts +15 -0
  189. package/dist/lib/corner-slot.d.ts.map +1 -0
  190. package/dist/{loading-provider-E3fbcekM.js → loading-provider-D-_5G-08.js} +71 -69
  191. package/dist/loading-provider-E4tIG9BM.cjs +1 -0
  192. package/dist/preset.css +7 -10
  193. package/dist/router.cjs +1 -1
  194. package/dist/router.es.js +2 -776
  195. package/dist/user.context-BXQ7lgg5.js +815 -0
  196. package/dist/user.context-D01CI8gq.cjs +1 -0
  197. package/dist/viglet-design-system.css +1 -1
  198. package/docs/BENTO-AUTHORING.md +61 -9
  199. package/package.json +13 -2
  200. package/scripts/consumer-entries.mjs +209 -0
  201. package/scripts/lib/contrast.mjs +301 -0
  202. package/scripts/mcp.mjs +300 -0
  203. package/scripts/page-lint.mjs +428 -0
  204. package/scripts/page-reference.mjs +54 -10
  205. package/dist/badge-locale-Bij94NwL.cjs +0 -1
  206. package/dist/badge-locale-Du41PpLA.js +0 -533
  207. package/dist/breadcrumb.context-BssgEydx.js +0 -926
  208. package/dist/breadcrumb.context-DLsiMS59.cjs +0 -1
  209. package/dist/components/router/blank-slate.d.ts +0 -21
  210. package/dist/components/router/blank-slate.d.ts.map +0 -1
  211. package/dist/components/router/grid.list.d.ts +0 -39
  212. package/dist/components/router/grid.list.d.ts.map +0 -1
  213. package/dist/components/router/internal.sidebar.d.ts +0 -61
  214. package/dist/components/router/internal.sidebar.d.ts.map +0 -1
  215. package/dist/components/router/nav-main.d.ts +0 -25
  216. package/dist/components/router/nav-main.d.ts.map +0 -1
  217. package/dist/components/router/nav-user.d.ts +0 -24
  218. package/dist/components/router/nav-user.d.ts.map +0 -1
  219. package/dist/components/router/page-content.d.ts +0 -12
  220. package/dist/components/router/page-content.d.ts.map +0 -1
  221. package/dist/components/router/page-header.d.ts +0 -23
  222. package/dist/components/router/page-header.d.ts.map +0 -1
  223. package/dist/components/router/page.d.ts +0 -22
  224. package/dist/components/router/page.d.ts.map +0 -1
  225. package/dist/components/router/sticky.page.header.d.ts +0 -85
  226. package/dist/components/router/sticky.page.header.d.ts.map +0 -1
  227. package/dist/components/router/sub.page.d.ts +0 -50
  228. package/dist/components/router/sub.page.d.ts.map +0 -1
  229. package/dist/components/router/sub.page.header.d.ts +0 -37
  230. package/dist/components/router/sub.page.header.d.ts.map +0 -1
  231. package/dist/lib/react-markers.d.ts +0 -17
  232. package/dist/lib/react-markers.d.ts.map +0 -1
  233. package/dist/loading-provider-B6-R2ONB.cjs +0 -1
  234. package/dist/user.context-CPmUBBLa.js +0 -522
  235. package/dist/user.context-xsJgPpcO.cjs +0 -1
@@ -0,0 +1,428 @@
1
+ #!/usr/bin/env node
2
+ // VDS132 — fail a consumer's lint on the two rules BENTO-AUTHORING calls easy
3
+ // to get wrong, and name the file and line of each.
4
+ //
5
+ // Run it from a product checkout, beside viglet-ds-check-duplicates:
6
+ //
7
+ // viglet-ds-page-lint # scan ./src
8
+ // viglet-ds-page-lint src app # scan these directories
9
+ // viglet-ds-page-lint --json # machine-readable findings
10
+ // viglet-ds-page-lint --warn # report, but exit 0
11
+ // viglet-ds-page-lint --page-pattern <re> # which files are routed pages
12
+ // viglet-ds-page-lint --contrast <css> # measure this stylesheet's tokens (repeatable)
13
+ //
14
+ // The rules:
15
+ //
16
+ // page.column A routed page's outermost element sets a max width, a gutter
17
+ // or a vertical rhythm. BentoShell owns all three and offers the
18
+ // other widths by name, so a page that sets its own disagrees
19
+ // with the next page, and the defect exists only between screens.
20
+ // primary.direct A stylesheet or a style object sets --vg-primary or
21
+ // --vg-primary-foreground. One value there wins on both grounds,
22
+ // so the dark ground takes the light colour; the four
23
+ // --vg-primary-*-base inputs are read per ground.
24
+ // contrast.pair With --contrast: a token pair the product's stylesheet
25
+ // re-keys falls under 4.5:1 on either ground. Measured over the
26
+ // installed preset with the product's stylesheets laid on top,
27
+ // the way they cascade (VDS137).
28
+ // contrast.unread With --contrast: a re-keyed token this cannot read as an
29
+ // opaque colour, reported rather than measured as black.
30
+ //
31
+ // The first two were prose in the vendored skill, and the bins beside this one
32
+ // read exports, imports and vendored files, never a consumer's pages or stylesheets.
33
+ //
34
+ // A page is a file whose name matches `--page-pattern` (default `.page.tsx` or
35
+ // `.page.jsx`), and its outermost element is what its default export returns:
36
+ // each branch of a conditional, and each child of a fragment. A width set deeper
37
+ // inside the page is not this rule's business. A class counts when it is written
38
+ // as a string, in a template, or as a string inside a call such as `cn(...)`.
39
+ //
40
+ // Some pages are deliberately their own, a print view or a kiosk. Keep one by
41
+ // writing, anywhere in the file:
42
+ //
43
+ // // viglet-ds-allow-page-column -- a kiosk with no shell
44
+ // /* viglet-ds-allow-primary -- the theme editor previews a raw value */
45
+ // /* viglet-ds-allow-contrast -- the brand orange, signed off at 4.2:1 as large text */
46
+ //
47
+ // The reason is required. An exemption with none is reported as the finding it
48
+ // was meant to excuse.
49
+
50
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs"
51
+ import { createRequire } from "node:module"
52
+ import { dirname, join, relative, resolve } from "node:path"
53
+ import { fileURLToPath } from "node:url"
54
+
55
+ import { AA_TEXT, measurePairs } from "./lib/contrast.mjs"
56
+
57
+ const SOURCE_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".mts", ".mjs", ".cts", ".cjs"])
58
+ const STYLE_EXTENSIONS = new Set([".css", ".scss"])
59
+ const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "build", "out", "coverage", ".next", "storybook-static"])
60
+
61
+ const args = process.argv.slice(2)
62
+ const asJson = args.includes("--json")
63
+ const warnOnly = args.includes("--warn")
64
+ // The value after a flag is not a root. Skipped by index and only where the flag
65
+ // is: VDS78 is what `indexOf(...) + 1` does when it is not.
66
+ const VALUE_FLAGS = new Set(["--page-pattern", "--contrast"])
67
+ const valueAt = new Set(args.flatMap((a, i) => (VALUE_FLAGS.has(a) ? [i + 1] : [])))
68
+ const patternFlag = args.indexOf("--page-pattern")
69
+ const pagePattern = new RegExp(patternFlag === -1 ? String.raw`\.page\.[jt]sx$` : (args[patternFlag + 1] ?? ""))
70
+ const contrastSheets = args.flatMap((a, i) => (a === "--contrast" && args[i + 1] ? [resolve(args[i + 1])] : []))
71
+ const roots = args.filter((a, i) => !a.startsWith("--") && !valueAt.has(i))
72
+
73
+ /** The consumer's TypeScript, or this package's own where it is a checkout. */
74
+ function loadTypeScript() {
75
+ for (const from of [join(process.cwd(), "package.json"), import.meta.url]) {
76
+ try {
77
+ return createRequire(from)("typescript")
78
+ } catch {
79
+ /* try the next */
80
+ }
81
+ }
82
+ console.error(
83
+ "page-lint: reads pages with the TypeScript compiler, and none is installed here.\n" +
84
+ "Add typescript to this repository's devDependencies and re-run.",
85
+ )
86
+ process.exit(1)
87
+ }
88
+
89
+ const scanRoots = (roots.length ? roots : ["src"]).map((r) => resolve(r))
90
+ const missing = scanRoots.filter((r) => !existsSync(r) || !statSync(r).isDirectory())
91
+ if (missing.length > 0) {
92
+ console.error(
93
+ `page-lint: not a directory: ${missing.map((r) => relative(process.cwd(), r) || ".").join(", ")}\n` +
94
+ "Pass the source directories explicitly: viglet-ds-page-lint <dir> [dir]",
95
+ )
96
+ process.exit(1)
97
+ }
98
+
99
+ function collect(dir, files = []) {
100
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
101
+ const full = join(dir, entry.name)
102
+ if (entry.isDirectory()) {
103
+ if (!SKIP_DIRS.has(entry.name) && !entry.name.startsWith(".")) collect(full, files)
104
+ } else {
105
+ files.push(full)
106
+ }
107
+ }
108
+ return files
109
+ }
110
+
111
+ const extensionOf = (file) => file.slice(file.lastIndexOf("."))
112
+ const lineOf = (text, index) => text.slice(0, index).split("\n").length
113
+ const shown = (file) => relative(process.cwd(), file).replaceAll("\\", "/")
114
+
115
+ /** Whether a pragma is present, and whether it carries a reason. */
116
+ function pragma(source, name) {
117
+ const found = source.match(new RegExp(String.raw`viglet-ds-allow-${name}\b(.*)`))
118
+ if (!found) return "absent"
119
+ return /^\s*--\s*\S/.test(found[1]) ? "reasoned" : "unreasoned"
120
+ }
121
+
122
+ // ---------------------------------------------------------------------------
123
+ // page.column
124
+
125
+ const WIDTH = /^(?:max-w-(?!none$|full$).+|container)$/
126
+ const GUTTER = /^(?:p|px|pl|pr|ps|pe)-(?!0$).+$/
127
+ const RHYTHM = /^(?:py|pt|pb|my|mt|mb)-(?!0$).+$/
128
+
129
+ /** `md:max-w-7xl!` -> `max-w-7xl`: variants and the important marker say where, not what. */
130
+ function utility(token) {
131
+ return token.split(":").pop().replace(/^!/, "").replace(/!$/, "")
132
+ }
133
+
134
+ function layoutClasses(classNames) {
135
+ return classNames
136
+ .flatMap((text) => text.split(/\s+/))
137
+ .filter(Boolean)
138
+ .filter((token) => {
139
+ const name = utility(token)
140
+ return WIDTH.test(name) || GUTTER.test(name) || RHYTHM.test(name)
141
+ })
142
+ }
143
+
144
+ function pageFindings(ts, file, source) {
145
+ const sf = ts.createSourceFile(file, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX)
146
+
147
+ /** Every string a className expression can produce, conditions and calls included. */
148
+ function strings(node, found = []) {
149
+ if (!node) return found
150
+ if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
151
+ found.push(node.text)
152
+ } else if (ts.isTemplateExpression(node)) {
153
+ found.push(node.head.text, ...node.templateSpans.map((span) => span.literal.text))
154
+ for (const span of node.templateSpans) strings(span.expression, found)
155
+ } else {
156
+ // forEachChild stops at the first callback that returns a value, so this
157
+ // one returns nothing: `cn("grid", wide && "max-w-6xl")` has two strings.
158
+ ts.forEachChild(node, (child) => {
159
+ strings(child, found)
160
+ })
161
+ }
162
+ return found
163
+ }
164
+
165
+ function classNameOf(opening) {
166
+ for (const attribute of opening.attributes.properties) {
167
+ if (!ts.isJsxAttribute(attribute) || attribute.name.getText(sf) !== "className") continue
168
+ return strings(attribute.initializer)
169
+ }
170
+ return []
171
+ }
172
+
173
+ /** The outermost elements an expression renders. */
174
+ function outermost(node, found = []) {
175
+ if (!node) return found
176
+ if (ts.isParenthesizedExpression(node)) return outermost(node.expression, found)
177
+ if (ts.isConditionalExpression(node)) {
178
+ outermost(node.whenTrue, found)
179
+ return outermost(node.whenFalse, found)
180
+ }
181
+ if (ts.isBinaryExpression(node)) return outermost(node.right, found)
182
+ if (ts.isJsxElement(node)) found.push(node.openingElement)
183
+ else if (ts.isJsxSelfClosingElement(node)) found.push(node)
184
+ else if (ts.isJsxFragment(node)) {
185
+ for (const child of node.children) outermost(child, found)
186
+ }
187
+ return found
188
+ }
189
+
190
+ /** What a function returns, without descending into the functions it declares. */
191
+ function returned(fn) {
192
+ if (!fn.body) return []
193
+ if (!ts.isBlock(fn.body)) return [fn.body]
194
+ const found = []
195
+ const visit = (node) => {
196
+ if (ts.isFunctionLike(node)) return
197
+ if (ts.isReturnStatement(node) && node.expression) found.push(node.expression)
198
+ ts.forEachChild(node, visit)
199
+ }
200
+ ts.forEachChild(fn.body, visit)
201
+ return found
202
+ }
203
+
204
+ /** A function, through `memo(...)`, `forwardRef(...)` or an identifier declared here. */
205
+ function asFunction(node) {
206
+ if (!node) return null
207
+ if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node)) return node
208
+ if (ts.isParenthesizedExpression(node)) return asFunction(node.expression)
209
+ if (ts.isCallExpression(node)) return asFunction(node.arguments[0])
210
+ if (ts.isIdentifier(node)) {
211
+ for (const statement of sf.statements) {
212
+ if (ts.isFunctionDeclaration(statement) && statement.name?.text === node.text) return statement
213
+ if (ts.isVariableStatement(statement)) {
214
+ for (const declaration of statement.declarationList.declarations) {
215
+ if (ts.isIdentifier(declaration.name) && declaration.name.text === node.text) {
216
+ return asFunction(declaration.initializer)
217
+ }
218
+ }
219
+ }
220
+ }
221
+ }
222
+ return null
223
+ }
224
+
225
+ let page = null
226
+ for (const statement of sf.statements) {
227
+ if (ts.isExportAssignment(statement)) page = asFunction(statement.expression)
228
+ const modifiers = ts.canHaveModifiers(statement) ? (ts.getModifiers(statement) ?? []) : []
229
+ if (
230
+ ts.isFunctionDeclaration(statement) &&
231
+ modifiers.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) &&
232
+ modifiers.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword)
233
+ ) {
234
+ page = statement
235
+ }
236
+ }
237
+ if (!page) return []
238
+
239
+ const findings = []
240
+ for (const expression of returned(page)) {
241
+ for (const opening of outermost(expression)) {
242
+ const offending = layoutClasses(classNameOf(opening))
243
+ if (offending.length === 0) continue
244
+ findings.push({
245
+ file: shown(file),
246
+ line: sf.getLineAndCharacterOfPosition(opening.getStart(sf)).line + 1,
247
+ rule: "page.column",
248
+ detail: `the page's outermost element sets ${[...new Set(offending)].join(" ")}`,
249
+ fix: 'remove them: BentoShell sets the column, and column="narrow" or "wide" names the other widths',
250
+ })
251
+ }
252
+ }
253
+ return findings
254
+ }
255
+
256
+ // ---------------------------------------------------------------------------
257
+ // primary.direct
258
+
259
+ // A declaration or a write, never a read: `var(--vg-primary)` has no colon after
260
+ // the name, `--vg-primary-base:` has more name before it, and
261
+ // `getPropertyValue("--vg-primary")` closes a call where `setProperty` takes a
262
+ // second argument.
263
+ const DECLARED = /--vg-primary(?:-foreground)?\s*:/g
264
+ const KEYED = /["'`]--vg-primary(?:-foreground)?["'`]\s*[:,]/g
265
+
266
+ // Blanked rather than removed, so a finding's line survives. In a script only a
267
+ // comment that opens its line: `//` also sits inside a URL, and a glob such as
268
+ // "src/**" opens a block anywhere else.
269
+ const blank = (text) => text.replace(/[^\n]/g, " ")
270
+ const withoutComments = (source, isStyle) =>
271
+ isStyle
272
+ ? source.replace(/\/\*[\s\S]*?\*\//g, blank)
273
+ : source.replace(/^[ \t]*\/\*[\s\S]*?\*\//gm, blank).replace(/^[ \t]*\/\/.*$/gm, blank)
274
+
275
+ function primaryFindings(file, source, isStyle) {
276
+ const text = withoutComments(source, isStyle)
277
+ const findings = []
278
+ const seen = new Set()
279
+ for (const pattern of isStyle ? [DECLARED] : [DECLARED, KEYED]) {
280
+ for (const match of text.matchAll(pattern)) {
281
+ const line = lineOf(text, match.index)
282
+ const name = match[0].replace(/["'`:,\s]/g, "")
283
+ if (seen.has(`${line}:${name}`)) continue
284
+ seen.add(`${line}:${name}`)
285
+ findings.push({
286
+ file: shown(file),
287
+ line,
288
+ rule: "primary.direct",
289
+ detail: `sets ${name}`,
290
+ fix: "claim it through the --vg-primary-base and --vg-primary-foreground-base inputs, light and -dark, at :root",
291
+ })
292
+ }
293
+ }
294
+ return findings.sort((a, b) => a.line - b.line)
295
+ }
296
+
297
+ // ---------------------------------------------------------------------------
298
+
299
+ let ts = null
300
+ const findings = []
301
+ let scanned = 0
302
+ let pages = 0
303
+
304
+ for (const root of scanRoots) {
305
+ for (const file of collect(root)) {
306
+ const extension = extensionOf(file)
307
+ const isStyle = STYLE_EXTENSIONS.has(extension)
308
+ const isSource = SOURCE_EXTENSIONS.has(extension)
309
+ if (!isStyle && !isSource) continue
310
+ if (/\.(test|spec|stories)\./.test(file)) continue
311
+
312
+ const source = readFileSync(file, "utf8")
313
+ scanned++
314
+
315
+ const fileFindings = []
316
+ if (isSource && pagePattern.test(file.replaceAll("\\", "/"))) {
317
+ pages++
318
+ ts ??= loadTypeScript()
319
+ fileFindings.push(...pageFindings(ts, file, source))
320
+ }
321
+ fileFindings.push(...primaryFindings(file, source, isStyle))
322
+
323
+ const allowed = {
324
+ "page.column": pragma(source, "page-column"),
325
+ "primary.direct": pragma(source, "primary"),
326
+ }
327
+ for (const finding of fileFindings) {
328
+ const state = allowed[finding.rule]
329
+ if (state === "reasoned") continue
330
+ if (state === "unreasoned") finding.detail += " (an exemption here gives no reason, so it does not apply)"
331
+ findings.push(finding)
332
+ }
333
+ }
334
+ }
335
+
336
+ if (scanned === 0) {
337
+ console.error(
338
+ `page-lint: no source file under ${scanRoots.map((r) => shown(r) || ".").join(", ")}, so this would pass vacuously.`,
339
+ )
340
+ process.exit(1)
341
+ }
342
+
343
+ // ---------------------------------------------------------------------------
344
+ // contrast.pair and contrast.unread
345
+
346
+ /** The preset this package ships, or its source where this is a checkout before a build. */
347
+ function presetCss() {
348
+ const here = dirname(fileURLToPath(import.meta.url))
349
+ for (const candidate of [join(here, "..", "dist", "preset.css"), join(here, "..", "src", "styles", "preset.css")]) {
350
+ if (existsSync(candidate)) return readFileSync(candidate, "utf8")
351
+ }
352
+ console.error("page-lint: --contrast needs the preset, and this copy of the package has none.")
353
+ process.exit(1)
354
+ }
355
+
356
+ /**
357
+ * Every pair a product's stylesheet re-keys, measured on both grounds. A pair
358
+ * the product does not touch is the preset's, and the package's own gate holds
359
+ * it, so it is not reported here.
360
+ */
361
+ function contrastFindings() {
362
+ const absent = contrastSheets.filter((file) => !existsSync(file))
363
+ if (absent.length > 0) {
364
+ console.error(`page-lint: --contrast names a stylesheet that is not there: ${absent.map(shown).join(", ")}`)
365
+ process.exit(1)
366
+ }
367
+ const products = contrastSheets.map((file) => ({ name: shown(file), css: readFileSync(file, "utf8") }))
368
+ const sheets = [{ name: "preset", css: presetCss() }, ...products]
369
+
370
+ const byPair = new Map()
371
+ for (const measured of [...measurePairs(sheets, "light"), ...measurePairs(sheets, "dark")]) {
372
+ const key = `${measured.foreground} on ${measured.surface}`
373
+ byPair.set(key, { ...byPair.get(key), [measured.ground]: measured })
374
+ }
375
+
376
+ const out = []
377
+ for (const [pair, grounds] of byPair) {
378
+ const both = [grounds.light, grounds.dark].filter(Boolean)
379
+ const touched = both.flatMap((m) => m.declared).find((d) => d.sheet !== "preset")
380
+ if (!touched) continue
381
+
382
+ const unread = [...new Set(both.flatMap((m) => m.unread))]
383
+ const failing = both.some((m) => m.ratio !== null && m.ratio < AA_TEXT)
384
+ if (unread.length === 0 && !failing) continue
385
+
386
+ const product = products.find((p) => p.name === touched.sheet)
387
+ const exemption = pragma(product.css, "contrast")
388
+ if (exemption === "reasoned") continue
389
+
390
+ const at = product.css.search(new RegExp(String.raw`${touched.token}\s*:`))
391
+ // Only a token with a -dark twin can be stepped on one ground alone.
392
+ const twin = sheets.some((sheet) => sheet.css.includes(`${touched.token}-dark:`))
393
+ const said = both
394
+ .map((m) => (m.ratio === null ? `unread on the ${m.ground} ground` : `${m.ratio.toFixed(2)}:1 on the ${m.ground} ground`))
395
+ .join(" and ")
396
+ out.push({
397
+ file: product.name,
398
+ line: at === -1 ? 1 : lineOf(product.css, at),
399
+ rule: unread.length > 0 ? "contrast.unread" : "contrast.pair",
400
+ detail:
401
+ `${pair} is ${said}; ${AA_TEXT}:1 needed` +
402
+ (exemption === "unreasoned" ? " (an exemption here gives no reason, so it does not apply)" : ""),
403
+ fix:
404
+ unread.length > 0
405
+ ? `write ${unread.join(" and ")} as an oklch(), hex, rgb() or hsl() colour, or a var() or color-mix(in oklab) this can follow`
406
+ : `step ${touched.token} until the pair clears ${AA_TEXT}:1 on both grounds` +
407
+ (twin ? `; ${touched.token}-dark sets the dark ground on its own` : ""),
408
+ })
409
+ }
410
+ return out
411
+ }
412
+
413
+ if (contrastSheets.length > 0) findings.push(...contrastFindings())
414
+
415
+ if (asJson) {
416
+ console.log(JSON.stringify({ scanned, pages, findings }, null, 2))
417
+ } else if (findings.length === 0) {
418
+ const measuredToo = contrastSheets.length > 0 ? `, every pair ${contrastSheets.length} stylesheet(s) re-key clears ${AA_TEXT}:1` : ""
419
+ console.log(`page-lint: ${scanned} file(s) scanned, ${pages} of them pages — no page sets its own column, nothing sets --vg-primary${measuredToo}.`)
420
+ } else {
421
+ console.error(`\npage-lint: ${findings.length} finding(s)\n`)
422
+ for (const finding of findings) {
423
+ console.error(` ${finding.file}:${finding.line} ${finding.rule} ${finding.detail}`)
424
+ console.error(` ${finding.fix}\n`)
425
+ }
426
+ }
427
+
428
+ process.exit(findings.length > 0 && !warnOnly ? 1 : 0)
@@ -102,10 +102,14 @@ An artboard opens in a browser straight from the file tree.
102
102
  - A product claims \`--primary\` through the four \`--vg-primary-*-base\` inputs at \`:root\`,
103
103
  never by setting \`--vg-primary\` itself, which would key the dark ground to the light value.
104
104
 
105
+ \`viglet-ds-page-lint <dir>\` fails on either one, naming the file and line. Point it at the
106
+ directories whose pages render inside \`BentoShell\`.
107
+
105
108
  ## Checking it is current
106
109
 
107
110
  \`\`\`bash
108
111
  viglet-ds-page-reference --check
112
+ viglet-ds-page-lint src
109
113
  \`\`\`
110
114
  `
111
115
  }
@@ -183,10 +187,42 @@ function declaration() {
183
187
  }
184
188
  }
185
189
 
190
+ /**
191
+ * VDS135 — whether this repository enables the `viglet-ds` Claude Code plugin.
192
+ *
193
+ * The plugin delivers the skill into every session by name and updates with it,
194
+ * so a vendored copy beside it is a second skill of the same name that drifts.
195
+ * The artboards are different: a consumer wants them on disk, and they stay.
196
+ * Read from the project's settings, where enabling a plugin for a repository is
197
+ * recorded, and never guessed from what is installed on this machine.
198
+ */
199
+ function pluginEnabled() {
200
+ for (const name of [".claude/settings.json", ".claude/settings.local.json"]) {
201
+ const at = join(root, name)
202
+ if (!existsSync(at)) continue
203
+ try {
204
+ const enabled = JSON.parse(readFileSync(at, "utf8")).enabledPlugins ?? {}
205
+ if (Object.entries(enabled).some(([id, on]) => on === true && id.split("@")[0] === "viglet-ds")) {
206
+ return true
207
+ }
208
+ } catch {
209
+ /* a settings file this cannot read enables nothing */
210
+ }
211
+ }
212
+ return false
213
+ }
214
+
215
+ const plugin = pluginEnabled()
216
+ const SKILL_FILES = [SKILL, `${SKILL_DIR}/authoring.md`, `${SKILL_DIR}/boundary.md`]
217
+
186
218
  const surfaces = [
187
- copied(SKILL, orientation(canvasDir, artboards.length)),
188
- copied(`${SKILL_DIR}/authoring.md`, readFileSync(join(payload, "docs", "BENTO-AUTHORING.md"), "utf8")),
189
- copied(`${SKILL_DIR}/boundary.md`, readFileSync(join(payload, "docs", "BENTO-BOUNDARY.md"), "utf8")),
219
+ ...(plugin
220
+ ? []
221
+ : [
222
+ copied(SKILL, orientation(canvasDir, artboards.length)),
223
+ copied(`${SKILL_DIR}/authoring.md`, readFileSync(join(payload, "docs", "BENTO-AUTHORING.md"), "utf8")),
224
+ copied(`${SKILL_DIR}/boundary.md`, readFileSync(join(payload, "docs", "BENTO-BOUNDARY.md"), "utf8")),
225
+ ]),
190
226
  ...artboards.map((name) =>
191
227
  copied(`${canvasDir}/vds-${name}`, readFileSync(join(REFERENCE, name), "utf8")),
192
228
  ),
@@ -200,11 +236,13 @@ const surfaces = [
200
236
  * "refresh" means downgrade and every word of a drift report would be wrong. So
201
237
  * it is named and refused instead.
202
238
  */
203
- const wired = existsSync(join(root, SKILL))
204
- const stamped = wired
239
+ const wired = plugin || existsSync(join(root, SKILL))
240
+ const stamped = existsSync(join(root, SKILL))
205
241
  ? (/^vds-version:\s*(\S+)\s*$/m.exec(readFileSync(join(root, SKILL), "utf8"))?.[1] ?? null)
206
242
  : null
207
243
  const ahead =
244
+ // With the plugin on, the vendored skill is on its way out, so its stamp says nothing.
245
+ !plugin &&
208
246
  stamped !== null &&
209
247
  stamped !== version &&
210
248
  stamped.localeCompare(version, undefined, { numeric: true }) > 0
@@ -220,11 +258,16 @@ const changing = surfaces.filter((s) => s.drifted)
220
258
  * gate to mean what it says.
221
259
  */
222
260
  const shipped = new Set(artboards.map((name) => `vds-${name}`))
223
- const orphans = existsSync(join(root, canvasDir))
224
- ? readdirSync(join(root, canvasDir))
225
- .filter((name) => name.startsWith("vds-") && name.endsWith(".dc.html") && !shipped.has(name))
226
- .map((name) => `${canvasDir}/${name}`)
227
- : []
261
+ const orphans = [
262
+ ...(existsSync(join(root, canvasDir))
263
+ ? readdirSync(join(root, canvasDir))
264
+ .filter((name) => name.startsWith("vds-") && name.endsWith(".dc.html") && !shipped.has(name))
265
+ .map((name) => `${canvasDir}/${name}`)
266
+ : []),
267
+ // A vendored skill left behind once the plugin carries it. Only one this
268
+ // package stamped: a skill of the same name the repository wrote is its own.
269
+ ...(plugin && stamped !== null ? SKILL_FILES.filter((file) => existsSync(join(root, file))) : []),
270
+ ]
228
271
 
229
272
  /** created / updated, in the conditional under `--check` and the past tense after a write. */
230
273
  function state(surface) {
@@ -246,6 +289,7 @@ if (asJson) {
246
289
  root,
247
290
  canvasDir,
248
291
  wired,
292
+ plugin,
249
293
  stamped,
250
294
  ahead,
251
295
  code: code(),
@@ -1 +0,0 @@
1
- const e=require("./rolldown-runtime-BocRIvOZ.cjs"),t=require("./utils-Oagwe_oa.cjs");let n=require("react");n=e.t(n,1);let r=require("react/jsx-runtime"),i=require("@radix-ui/react-slot"),a=require("class-variance-authority"),o=require("react-i18next"),s=require("@radix-ui/react-dialog");s=e.t(s,1);let c=require("@radix-ui/react-dropdown-menu");c=e.t(c,1);let l=require("@radix-ui/react-tooltip");l=e.t(l,1);var u=(...e)=>e.filter((e,t,n)=>!!e&&e.trim()!==``&&n.indexOf(e)===t).join(` `).trim(),d=e=>e.replace(/([a-z0-9])([A-Z])/g,`$1-$2`).toLowerCase(),f=e=>e.replace(/^([A-Z])|[\s-_]+(\w)/g,(e,t,n)=>n?n.toUpperCase():t.toLowerCase()),p=e=>{let t=f(e);return t.charAt(0).toUpperCase()+t.slice(1)},m={xmlns:`http://www.w3.org/2000/svg`,width:24,height:24,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:2,strokeLinecap:`round`,strokeLinejoin:`round`},h=e=>{for(let t in e)if(t.startsWith(`aria-`)||t===`role`||t===`title`)return!0;return!1},g=(0,n.createContext)({}),_=()=>(0,n.useContext)(g),v=(0,n.forwardRef)(({color:e,size:t,strokeWidth:r,absoluteStrokeWidth:i,className:a=``,children:o,iconNode:s,...c},l)=>{let{size:d=24,strokeWidth:f=2,absoluteStrokeWidth:p=!1,color:g=`currentColor`,className:v=``}=_()??{},y=i??p?Number(r??f)*24/Number(t??d):r??f;return(0,n.createElement)(`svg`,{ref:l,...m,width:t??d??m.width,height:t??d??m.height,stroke:e??g,strokeWidth:y,className:u(`lucide`,v,a),...!o&&!h(c)&&{"aria-hidden":`true`},...c},[...s.map(([e,t])=>(0,n.createElement)(e,t)),...Array.isArray(o)?o:[o]])}),y=(e,t)=>{let r=(0,n.forwardRef)(({className:r,...i},a)=>(0,n.createElement)(v,{ref:a,iconNode:t,className:u(`lucide-${d(p(e))}`,`lucide-${e}`,r),...i}));return r.displayName=p(e),r},b=y(`check`,[[`path`,{d:`M20 6 9 17l-5-5`,key:`1gmf2c`}]]),x=y(`chevron-right`,[[`path`,{d:`m9 18 6-6-6-6`,key:`mthhwq`}]]),ee=y(`circle`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}]]),S=y(`globe`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20`,key:`13o1zl`}],[`path`,{d:`M2 12h20`,key:`9i4pu4`}]]),C=y(`x`,[[`path`,{d:`M18 6 6 18`,key:`1bl5f8`}],[`path`,{d:`m6 6 12 12`,key:`d8bk6v`}]]),w=(0,a.cva)(`inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden`,{variants:{variant:{default:`border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90`,secondary:`border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90`,destructive:`border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60`,outline:`text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground`}},defaultVariants:{variant:`default`}});function T({className:e,variant:n,asChild:a=!1,...o}){return(0,r.jsx)(a?i.Slot:`span`,{"data-slot":`badge`,className:t.t(w({variant:n}),e),...o})}function te({...e}){return(0,r.jsx)(s.Root,{"data-slot":`dialog`,...e})}function ne({...e}){return(0,r.jsx)(s.Trigger,{"data-slot":`dialog-trigger`,...e})}function E({...e}){return(0,r.jsx)(s.Portal,{"data-slot":`dialog-portal`,...e})}function D({...e}){return(0,r.jsx)(s.Close,{"data-slot":`dialog-close`,...e})}function O({className:e,...n}){return(0,r.jsx)(s.Overlay,{"data-slot":`dialog-overlay`,className:t.t(`data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50`,e),...n})}function k({className:e,children:n,showCloseButton:i=!0,...a}){let{t:c}=(0,o.useTranslation)();return(0,r.jsxs)(E,{"data-slot":`dialog-portal`,children:[(0,r.jsx)(O,{}),(0,r.jsxs)(s.Content,{"data-slot":`dialog-content`,className:t.t(`bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg`,e),...a,children:[n,i&&(0,r.jsxs)(s.Close,{"data-slot":`dialog-close`,className:`ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4`,children:[(0,r.jsx)(C,{}),(0,r.jsx)(`span`,{className:`sr-only`,children:c(`dialog.close`,{defaultValue:`Close`})})]})]})]})}function A({className:e,...n}){return(0,r.jsx)(`div`,{"data-slot":`dialog-header`,className:t.t(`flex flex-col gap-2 text-center sm:text-left`,e),...n})}function j({className:e,...n}){return(0,r.jsx)(`div`,{"data-slot":`dialog-footer`,className:t.t(`flex flex-col-reverse gap-2 sm:flex-row sm:justify-end`,e),...n})}function M({className:e,...n}){return(0,r.jsx)(s.Title,{"data-slot":`dialog-title`,className:t.t(`text-lg leading-none font-semibold`,e),...n})}function N({className:e,...n}){return(0,r.jsx)(s.Description,{"data-slot":`dialog-description`,className:t.t(`text-muted-foreground text-sm`,e),...n})}function P({...e}){return(0,r.jsx)(c.Root,{"data-slot":`dropdown-menu`,...e})}function F({...e}){return(0,r.jsx)(c.Portal,{"data-slot":`dropdown-menu-portal`,...e})}function I({...e}){return(0,r.jsx)(c.Trigger,{"data-slot":`dropdown-menu-trigger`,...e})}function L({className:e,sideOffset:n=4,...i}){return(0,r.jsx)(c.Portal,{children:(0,r.jsx)(c.Content,{"data-slot":`dropdown-menu-content`,sideOffset:n,className:t.t(`bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md`,e),...i})})}function R({...e}){return(0,r.jsx)(c.Group,{"data-slot":`dropdown-menu-group`,...e})}function z({className:e,inset:n,variant:i=`default`,...a}){return(0,r.jsx)(c.Item,{"data-slot":`dropdown-menu-item`,"data-inset":n,"data-variant":i,className:t.t(`focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4`,e),...a})}function B({className:e,children:n,checked:i,...a}){return(0,r.jsxs)(c.CheckboxItem,{"data-slot":`dropdown-menu-checkbox-item`,className:t.t(`focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4`,e),checked:i,...a,children:[(0,r.jsx)(`span`,{className:`pointer-events-none absolute left-2 flex size-3.5 items-center justify-center`,children:(0,r.jsx)(c.ItemIndicator,{children:(0,r.jsx)(b,{className:`size-4`})})}),n]})}function V({...e}){return(0,r.jsx)(c.RadioGroup,{"data-slot":`dropdown-menu-radio-group`,...e})}function H({className:e,children:n,...i}){return(0,r.jsxs)(c.RadioItem,{"data-slot":`dropdown-menu-radio-item`,className:t.t(`focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4`,e),...i,children:[(0,r.jsx)(`span`,{className:`pointer-events-none absolute left-2 flex size-3.5 items-center justify-center`,children:(0,r.jsx)(c.ItemIndicator,{children:(0,r.jsx)(ee,{className:`size-2 fill-current`})})}),n]})}function re({className:e,inset:n,...i}){return(0,r.jsx)(c.Label,{"data-slot":`dropdown-menu-label`,"data-inset":n,className:t.t(`px-2 py-1.5 text-sm font-medium data-[inset]:pl-8`,e),...i})}function U({className:e,...n}){return(0,r.jsx)(c.Separator,{"data-slot":`dropdown-menu-separator`,className:t.t(`bg-border -mx-1 my-1 h-px`,e),...n})}function W({className:e,...n}){return(0,r.jsx)(`span`,{"data-slot":`dropdown-menu-shortcut`,className:t.t(`text-muted-foreground ml-auto text-xs tracking-widest`,e),...n})}function G({...e}){return(0,r.jsx)(c.Sub,{"data-slot":`dropdown-menu-sub`,...e})}function K({className:e,inset:n,children:i,...a}){return(0,r.jsxs)(c.SubTrigger,{"data-slot":`dropdown-menu-sub-trigger`,"data-inset":n,className:t.t(`focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4`,e),...a,children:[i,(0,r.jsx)(x,{className:`ml-auto size-4`})]})}function q({className:e,...n}){return(0,r.jsx)(c.SubContent,{"data-slot":`dropdown-menu-sub-content`,className:t.t(`bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg`,e),...n})}var J=(0,a.cva)(`inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all duration-200 cursor-pointer disabled:cursor-not-allowed disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:ring-[3px]`,{variants:{variant:{default:[`bg-gradient-to-r from-[var(--vg-accent-fill-from)] to-[var(--vg-accent-fill-to)] text-white`,`shadow-md shadow-[var(--vg-accent-shadow)]`,`hover:from-[var(--vg-accent-fill-from-hover)] hover:to-[var(--vg-accent-fill-to-hover)]`,`hover:shadow-lg hover:shadow-[var(--vg-accent-shadow-hover)]`,`focus-visible:ring-[var(--vg-accent-ring)]`].join(` `),secondary:[`bg-gradient-to-r from-slate-600 to-slate-700 text-white`,`shadow-md shadow-slate-500/25`,`hover:from-slate-700 hover:to-slate-800`,`hover:shadow-lg hover:shadow-slate-500/30`,`focus-visible:ring-slate-500/50`,`dark:from-slate-500 dark:to-slate-600`,`dark:shadow-slate-500/20`,`dark:hover:from-slate-600 dark:hover:to-slate-700`,`dark:hover:shadow-slate-500/25`,`dark:focus-visible:ring-slate-400/50`].join(` `),destructive:[`bg-gradient-to-r from-red-600 to-rose-600 text-white`,`shadow-md shadow-red-500/25`,`hover:from-red-700 hover:to-rose-700`,`hover:shadow-lg hover:shadow-red-500/30`,`focus-visible:ring-red-500/50`,`dark:from-red-500 dark:to-rose-500`,`dark:shadow-red-500/20`,`dark:hover:from-red-600 dark:hover:to-rose-600`,`dark:hover:shadow-red-500/25`,`dark:focus-visible:ring-red-400/50`].join(` `),success:[`bg-gradient-to-r from-emerald-600 to-teal-600 text-white`,`shadow-md shadow-emerald-500/25`,`hover:from-emerald-700 hover:to-teal-700`,`hover:shadow-lg hover:shadow-emerald-500/30`,`focus-visible:ring-emerald-500/50`,`dark:from-emerald-500 dark:to-teal-500`,`dark:shadow-emerald-500/20`,`dark:hover:from-emerald-600 dark:hover:to-teal-600`,`dark:hover:shadow-emerald-500/25`,`dark:focus-visible:ring-emerald-400/50`].join(` `),outline:[`border-2 border-[var(--vg-accent-fg)] text-[var(--vg-accent-fg)] bg-transparent`,`hover:bg-[var(--vg-accent-fill-from)] hover:text-white`,`shadow-sm hover:shadow-md hover:shadow-[var(--vg-accent-shadow)]`,`focus-visible:ring-[var(--vg-accent-ring)]`].join(` `),ghost:[`bg-transparent text-[var(--vg-accent-fg)]`,`hover:bg-[var(--vg-accent-surface)]`,`focus-visible:ring-[var(--vg-accent-ring)]`].join(` `)},size:{default:`h-11 px-5 py-2 has-[>svg]:px-4`,sm:`h-9 rounded-md gap-1.5 px-4 has-[>svg]:px-3 text-xs`,lg:`h-12 rounded-md px-8 has-[>svg]:px-6 text-base`,icon:`size-11`,"icon-sm":`size-9`,"icon-lg":`size-12`}},defaultVariants:{variant:`default`,size:`default`}});function Y({className:e,variant:n,size:a,asChild:o=!1,loading:s=!1,...c}){let l=t.t(J({variant:n,size:a,className:e}));return(0,r.jsx)(o?i.Slot:`button`,{"data-slot":`gradient-button`,className:l,disabled:s||c.disabled,...c,children:s?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsxs)(`svg`,{className:`size-4 animate-spin`,xmlns:`http://www.w3.org/2000/svg`,fill:`none`,viewBox:`0 0 24 24`,children:[(0,r.jsx)(`circle`,{className:`opacity-25`,cx:`12`,cy:`12`,r:`10`,stroke:`currentColor`,strokeWidth:`4`}),(0,r.jsx)(`path`,{className:`opacity-75`,fill:`currentColor`,d:`M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z`})]}),c.children]}):c.children})}function X({className:e,type:n,...i}){return(0,r.jsx)(`input`,{type:n,"data-slot":`input`,className:t.t(`file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent pl-3 pr-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm`,`focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]`,`aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive`,e),...i})}function ie({className:e,...n}){return(0,r.jsx)(`div`,{"data-slot":`skeleton`,className:t.t(`bg-accent animate-pulse rounded-md`,e),...n})}function Z({delayDuration:e=0,...t}){return(0,r.jsx)(l.Provider,{"data-slot":`tooltip-provider`,delayDuration:e,...t})}function ae({...e}){return(0,r.jsx)(Z,{children:(0,r.jsx)(l.Root,{"data-slot":`tooltip`,...e})})}function oe({...e}){return(0,r.jsx)(l.Trigger,{"data-slot":`tooltip-trigger`,...e})}function se({className:e,sideOffset:n=0,children:i,...a}){return(0,r.jsx)(l.Portal,{children:(0,r.jsxs)(l.Content,{"data-slot":`tooltip-content`,sideOffset:n,className:t.t(`bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance`,e),...a,children:[i,(0,r.jsx)(l.Arrow,{className:`bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]`})]})})}var ce=({text:e,href:n,onClick:i,className:a,prefix:o})=>{if(!e)return null;let s=t.r(e);return(0,r.jsxs)(T,{variant:`outline`,title:e,onClick:()=>n&&i?.(n),className:t.t(`vg-badge-colorful text-xs font-medium px-2 py-0.5 gap-1.5 cursor-pointer transition-all hover:opacity-80`,`dynamic-badge-${e.length}`,a),style:{"--badge-bg":s.light.bg,"--badge-text":s.light.text,"--badge-border":s.light.border,"--badge-dark-bg":s.dark.bg,"--badge-dark-text":s.dark.text,"--badge-dark-border":s.dark.border},children:[o,(0,r.jsx)(`span`,{children:e})]})},le={AR:`SA`,BN:`BD`,CA:`ES`,CS:`CZ`,DA:`DK`,DE:`DE`,EL:`GR`,EN:`US`,EU:`ES`,FA:`IR`,FI:`FI`,FIL:`PH`,FR:`FR`,GA:`IE`,GL:`ES`,GU:`IN`,HE:`IL`,HI:`IN`,HU:`HU`,ID:`ID`,IT:`IT`,JA:`JP`,KN:`IN`,KO:`KR`,MR:`IN`,MSA:`MY`,NL:`NL`,NO:`NO`,PL:`PL`,PT:`BR`,RO:`RO`,RU:`RU`,ES:`ES`,SV:`SE`,TA:`IN`,TH:`TH`,TR:`TR`,UK:`UA`,UR:`PK`,VI:`VN`,ZH:`CN`},Q={UK:`GB`};function $(e){if(!e)return``;let{language:n,region:r}=t.i(e);if(r)return(Q[r]||r).toLowerCase();let i=le[n];return i?i.toLowerCase():(Q[n]||n).toLowerCase()}var ue=({locale:e,className:i})=>{let a=$(e),[o,s]=(0,n.useState)(null),c=o===a;return(0,r.jsxs)(T,{variant:`secondary`,className:t.t(`font-mono gap-2 py-1 pl-1 pr-2 w-fit`,i),children:[!c&&a?(0,r.jsx)(`img`,{src:`https://flagcdn.com/w40/${a}.png`,alt:a,onError:()=>s(a),className:`w-5 h-3.5 object-cover rounded-sm shadow-sm`}):(0,r.jsx)(S,{className:`w-3.5 h-3.5 text-muted-foreground`}),(0,r.jsx)(`span`,{className:`text-xs font-bold uppercase tracking-tight leading-none`,children:e})]})};Object.defineProperty(exports,"A",{enumerable:!0,get:function(){return N}}),Object.defineProperty(exports,"B",{enumerable:!0,get:function(){return x}}),Object.defineProperty(exports,"C",{enumerable:!0,get:function(){return G}}),Object.defineProperty(exports,"D",{enumerable:!0,get:function(){return te}}),Object.defineProperty(exports,"E",{enumerable:!0,get:function(){return I}}),Object.defineProperty(exports,"F",{enumerable:!0,get:function(){return M}}),Object.defineProperty(exports,"H",{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,"I",{enumerable:!0,get:function(){return ne}}),Object.defineProperty(exports,"L",{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,"M",{enumerable:!0,get:function(){return A}}),Object.defineProperty(exports,"N",{enumerable:!0,get:function(){return O}}),Object.defineProperty(exports,"O",{enumerable:!0,get:function(){return D}}),Object.defineProperty(exports,"P",{enumerable:!0,get:function(){return E}}),Object.defineProperty(exports,"R",{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,"S",{enumerable:!0,get:function(){return W}}),Object.defineProperty(exports,"T",{enumerable:!0,get:function(){return K}}),Object.defineProperty(exports,"V",{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,"_",{enumerable:!0,get:function(){return re}}),Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return se}}),Object.defineProperty(exports,"b",{enumerable:!0,get:function(){return H}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return ie}}),Object.defineProperty(exports,"d",{enumerable:!0,get:function(){return J}}),Object.defineProperty(exports,"f",{enumerable:!0,get:function(){return P}}),Object.defineProperty(exports,"g",{enumerable:!0,get:function(){return z}}),Object.defineProperty(exports,"h",{enumerable:!0,get:function(){return R}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return ae}}),Object.defineProperty(exports,"j",{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,"k",{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,"l",{enumerable:!0,get:function(){return X}}),Object.defineProperty(exports,"m",{enumerable:!0,get:function(){return L}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return $}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return Z}}),Object.defineProperty(exports,"p",{enumerable:!0,get:function(){return B}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return ce}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return oe}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return ue}}),Object.defineProperty(exports,"u",{enumerable:!0,get:function(){return Y}}),Object.defineProperty(exports,"v",{enumerable:!0,get:function(){return F}}),Object.defineProperty(exports,"w",{enumerable:!0,get:function(){return q}}),Object.defineProperty(exports,"x",{enumerable:!0,get:function(){return U}}),Object.defineProperty(exports,"y",{enumerable:!0,get:function(){return V}}),Object.defineProperty(exports,"z",{enumerable:!0,get:function(){return C}});