@viglet/viglet-design-system 2026.3.9 → 2026.3.11

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 (259) hide show
  1. package/README.md +528 -44
  2. package/consumers.json +186 -0
  3. package/dist/badge-locale-CJkHDlPg.cjs +1 -0
  4. package/dist/badge-locale-DxDp8Q3S.js +397 -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 +56 -9
  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 +14 -1
  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-nav.d.ts +10 -2
  26. package/dist/bento/bento-nav.d.ts.map +1 -1
  27. package/dist/bento/bento-palette-trigger.d.ts +18 -0
  28. package/dist/bento/bento-palette-trigger.d.ts.map +1 -0
  29. package/dist/bento/bento-section-chrome.d.ts +19 -39
  30. package/dist/bento/bento-section-chrome.d.ts.map +1 -1
  31. package/dist/bento/bento-shell-shortcuts.d.ts +67 -0
  32. package/dist/bento/bento-shell-shortcuts.d.ts.map +1 -0
  33. package/dist/bento/bento-shell.d.ts +53 -0
  34. package/dist/bento/bento-shell.d.ts.map +1 -0
  35. package/dist/bento/bento-shortcuts-dialog.d.ts +9 -7
  36. package/dist/bento/bento-shortcuts-dialog.d.ts.map +1 -1
  37. package/dist/bento/bento-status-marker.d.ts +5 -2
  38. package/dist/bento/bento-status-marker.d.ts.map +1 -1
  39. package/dist/bento/bento-tile.d.ts.map +1 -1
  40. package/dist/bento/bento-user-menu.d.ts +9 -8
  41. package/dist/bento/bento-user-menu.d.ts.map +1 -1
  42. package/dist/bento/bento-version-rail.d.ts +31 -0
  43. package/dist/bento/bento-version-rail.d.ts.map +1 -0
  44. package/dist/bento/index.d.ts +15 -6
  45. package/dist/bento/index.d.ts.map +1 -1
  46. package/dist/bento.cjs +5 -1
  47. package/dist/bento.css +70 -4
  48. package/dist/bento.es.js +1778 -701
  49. package/dist/catalogue.json +5283 -0
  50. package/dist/components/app-footer.d.ts +7 -0
  51. package/dist/components/app-footer.d.ts.map +1 -1
  52. package/dist/components/app-switcher.d.ts.map +1 -1
  53. package/dist/components/backend-status.d.ts +19 -11
  54. package/dist/components/backend-status.d.ts.map +1 -1
  55. package/dist/components/badge-colorful.d.ts +13 -0
  56. package/dist/components/badge-colorful.d.ts.map +1 -1
  57. package/dist/components/badge-locale.d.ts +7 -0
  58. package/dist/components/badge-locale.d.ts.map +1 -1
  59. package/dist/components/error-boundary.d.ts +15 -13
  60. package/dist/components/error-boundary.d.ts.map +1 -1
  61. package/dist/components/language-select.d.ts +8 -0
  62. package/dist/components/language-select.d.ts.map +1 -1
  63. package/dist/components/login/login.d.ts +27 -0
  64. package/dist/components/login/login.d.ts.map +1 -1
  65. package/dist/components/mode-toggle.d.ts +9 -0
  66. package/dist/components/mode-toggle.d.ts.map +1 -1
  67. package/dist/components/nav-secondary.d.ts +7 -0
  68. package/dist/components/nav-secondary.d.ts.map +1 -1
  69. package/dist/components/router/dialog.delete.d.ts +8 -0
  70. package/dist/components/router/dialog.delete.d.ts.map +1 -1
  71. package/dist/components/router/gradient-button-link.d.ts +6 -0
  72. package/dist/components/router/gradient-button-link.d.ts.map +1 -1
  73. package/dist/components/router/index.d.ts +0 -11
  74. package/dist/components/router/index.d.ts.map +1 -1
  75. package/dist/components/router/loading-provider.d.ts +4 -0
  76. package/dist/components/router/loading-provider.d.ts.map +1 -1
  77. package/dist/components/startup-first/startup-first.d.ts +29 -0
  78. package/dist/components/startup-first/startup-first.d.ts.map +1 -1
  79. package/dist/components/ui/accordion.d.ts +17 -0
  80. package/dist/components/ui/accordion.d.ts.map +1 -1
  81. package/dist/components/ui/avatar.d.ts +14 -0
  82. package/dist/components/ui/avatar.d.ts.map +1 -1
  83. package/dist/components/ui/badge.d.ts +6 -0
  84. package/dist/components/ui/badge.d.ts.map +1 -1
  85. package/dist/components/ui/breadcrumb.d.ts +25 -0
  86. package/dist/components/ui/breadcrumb.d.ts.map +1 -1
  87. package/dist/components/ui/button.d.ts +15 -1
  88. package/dist/components/ui/button.d.ts.map +1 -1
  89. package/dist/components/ui/card.d.ts +27 -0
  90. package/dist/components/ui/card.d.ts.map +1 -1
  91. package/dist/components/ui/checkbox.d.ts +4 -0
  92. package/dist/components/ui/checkbox.d.ts.map +1 -1
  93. package/dist/components/ui/dialog.d.ts +36 -0
  94. package/dist/components/ui/dialog.d.ts.map +1 -1
  95. package/dist/components/ui/drawer.d.ts +35 -0
  96. package/dist/components/ui/drawer.d.ts.map +1 -1
  97. package/dist/components/ui/dropdown-menu.d.ts +53 -0
  98. package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
  99. package/dist/components/ui/floating-formulas-bg.d.ts +8 -0
  100. package/dist/components/ui/floating-formulas-bg.d.ts.map +1 -1
  101. package/dist/components/ui/form-actions.d.ts +7 -0
  102. package/dist/components/ui/form-actions.d.ts.map +1 -1
  103. package/dist/components/ui/form-item-two-columns.d.ts +7 -0
  104. package/dist/components/ui/form-item-two-columns.d.ts.map +1 -1
  105. package/dist/components/ui/form.d.ts +25 -0
  106. package/dist/components/ui/form.d.ts.map +1 -1
  107. package/dist/components/ui/glass-card.d.ts +18 -14
  108. package/dist/components/ui/glass-card.d.ts.map +1 -1
  109. package/dist/components/ui/gradient-button.d.ts +15 -1
  110. package/dist/components/ui/gradient-button.d.ts.map +1 -1
  111. package/dist/components/ui/gradient-switch.d.ts +6 -0
  112. package/dist/components/ui/gradient-switch.d.ts.map +1 -1
  113. package/dist/components/ui/hover-card.d.ts +14 -0
  114. package/dist/components/ui/hover-card.d.ts.map +1 -1
  115. package/dist/components/ui/icon-picker-dialog.d.ts.map +1 -1
  116. package/dist/components/ui/index.d.ts +6 -4
  117. package/dist/components/ui/index.d.ts.map +1 -1
  118. package/dist/components/ui/input.d.ts +6 -0
  119. package/dist/components/ui/input.d.ts.map +1 -1
  120. package/dist/components/ui/label.d.ts +4 -0
  121. package/dist/components/ui/label.d.ts.map +1 -1
  122. package/dist/components/ui/navigation-menu.d.ts +41 -0
  123. package/dist/components/ui/navigation-menu.d.ts.map +1 -1
  124. package/dist/components/ui/pagination.d.ts +25 -0
  125. package/dist/components/ui/pagination.d.ts.map +1 -1
  126. package/dist/components/ui/popover.d.ts +24 -0
  127. package/dist/components/ui/popover.d.ts.map +1 -1
  128. package/dist/components/ui/progress.d.ts +4 -0
  129. package/dist/components/ui/progress.d.ts.map +1 -1
  130. package/dist/components/ui/pulse-ring.d.ts +9 -11
  131. package/dist/components/ui/pulse-ring.d.ts.map +1 -1
  132. package/dist/components/ui/resizable.d.ts +11 -0
  133. package/dist/components/ui/resizable.d.ts.map +1 -1
  134. package/dist/components/ui/section-card.d.ts +9 -0
  135. package/dist/components/ui/section-card.d.ts.map +1 -1
  136. package/dist/components/ui/select.d.ts +36 -0
  137. package/dist/components/ui/select.d.ts.map +1 -1
  138. package/dist/components/ui/separator.d.ts +4 -0
  139. package/dist/components/ui/separator.d.ts.map +1 -1
  140. package/dist/components/ui/sheet.d.ts +29 -0
  141. package/dist/components/ui/sheet.d.ts.map +1 -1
  142. package/dist/components/ui/sidebar.d.ts +83 -0
  143. package/dist/components/ui/sidebar.d.ts.map +1 -1
  144. package/dist/components/ui/skeleton.d.ts +4 -0
  145. package/dist/components/ui/skeleton.d.ts.map +1 -1
  146. package/dist/components/ui/slider.d.ts +4 -0
  147. package/dist/components/ui/slider.d.ts.map +1 -1
  148. package/dist/components/ui/sonner.d.ts +16 -0
  149. package/dist/components/ui/sonner.d.ts.map +1 -1
  150. package/dist/components/ui/stepper.d.ts +8 -0
  151. package/dist/components/ui/stepper.d.ts.map +1 -1
  152. package/dist/components/ui/sticky-save-bar.d.ts +7 -0
  153. package/dist/components/ui/sticky-save-bar.d.ts.map +1 -1
  154. package/dist/components/ui/switch.d.ts +4 -0
  155. package/dist/components/ui/switch.d.ts.map +1 -1
  156. package/dist/components/ui/table.d.ts +31 -0
  157. package/dist/components/ui/table.d.ts.map +1 -1
  158. package/dist/components/ui/tabs.d.ts +16 -0
  159. package/dist/components/ui/tabs.d.ts.map +1 -1
  160. package/dist/components/ui/textarea.d.ts +4 -0
  161. package/dist/components/ui/textarea.d.ts.map +1 -1
  162. package/dist/components/ui/toggle-group.d.ts +7 -0
  163. package/dist/components/ui/toggle-group.d.ts.map +1 -1
  164. package/dist/components/ui/toggle.d.ts +4 -0
  165. package/dist/components/ui/toggle.d.ts.map +1 -1
  166. package/dist/components/ui/tooltip.d.ts +18 -0
  167. package/dist/components/ui/tooltip.d.ts.map +1 -1
  168. package/dist/components/ui/user-avatar.d.ts +2 -1
  169. package/dist/components/ui/user-avatar.d.ts.map +1 -1
  170. package/dist/components/ui/viglet-assistant.d.ts +103 -0
  171. package/dist/components/ui/viglet-assistant.d.ts.map +1 -0
  172. package/dist/components/ui/viglet-avatar.d.ts +44 -0
  173. package/dist/components/ui/viglet-avatar.d.ts.map +1 -0
  174. package/dist/contexts/breadcrumb.context.d.ts +7 -0
  175. package/dist/contexts/breadcrumb.context.d.ts.map +1 -1
  176. package/dist/contexts/user.context.d.ts +8 -0
  177. package/dist/contexts/user.context.d.ts.map +1 -1
  178. package/dist/exports.json +359 -30
  179. package/dist/floating-formulas-bg.cjs +1 -1
  180. package/dist/floating-formulas-bg.es.js +56 -55
  181. package/dist/hooks/index.d.ts +1 -0
  182. package/dist/hooks/index.d.ts.map +1 -1
  183. package/dist/hooks/use-assistant-notifications.d.ts +15 -0
  184. package/dist/hooks/use-assistant-notifications.d.ts.map +1 -0
  185. package/dist/i18n/index.d.ts +168 -0
  186. package/dist/i18n/index.d.ts.map +1 -1
  187. package/dist/i18n/locales/en/assistant.json.d.ts +25 -0
  188. package/dist/i18n/locales/en/bento.json.d.ts +48 -2
  189. package/dist/i18n/locales/en/common.json.d.ts +6 -1
  190. package/dist/i18n/locales/en/language.json.d.ts +8 -0
  191. package/dist/i18n/locales/pt/assistant.json.d.ts +25 -0
  192. package/dist/i18n/locales/pt/bento.json.d.ts +48 -2
  193. package/dist/i18n/locales/pt/common.json.d.ts +6 -1
  194. package/dist/i18n/locales/pt/language.json.d.ts +8 -0
  195. package/dist/i18n.cjs +1 -1
  196. package/dist/i18n.es.js +572 -2
  197. package/dist/index.cjs +1 -1
  198. package/dist/index.d.ts +0 -1
  199. package/dist/index.d.ts.map +1 -1
  200. package/dist/index.es.js +2280 -733
  201. package/dist/lib/busy.d.ts +26 -0
  202. package/dist/lib/busy.d.ts.map +1 -0
  203. package/dist/lib/corner-slot.d.ts +15 -0
  204. package/dist/lib/corner-slot.d.ts.map +1 -0
  205. package/dist/{loading-provider-BWWhhfEn.js → loading-provider-ClDkFPuv.js} +84 -81
  206. package/dist/loading-provider-DGuCfsl9.cjs +1 -0
  207. package/dist/preset.css +7 -10
  208. package/dist/router.cjs +1 -1
  209. package/dist/router.es.js +2 -776
  210. package/dist/user.context-Cr3GAQ_r.js +762 -0
  211. package/dist/user.context-hbdvmXOn.cjs +1 -0
  212. package/dist/utils-Fk5jmzzb.js +41 -0
  213. package/dist/utils-Oagwe_oa.cjs +1 -0
  214. package/dist/viglet-design-system.css +1 -1
  215. package/dist/vite/boot-loader.d.ts +33 -2
  216. package/dist/vite/boot-loader.d.ts.map +1 -1
  217. package/dist/vite.cjs +16 -5
  218. package/dist/vite.es.js +27 -14
  219. package/docs/BENTO-AUTHORING.md +81 -11
  220. package/docs/reference/grounds.dc.html +9 -9
  221. package/package.json +16 -4
  222. package/scripts/consumer-entries.mjs +209 -0
  223. package/scripts/lib/contrast.mjs +301 -0
  224. package/scripts/mcp.mjs +300 -0
  225. package/scripts/page-lint.mjs +428 -0
  226. package/scripts/page-reference.mjs +54 -10
  227. package/dist/badge-locale-CoEEv7s5.js +0 -581
  228. package/dist/badge-locale-hQyWhAKe.cjs +0 -7
  229. package/dist/breadcrumb.context-KQut7j8U.js +0 -917
  230. package/dist/breadcrumb.context-Vwc3lAlf.cjs +0 -1
  231. package/dist/components/router/blank-slate.d.ts +0 -21
  232. package/dist/components/router/blank-slate.d.ts.map +0 -1
  233. package/dist/components/router/grid.list.d.ts +0 -39
  234. package/dist/components/router/grid.list.d.ts.map +0 -1
  235. package/dist/components/router/internal.sidebar.d.ts +0 -61
  236. package/dist/components/router/internal.sidebar.d.ts.map +0 -1
  237. package/dist/components/router/nav-main.d.ts +0 -25
  238. package/dist/components/router/nav-main.d.ts.map +0 -1
  239. package/dist/components/router/nav-user.d.ts +0 -24
  240. package/dist/components/router/nav-user.d.ts.map +0 -1
  241. package/dist/components/router/page-content.d.ts +0 -12
  242. package/dist/components/router/page-content.d.ts.map +0 -1
  243. package/dist/components/router/page-header.d.ts +0 -23
  244. package/dist/components/router/page-header.d.ts.map +0 -1
  245. package/dist/components/router/page.d.ts +0 -22
  246. package/dist/components/router/page.d.ts.map +0 -1
  247. package/dist/components/router/sticky.page.header.d.ts +0 -85
  248. package/dist/components/router/sticky.page.header.d.ts.map +0 -1
  249. package/dist/components/router/sub.page.d.ts +0 -50
  250. package/dist/components/router/sub.page.d.ts.map +0 -1
  251. package/dist/components/router/sub.page.header.d.ts +0 -37
  252. package/dist/components/router/sub.page.header.d.ts.map +0 -1
  253. package/dist/i18n-DD3rQOWi.js +0 -426
  254. package/dist/i18n-DrBq-qJJ.cjs +0 -1
  255. package/dist/lib/react-markers.d.ts +0 -17
  256. package/dist/lib/react-markers.d.ts.map +0 -1
  257. package/dist/loading-provider-CmeMqLGk.cjs +0 -1
  258. package/dist/user.context-B9vIBzY5.cjs +0 -1
  259. package/dist/user.context-D1PxP4D4.js +0 -512
@@ -0,0 +1,300 @@
1
+ #!/usr/bin/env node
2
+ // VDS136 — the component catalogue, served to an agent over MCP (stdio).
3
+ //
4
+ // viglet-ds-mcp # serve the installed package's catalogue
5
+ // viglet-ds-mcp --catalogue <file> # serve another catalogue.json
6
+ // viglet-ds-mcp --package-root <dir> # read dist/ and docs/ from another copy
7
+ //
8
+ // Two tools, because a tool list is paid on every turn of every session:
9
+ //
10
+ // find_component which components do a job, ranked, one line each
11
+ // read_component one component's purpose, props, the rules that govern it
12
+ // and an import to start from
13
+ //
14
+ // The long text is a resource, read when a session asks for it: the authoring
15
+ // contract, the boundary, and the token reference. Everything is read from the
16
+ // installed package, so the answers describe the release the product has, and
17
+ // nothing here is written by hand: the catalogue is generated at build time from
18
+ // the declarations the product compiles against.
19
+ //
20
+ // The protocol is JSON-RPC 2.0, one message per line, with no SDK: initialize,
21
+ // ping, tools/list, tools/call, resources/list and resources/read are the whole
22
+ // of what a client asks this server.
23
+
24
+ import { existsSync, readFileSync } from "node:fs"
25
+ import { dirname, join, resolve } from "node:path"
26
+ import { createInterface } from "node:readline"
27
+ import { fileURLToPath } from "node:url"
28
+
29
+ const args = process.argv.slice(2)
30
+ function option(flag) {
31
+ const at = args.indexOf(flag)
32
+ return at === -1 ? null : (args[at + 1] ?? null)
33
+ }
34
+
35
+ const packageRoot = resolve(option("--package-root") ?? join(dirname(fileURLToPath(import.meta.url)), ".."))
36
+ const cataloguePath = resolve(option("--catalogue") ?? join(packageRoot, "dist", "catalogue.json"))
37
+
38
+ const PROTOCOL = "2025-06-18"
39
+ const FIND_LIMIT = 5
40
+
41
+ // ---------------------------------------------------------------------------
42
+ // The surface. Descriptions are short on purpose: each word here is in every
43
+ // turn's context, and the per-call answers carry the detail.
44
+
45
+ export const TOOLS = [
46
+ {
47
+ name: "find_component",
48
+ description:
49
+ "Find Viglet Design System components for a job, e.g. 'frosted container with no heading' or 'confirm a delete'. Returns ranked names, imports and one-line purposes.",
50
+ inputSchema: {
51
+ type: "object",
52
+ properties: {
53
+ job: { type: "string", description: "What the component should do, in plain words." },
54
+ limit: { type: "integer", minimum: 1, maximum: 10, description: "How many to return (default 5)." },
55
+ },
56
+ required: ["job"],
57
+ },
58
+ },
59
+ {
60
+ name: "read_component",
61
+ description:
62
+ "Read one component: what it is for, its props, the contract rules that govern it, and an import to start from.",
63
+ inputSchema: {
64
+ type: "object",
65
+ properties: { name: { type: "string", description: "The exported name, e.g. BentoPanel." } },
66
+ required: ["name"],
67
+ },
68
+ },
69
+ ]
70
+
71
+ export const RESOURCES = [
72
+ {
73
+ uri: "viglet-ds://authoring",
74
+ name: "authoring",
75
+ title: "Authoring a bento page",
76
+ description: "The page contract: the shell and who owns each region, page shapes, colour, i18n, accessibility.",
77
+ mimeType: "text/markdown",
78
+ file: "docs/BENTO-AUTHORING.md",
79
+ },
80
+ {
81
+ uri: "viglet-ds://boundary",
82
+ name: "boundary",
83
+ title: "What the bento layer holds",
84
+ description: "Which components are the shared layer and which stay in a product, and why.",
85
+ mimeType: "text/markdown",
86
+ file: "docs/BENTO-BOUNDARY.md",
87
+ },
88
+ {
89
+ uri: "viglet-ds://tokens",
90
+ name: "tokens",
91
+ title: "Design tokens",
92
+ description: "Every --vg-* custom property the preset declares, on the light and the dark ground.",
93
+ mimeType: "text/markdown",
94
+ file: "dist/preset.css",
95
+ },
96
+ ]
97
+
98
+ // ---------------------------------------------------------------------------
99
+ // The answers.
100
+
101
+ const STOP = new Set(["a", "an", "the", "to", "of", "for", "with", "and", "or", "in", "on", "that", "which", "is", "it", "my", "i", "want", "need", "component"])
102
+
103
+ /** `BentoPanel` -> ["bento", "panel"], `aria-label` -> ["aria", "label"]. */
104
+ const words = (text) =>
105
+ (text ?? "")
106
+ .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
107
+ .toLowerCase()
108
+ .split(/[^a-z0-9]+/)
109
+ .filter((w) => w.length > 1 && !STOP.has(w))
110
+
111
+ /** A crude stem, so "deleting" and "delete" meet at "delet". */
112
+ const stem = (word) => (word.length > 4 ? word.replace(/(ing|ion|ed|es|s|e)$/, "") : word)
113
+
114
+ /** A word counts when it starts a word of the text, so "confirm" finds "confirmation". */
115
+ function hits(query, text) {
116
+ const haystack = words(text).map(stem)
117
+ return query.filter((q) => haystack.some((w) => w.startsWith(q))).length
118
+ }
119
+
120
+ export function findComponents(catalogue, job, limit = FIND_LIMIT) {
121
+ const query = [...new Set(words(job).map(stem))]
122
+ if (query.length === 0) return []
123
+ return catalogue.components
124
+ .map((component) => ({
125
+ component,
126
+ score:
127
+ hits(query, component.name) * 5 +
128
+ hits(query, component.summary) * 3 +
129
+ hits(query, component.description) +
130
+ hits(query, component.props.map((p) => p.name).join(" ")) +
131
+ hits(query, component.rules.join(" ")) -
132
+ (component.deprecated ? 4 : 0),
133
+ }))
134
+ .filter((ranked) => ranked.score > 0)
135
+ .sort((a, b) => b.score - a.score || a.component.name.localeCompare(b.component.name))
136
+ .slice(0, Math.max(1, Math.min(10, limit)))
137
+ .map(({ component }) => component)
138
+ }
139
+
140
+ export function renderFound(found, job) {
141
+ if (found.length === 0) {
142
+ return `No component matches "${job}". Try other words for the job, or read viglet-ds://boundary for what the layer holds.`
143
+ }
144
+ return found
145
+ .map((c) => {
146
+ const purpose = c.summary || "(no description in its declaration)"
147
+ const deprecated = c.deprecated ? ` DEPRECATED: ${c.deprecated}` : ""
148
+ return `${c.name} (${c.specifier}): ${purpose}${deprecated}`
149
+ })
150
+ .join("\n")
151
+ }
152
+
153
+ /** An import and the smallest element that type-checks: the required props, as placeholders. */
154
+ function starter(component) {
155
+ if (component.example && component.example !== "yes") return component.example
156
+ const required = component.props.filter((p) => !p.optional && p.name !== "children")
157
+ const attributes = required.map((p) => ` ${p.name}={…}`).join("")
158
+ return `import { ${component.name} } from "${component.specifier}"\n\n<${component.name}${attributes} />`
159
+ }
160
+
161
+ export function renderComponent(component) {
162
+ const lines = [`# ${component.name}`, `import from ${component.specifier}`, ""]
163
+ if (component.deprecated) lines.push(`DEPRECATED: ${component.deprecated}`, "")
164
+ lines.push(component.description || "No description in its declaration.", "")
165
+
166
+ lines.push("## Props")
167
+ if (component.props.length === 0) lines.push("none of its own")
168
+ for (const p of component.props) {
169
+ lines.push(`- ${p.name}${p.optional ? "?" : ""}: ${p.type}${p.summary ? ` — ${p.summary}` : ""}`)
170
+ }
171
+ if (component.inheritedProps > 0) {
172
+ lines.push(`- and ${component.inheritedProps} attributes of the element it renders, passed through`)
173
+ }
174
+
175
+ lines.push("", "## Governed by")
176
+ lines.push(...(component.rules.length > 0 ? component.rules.map((r) => `- ${r}`) : ["no section of the contract names it"]))
177
+
178
+ lines.push("", "## Start from", "```tsx", starter(component), "```")
179
+ return lines.join("\n")
180
+ }
181
+
182
+ /** The preset's tokens, by ground, as `--name: value` lines. */
183
+ export function renderTokens(css) {
184
+ const text = css.replace(/\/\*[\s\S]*?\*\//g, "")
185
+ const block = (selector) => {
186
+ const at = text.search(selector)
187
+ if (at === -1) return []
188
+ const open = text.indexOf("{", at)
189
+ const close = text.indexOf("\n}", open)
190
+ return [...text.slice(open + 1, close).matchAll(/(--vg-[\w-]+)\s*:\s*([^;]+);/g)].map(
191
+ ([, name, value]) => `${name}: ${value.replace(/\s+/g, " ").trim()}`,
192
+ )
193
+ }
194
+ return [
195
+ "# Design tokens",
196
+ "",
197
+ "Claim a token at `:root` in the product's stylesheet. `--primary` is claimed through the `--vg-primary-*-base` inputs, never by setting `--vg-primary` (see viglet-ds://authoring).",
198
+ "",
199
+ "## Light ground (:root)",
200
+ ...block(/^:root\s*\{/m),
201
+ "",
202
+ "## Dark ground (.dark)",
203
+ ...block(/^\.dark,?/m),
204
+ ].join("\n")
205
+ }
206
+
207
+ // ---------------------------------------------------------------------------
208
+ // The server.
209
+
210
+ function loadCatalogue() {
211
+ if (!existsSync(cataloguePath)) return null
212
+ return JSON.parse(readFileSync(cataloguePath, "utf8"))
213
+ }
214
+
215
+ function textResult(text, isError = false) {
216
+ return { content: [{ type: "text", text }], ...(isError ? { isError: true } : {}) }
217
+ }
218
+
219
+ export function handle(message, catalogue) {
220
+ const { method, params = {} } = message
221
+ switch (method) {
222
+ case "initialize":
223
+ return {
224
+ protocolVersion: params.protocolVersion ?? PROTOCOL,
225
+ capabilities: { tools: { listChanged: false }, resources: { listChanged: false } },
226
+ serverInfo: { name: "viglet-ds", version: catalogue?.version ?? "unknown" },
227
+ }
228
+ case "ping":
229
+ return {}
230
+ case "tools/list":
231
+ return { tools: TOOLS }
232
+ case "resources/list":
233
+ return { resources: RESOURCES.map(({ file: _file, ...resource }) => resource) }
234
+ case "resources/read": {
235
+ const resource = RESOURCES.find((r) => r.uri === params.uri)
236
+ if (!resource) throw rpcError(-32602, `no resource ${params.uri}`)
237
+ const path = join(packageRoot, resource.file)
238
+ if (!existsSync(path)) throw rpcError(-32002, `${resource.file} is not in this copy of the package`)
239
+ const raw = readFileSync(path, "utf8")
240
+ const text = resource.name === "tokens" ? renderTokens(raw) : raw
241
+ return { contents: [{ uri: resource.uri, mimeType: resource.mimeType, text }] }
242
+ }
243
+ case "tools/call": {
244
+ if (!catalogue) {
245
+ return textResult(`No catalogue at ${cataloguePath}: this release of the package does not ship one.`, true)
246
+ }
247
+ const input = params.arguments ?? {}
248
+ if (params.name === "find_component") {
249
+ if (typeof input.job !== "string" || !input.job.trim()) return textResult("find_component needs a job.", true)
250
+ return textResult(renderFound(findComponents(catalogue, input.job, input.limit), input.job))
251
+ }
252
+ if (params.name === "read_component") {
253
+ const wanted = String(input.name ?? "").trim()
254
+ const component =
255
+ catalogue.components.find((c) => c.name === wanted) ??
256
+ catalogue.components.find((c) => c.name.toLowerCase() === wanted.toLowerCase())
257
+ if (!component) {
258
+ const near = findComponents(catalogue, wanted, 3).map((c) => c.name)
259
+ return textResult(`No component named "${wanted}".${near.length ? ` Did you mean ${near.join(", ")}?` : ""}`, true)
260
+ }
261
+ return textResult(renderComponent(component))
262
+ }
263
+ throw rpcError(-32602, `no tool ${params.name}`)
264
+ }
265
+ default:
266
+ throw rpcError(-32601, `method not found: ${method}`)
267
+ }
268
+ }
269
+
270
+ function rpcError(code, message) {
271
+ return Object.assign(new Error(message), { rpc: { code, message } })
272
+ }
273
+
274
+ function serve() {
275
+ const catalogue = loadCatalogue()
276
+ const reply = (payload) => process.stdout.write(`${JSON.stringify({ jsonrpc: "2.0", ...payload })}\n`)
277
+
278
+ createInterface({ input: process.stdin }).on("line", (line) => {
279
+ if (!line.trim()) return
280
+ let message
281
+ try {
282
+ // A client on Windows can open the stream with a byte-order mark.
283
+ message = JSON.parse(line.startsWith("\uFEFF") ? line.slice(1) : line)
284
+ } catch {
285
+ reply({ id: null, error: { code: -32700, message: "parse error" } })
286
+ return
287
+ }
288
+ // A notification has no id and wants no answer.
289
+ if (message.id === undefined || message.id === null) return
290
+ try {
291
+ reply({ id: message.id, result: handle(message, catalogue) })
292
+ } catch (error) {
293
+ reply({ id: message.id, error: error.rpc ?? { code: -32603, message: String(error.message ?? error) } })
294
+ }
295
+ })
296
+ }
297
+
298
+ if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
299
+ serve()
300
+ }