banhaten 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. package/README.md +93 -328
  2. package/banhaten.config.example.json +1 -1
  3. package/docs/design-system/README.md +11 -0
  4. package/docs/design-system/appearance-presets.md +184 -0
  5. package/docs/design-system/appearances/default.md +94 -0
  6. package/docs/design-system/appearances/rounded.md +95 -0
  7. package/docs/design-system/appearances/sharp.md +95 -0
  8. package/docs/design-system/component-showcase-consistency-report.md +217 -0
  9. package/docs/design-system/component-token-consistency-audit.md +163 -0
  10. package/docs/design-system/components/README.md +74 -0
  11. package/docs/design-system/components/accordion.md +51 -0
  12. package/docs/design-system/components/activity-feed.md +92 -0
  13. package/docs/design-system/components/alert-dialog.md +70 -0
  14. package/docs/design-system/components/alert.md +79 -0
  15. package/docs/design-system/components/aspect-ratio.md +44 -0
  16. package/docs/design-system/components/attribute.md +87 -0
  17. package/docs/design-system/components/autocomplete.md +74 -0
  18. package/docs/design-system/components/avatar.md +52 -0
  19. package/docs/design-system/components/badge.md +53 -0
  20. package/docs/design-system/components/banner.md +85 -0
  21. package/docs/design-system/components/breadcrumbs.md +174 -0
  22. package/docs/design-system/components/button-group.md +83 -0
  23. package/docs/design-system/components/button.md +77 -0
  24. package/docs/design-system/components/card.md +78 -0
  25. package/docs/design-system/components/carousel.md +44 -0
  26. package/docs/design-system/components/catalog-components.md +45 -0
  27. package/docs/design-system/components/chart.md +43 -0
  28. package/docs/design-system/components/checkbox.md +52 -0
  29. package/docs/design-system/components/collapsible.md +48 -0
  30. package/docs/design-system/components/command-bar.md +57 -0
  31. package/docs/design-system/components/command.md +60 -0
  32. package/docs/design-system/components/context-menu.md +44 -0
  33. package/docs/design-system/components/date-picker.md +77 -0
  34. package/docs/design-system/components/divider.md +101 -0
  35. package/docs/design-system/components/empty-state.md +55 -0
  36. package/docs/design-system/components/field.md +69 -0
  37. package/docs/design-system/components/file-upload.md +185 -0
  38. package/docs/design-system/components/hover-card.md +46 -0
  39. package/docs/design-system/components/icons.md +48 -0
  40. package/docs/design-system/components/input-group.md +56 -0
  41. package/docs/design-system/components/input-otp.md +55 -0
  42. package/docs/design-system/components/input.md +48 -0
  43. package/docs/design-system/components/kbd.md +44 -0
  44. package/docs/design-system/components/label.md +48 -0
  45. package/docs/design-system/components/menu.md +59 -0
  46. package/docs/design-system/components/menubar.md +45 -0
  47. package/docs/design-system/components/modal.md +98 -0
  48. package/docs/design-system/components/native-select.md +52 -0
  49. package/docs/design-system/components/navigation-menu.md +48 -0
  50. package/docs/design-system/components/onboarding-step-list-item.md +80 -0
  51. package/docs/design-system/components/page-header.md +84 -0
  52. package/docs/design-system/components/pagination.md +49 -0
  53. package/docs/design-system/components/popover.md +58 -0
  54. package/docs/design-system/components/progress-slider.md +48 -0
  55. package/docs/design-system/components/progress.md +75 -0
  56. package/docs/design-system/components/radio-card.md +49 -0
  57. package/docs/design-system/components/radio-group.md +55 -0
  58. package/docs/design-system/components/resizable.md +42 -0
  59. package/docs/design-system/components/scroll-area.md +45 -0
  60. package/docs/design-system/components/select.md +50 -0
  61. package/docs/design-system/components/sheet.md +65 -0
  62. package/docs/design-system/components/sidebar.md +68 -0
  63. package/docs/design-system/components/skeleton.md +73 -0
  64. package/docs/design-system/components/slideout.md +63 -0
  65. package/docs/design-system/components/slider.md +61 -0
  66. package/docs/design-system/components/social-button.md +47 -0
  67. package/docs/design-system/components/spinner.md +61 -0
  68. package/docs/design-system/components/steps.md +63 -0
  69. package/docs/design-system/components/table.md +397 -0
  70. package/docs/design-system/components/tabs.md +52 -0
  71. package/docs/design-system/components/tag.md +78 -0
  72. package/docs/design-system/components/textarea.md +48 -0
  73. package/docs/design-system/components/timeline.md +81 -0
  74. package/docs/design-system/components/toast.md +56 -0
  75. package/docs/design-system/components/toggle.md +79 -0
  76. package/docs/design-system/components/toolbar.md +85 -0
  77. package/docs/design-system/components/tooltip.md +90 -0
  78. package/docs/design-system/components/typography.md +18 -0
  79. package/docs/design-system/design-system-test-missing-items.md +368 -0
  80. package/docs/design-system/icons.md +69 -0
  81. package/docs/design-system/registry-and-cli.md +41 -0
  82. package/docs/design-system/tabs.md +53 -0
  83. package/docs/design-system/token-governance.md +38 -0
  84. package/package.json +83 -65
  85. package/registry/components/alert-dialog.tsx +297 -0
  86. package/registry/components/aspect-ratio.tsx +30 -0
  87. package/registry/components/carousel.tsx +234 -0
  88. package/registry/components/chart.tsx +170 -0
  89. package/registry/components/collapsible.tsx +69 -0
  90. package/registry/components/command.tsx +174 -0
  91. package/registry/components/context-menu.tsx +236 -0
  92. package/registry/components/date-picker.tsx +1 -1
  93. package/registry/components/expanded/PageHeader.tsx +1 -1
  94. package/registry/components/expanded/breadcrumbs.css +139 -139
  95. package/registry/components/expanded/catalogComponentsShowcase.css +83 -83
  96. package/registry/components/expanded/steps.css +274 -274
  97. package/registry/components/expanded/timeline.css +264 -264
  98. package/registry/components/field.tsx +230 -0
  99. package/registry/components/hover-card.tsx +48 -0
  100. package/registry/components/input-group.tsx +130 -0
  101. package/registry/components/input.tsx +2 -2
  102. package/registry/components/kbd.tsx +44 -0
  103. package/registry/components/label.tsx +78 -0
  104. package/registry/components/menu.tsx +3 -1
  105. package/registry/components/menubar.tsx +226 -0
  106. package/registry/components/modal.tsx +109 -76
  107. package/registry/components/native-select.tsx +205 -0
  108. package/registry/components/navigation-menu.tsx +171 -0
  109. package/registry/components/radio-group.tsx +1 -1
  110. package/registry/components/resizable.tsx +74 -0
  111. package/registry/components/scroll-area.tsx +67 -0
  112. package/registry/components/select.tsx +2 -4
  113. package/registry/components/sheet.tsx +305 -0
  114. package/registry/components/sidebar.tsx +352 -0
  115. package/registry/components/social-button.tsx +74 -10
  116. package/registry/components/{expanded/tabs.css → tabs.css} +127 -106
  117. package/registry/components/tabs.tsx +242 -0
  118. package/registry/components/textarea.tsx +1 -1
  119. package/registry/components/toast.tsx +131 -0
  120. package/registry/examples/alert-dialog-demo.tsx +42 -0
  121. package/registry/examples/aspect-ratio-demo.tsx +11 -0
  122. package/registry/examples/carousel-demo.tsx +25 -0
  123. package/registry/examples/chart-demo.tsx +33 -0
  124. package/registry/examples/collapsible-demo.tsx +16 -0
  125. package/registry/examples/command-demo.tsx +42 -0
  126. package/registry/examples/context-menu-demo.tsx +29 -0
  127. package/registry/examples/expanded/tabs-demo.tsx +1 -1
  128. package/registry/examples/field-demo.tsx +51 -0
  129. package/registry/examples/hover-card-demo.tsx +23 -0
  130. package/registry/examples/input-group-demo.tsx +16 -0
  131. package/registry/examples/kbd-demo.tsx +11 -0
  132. package/registry/examples/label-demo.tsx +20 -0
  133. package/registry/examples/menubar-demo.tsx +34 -0
  134. package/registry/examples/native-select-demo.tsx +16 -0
  135. package/registry/examples/navigation-menu-demo.tsx +29 -0
  136. package/registry/examples/resizable-demo.tsx +22 -0
  137. package/registry/examples/scroll-area-demo.tsx +15 -0
  138. package/registry/examples/sheet-demo.tsx +47 -0
  139. package/registry/examples/sidebar-demo.tsx +55 -0
  140. package/registry/examples/tabs-demo.tsx +13 -0
  141. package/registry/examples/toast-demo.tsx +35 -0
  142. package/registry/index.json +655 -11
  143. package/registry/styles/globals.css +4733 -4690
  144. package/registry.json +1612 -0
  145. package/schema/config.schema.json +48 -0
  146. package/schema/registry.schema.json +85 -0
  147. package/schema/tokens.schema.json +63 -0
  148. package/src/cli/index.js +312 -18
  149. package/tokens/banhaten.tokens.json +1 -1
  150. package/registry/assets/avatars/avatar-02.jpg +0 -0
  151. package/registry/assets/avatars/avatar-03.jpg +0 -0
  152. package/registry/assets/avatars/avatar-04.jpg +0 -0
  153. package/registry/assets/avatars/avatar-05.jpg +0 -0
  154. package/registry/assets/avatars/avatar-06.jpg +0 -0
  155. package/registry/assets/avatars/avatar-07.jpg +0 -0
  156. package/registry/assets/avatars/avatar-08.jpg +0 -0
  157. package/registry/assets/avatars/avatar-09.jpg +0 -0
  158. package/registry/assets/avatars/avatar-10.jpg +0 -0
  159. package/registry/assets/avatars/avatar-11.jpg +0 -0
  160. package/registry/assets/avatars/avatar-12.jpg +0 -0
  161. package/registry/assets/avatars/avatar-13.jpg +0 -0
  162. package/registry/assets/avatars/avatar-14.jpg +0 -0
  163. package/registry/assets/avatars/avatar-15.jpg +0 -0
  164. package/registry/assets/avatars/avatar-16.jpg +0 -0
  165. package/registry/assets/avatars/avatar-17.jpg +0 -0
  166. package/registry/assets/avatars/avatar-18.jpg +0 -0
  167. package/registry/assets/avatars/avatar-19.jpg +0 -0
  168. package/registry/assets/avatars/avatar-20.jpg +0 -0
  169. package/registry/assets/avatars/avatar-21.jpg +0 -0
  170. package/registry/assets/avatars/avatar-22.jpg +0 -0
  171. package/registry/assets/avatars/avatar-23.jpg +0 -0
  172. package/registry/assets/avatars/avatar-24.jpg +0 -0
  173. package/registry/assets/avatars/avatar-25.jpg +0 -0
  174. package/registry/assets/avatars/avatar-26.jpg +0 -0
  175. package/registry/assets/avatars/avatar-27.jpg +0 -0
  176. package/registry/assets/avatars/avatar-28.jpg +0 -0
  177. package/registry/assets/avatars/avatar-29.jpg +0 -0
  178. package/registry/assets/avatars/avatar-30.jpg +0 -0
  179. package/registry/assets/avatars/avatar-31.jpg +0 -0
  180. package/registry/assets/avatars/avatar-32.jpg +0 -0
  181. package/registry/assets/avatars/avatar-33.jpg +0 -0
  182. package/registry/assets/avatars/avatar-34.jpg +0 -0
  183. package/registry/assets/avatars/avatar-35.jpg +0 -0
  184. package/registry/assets/image-assets.json +0 -744
  185. package/registry/assets/images/art-02.jpg +0 -0
  186. package/registry/assets/images/art-03.jpg +0 -0
  187. package/registry/assets/images/art-04.jpg +0 -0
  188. package/registry/assets/images/art-05.jpg +0 -0
  189. package/registry/assets/images/art-06.jpg +0 -0
  190. package/registry/assets/images/art-07.jpg +0 -0
  191. package/registry/assets/images/art-08.jpg +0 -0
  192. package/registry/assets/images/art-09.jpg +0 -0
  193. package/registry/assets/images/art-10.jpg +0 -0
  194. package/registry/assets/images/art-11.jpg +0 -0
  195. package/registry/assets/images/art-12.jpg +0 -0
  196. package/registry/assets/images/art-13.jpg +0 -0
  197. package/registry/assets/images/art-14.jpg +0 -0
  198. package/registry/assets/images/art-15.jpg +0 -0
  199. package/registry/assets/images/art-16.jpg +0 -0
  200. package/registry/assets/images/art-17.jpg +0 -0
  201. package/registry/assets/images/art-18.jpg +0 -0
  202. package/registry/assets/images/art-19.jpg +0 -0
  203. package/registry/assets/images/art-20.jpg +0 -0
  204. package/registry/assets/images/art-21.jpg +0 -0
  205. package/registry/assets/images/art-22.jpg +0 -0
  206. package/registry/assets/images/art-23.jpg +0 -0
  207. package/registry/assets/images/art-24.jpg +0 -0
  208. package/registry/assets/images/art-25.jpg +0 -0
  209. package/registry/assets/images/art-26.jpg +0 -0
  210. package/registry/assets/images/art-27.jpg +0 -0
  211. package/registry/assets/images/nature-01.jpg +0 -0
  212. package/registry/assets/images/nature-02.jpg +0 -0
  213. package/registry/assets/images/nature-03.jpg +0 -0
  214. package/registry/assets/images/nature-04.jpg +0 -0
  215. package/registry/assets/images/nature-05.jpg +0 -0
  216. package/registry/assets/images/nature-06.jpg +0 -0
  217. package/registry/assets/images/nature-07.jpg +0 -0
  218. package/registry/assets/images/nature-08.jpg +0 -0
  219. package/registry/assets/images/nature-09.jpg +0 -0
  220. package/registry/assets/images/nature-10.jpg +0 -0
  221. package/registry/assets/images/nature-11.jpg +0 -0
  222. package/registry/assets/images/nature-12.jpg +0 -0
  223. package/registry/assets/images/nature-13.jpg +0 -0
  224. package/registry/assets/images/nature-14.jpg +0 -0
  225. package/registry/assets/images/nature-15.jpg +0 -0
  226. package/registry/assets/images/nature-16.jpg +0 -0
  227. package/registry/assets/images/nature-17.jpg +0 -0
  228. package/registry/assets/images/nature-18.jpg +0 -0
  229. package/registry/assets/images/nature-19.jpg +0 -0
  230. package/registry/assets/images/nature-20.jpg +0 -0
  231. package/registry/components/expanded/Tabs.tsx +0 -86
@@ -0,0 +1,48 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:banhaten:schema:config",
4
+ "title": "Banhaten CLI config",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "properties": {
8
+ "$schema": {
9
+ "type": "string"
10
+ },
11
+ "style": {
12
+ "type": "string",
13
+ "default": "default"
14
+ },
15
+ "tsx": {
16
+ "type": "boolean",
17
+ "default": true
18
+ },
19
+ "tailwind": {
20
+ "type": "object",
21
+ "additionalProperties": false,
22
+ "properties": {
23
+ "css": {
24
+ "type": "string",
25
+ "description": "Path to the global CSS file that imports Tailwind and receives Banhaten tokens."
26
+ }
27
+ }
28
+ },
29
+ "aliases": {
30
+ "type": "object",
31
+ "additionalProperties": false,
32
+ "properties": {
33
+ "ui": {
34
+ "type": "string",
35
+ "description": "Import alias where Banhaten UI components are installed."
36
+ },
37
+ "utils": {
38
+ "type": "string",
39
+ "description": "Import alias for the cn utility file."
40
+ },
41
+ "hooks": {
42
+ "type": "string",
43
+ "description": "Import alias for generated hooks."
44
+ }
45
+ }
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,85 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:banhaten:schema:registry",
4
+ "title": "Banhaten registry index",
5
+ "type": "object",
6
+ "required": ["name", "base", "components"],
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "$schema": {
10
+ "type": "string"
11
+ },
12
+ "name": {
13
+ "type": "string"
14
+ },
15
+ "base": {
16
+ "$ref": "#/$defs/registryEntry"
17
+ },
18
+ "components": {
19
+ "type": "object",
20
+ "additionalProperties": {
21
+ "$ref": "#/$defs/registryEntry"
22
+ }
23
+ }
24
+ },
25
+ "$defs": {
26
+ "registryEntry": {
27
+ "type": "object",
28
+ "additionalProperties": true,
29
+ "properties": {
30
+ "description": {
31
+ "type": "string"
32
+ },
33
+ "dependencies": {
34
+ "type": "object",
35
+ "additionalProperties": {
36
+ "type": "string"
37
+ }
38
+ },
39
+ "registryDependencies": {
40
+ "type": "array",
41
+ "items": {
42
+ "type": "string"
43
+ }
44
+ },
45
+ "parts": {
46
+ "type": "array",
47
+ "items": {
48
+ "type": "string"
49
+ }
50
+ },
51
+ "files": {
52
+ "type": "array",
53
+ "items": {
54
+ "$ref": "#/$defs/registryFile"
55
+ }
56
+ },
57
+ "examples": {
58
+ "type": "array",
59
+ "items": {
60
+ "type": "string"
61
+ }
62
+ },
63
+ "rtl": {
64
+ "type": "object",
65
+ "additionalProperties": {
66
+ "type": ["string", "boolean"]
67
+ }
68
+ }
69
+ }
70
+ },
71
+ "registryFile": {
72
+ "type": "object",
73
+ "required": ["source", "target"],
74
+ "additionalProperties": false,
75
+ "properties": {
76
+ "source": {
77
+ "type": "string"
78
+ },
79
+ "target": {
80
+ "type": "string"
81
+ }
82
+ }
83
+ }
84
+ }
85
+ }
@@ -0,0 +1,63 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:banhaten:schema:tokens",
4
+ "title": "Banhaten design tokens",
5
+ "type": "object",
6
+ "required": ["name", "sourceStatus", "sourceFiles", "modes", "cssContract", "tokens"],
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "$schema": {
10
+ "type": "string"
11
+ },
12
+ "name": {
13
+ "type": "string"
14
+ },
15
+ "sourceStatus": {
16
+ "type": "string"
17
+ },
18
+ "sourceFiles": {
19
+ "type": "array",
20
+ "items": {
21
+ "type": "string"
22
+ }
23
+ },
24
+ "modes": {
25
+ "type": "object",
26
+ "additionalProperties": {
27
+ "type": "array",
28
+ "items": {
29
+ "type": "string"
30
+ }
31
+ }
32
+ },
33
+ "cssContract": {
34
+ "type": "object",
35
+ "required": ["semanticPrefix", "fontFamily", "defaultTheme", "defaultRadius"],
36
+ "additionalProperties": true,
37
+ "properties": {
38
+ "semanticPrefix": {
39
+ "type": "string"
40
+ },
41
+ "fontFamily": {
42
+ "type": "string"
43
+ },
44
+ "defaultTheme": {
45
+ "type": "string"
46
+ },
47
+ "defaultRadius": {
48
+ "type": "string"
49
+ },
50
+ "shadcnAliases": {
51
+ "type": "object",
52
+ "additionalProperties": {
53
+ "type": "string"
54
+ }
55
+ }
56
+ }
57
+ },
58
+ "tokens": {
59
+ "type": "object",
60
+ "additionalProperties": true
61
+ }
62
+ }
63
+ }
package/src/cli/index.js CHANGED
@@ -12,6 +12,8 @@ const packageRoot = path.resolve(
12
12
  )
13
13
  const registryRoot = path.join(packageRoot, "registry")
14
14
  const registryIndexPath = path.join(registryRoot, "index.json")
15
+ const packageJsonPath = path.join(packageRoot, "package.json")
16
+ const schemaRoot = path.join(packageRoot, "schema")
15
17
 
16
18
  const CONFIG_FILE = "banhaten.config.json"
17
19
  const FONT_IMPORTS = [
@@ -30,9 +32,14 @@ const VITE_DEV_DEPENDENCIES = {
30
32
  const CSS_START = "/* Banhaten design system tokens:"
31
33
  const CSS_END = "/* End Banhaten design system tokens. */"
32
34
  const syncExistsCache = new Map()
35
+ const VALUE_FLAGS = new Set(["cwd", "file", "format"])
36
+ let runtimeOptions = {
37
+ json: false,
38
+ silent: false,
39
+ }
33
40
 
34
41
  const DEFAULT_CONFIG = {
35
- $schema: "https://banhaten.dev/schema.json",
42
+ $schema: ".banhaten/schema/config.schema.json",
36
43
  style: "default",
37
44
  tsx: true,
38
45
  tailwind: {
@@ -46,6 +53,7 @@ const DEFAULT_CONFIG = {
46
53
  }
47
54
 
48
55
  const registry = await readJson(registryIndexPath)
56
+ const packageManifest = await readJson(packageJsonPath)
49
57
 
50
58
  main().catch((error) => {
51
59
  console.error(`banhaten: ${error.message}`)
@@ -54,6 +62,15 @@ main().catch((error) => {
54
62
 
55
63
  async function main() {
56
64
  const { command, positionals, flags } = parseArgs(process.argv.slice(2))
65
+ runtimeOptions = {
66
+ json: Boolean(flags.json),
67
+ silent: Boolean(flags.silent || flags.s),
68
+ }
69
+
70
+ if (command === "version" || flags.version || flags.v) {
71
+ console.log(packageManifest.version)
72
+ return
73
+ }
57
74
 
58
75
  if (!command || command === "help" || flags.help || flags.h) {
59
76
  printHelp()
@@ -61,7 +78,22 @@ async function main() {
61
78
  }
62
79
 
63
80
  if (command === "list") {
64
- listComponents()
81
+ listComponents(flags)
82
+ return
83
+ }
84
+
85
+ if (command === "search") {
86
+ searchComponents(positionals, flags)
87
+ return
88
+ }
89
+
90
+ if (command === "docs") {
91
+ showComponentDocs(positionals, flags)
92
+ return
93
+ }
94
+
95
+ if (command === "view") {
96
+ await viewComponent(positionals, flags)
65
97
  return
66
98
  }
67
99
 
@@ -100,6 +132,21 @@ function parseArgs(argv) {
100
132
  continue
101
133
  }
102
134
 
135
+ if (arg === "-s") {
136
+ flags.s = true
137
+ continue
138
+ }
139
+
140
+ if (arg === "-v") {
141
+ flags.v = true
142
+ continue
143
+ }
144
+
145
+ if (arg === "-y") {
146
+ flags.yes = true
147
+ continue
148
+ }
149
+
103
150
  if (!arg.startsWith("--")) {
104
151
  positionals.push(arg)
105
152
  continue
@@ -114,7 +161,7 @@ function parseArgs(argv) {
114
161
  }
115
162
 
116
163
  const next = argv[index + 1]
117
- if ((key === "cwd" || key === "out") && next && !next.startsWith("-")) {
164
+ if (VALUE_FLAGS.has(key) && next && !next.startsWith("-")) {
118
165
  flags[key] = next
119
166
  index += 1
120
167
  continue
@@ -135,24 +182,196 @@ Usage:
135
182
  banhaten add <component...> [--cwd <path>] [--force] [--dry-run]
136
183
  banhaten diff [component...] [--cwd <path>] [--force]
137
184
  banhaten update [component...] [--cwd <path>] [--force] [--dry-run]
138
- banhaten list
185
+ banhaten list [--json]
186
+ banhaten search <query> [--json]
187
+ banhaten docs <component> [--json]
188
+ banhaten view <component> [--file <registry-source>] [--json]
189
+ banhaten version
139
190
 
140
191
  Examples:
141
192
  npx banhaten init
142
193
  npx banhaten add button
143
194
  npx banhaten diff
144
195
  npx banhaten update button
196
+ npx banhaten search input
197
+ npx banhaten docs button
198
+ npx banhaten view button
199
+
200
+ Global flags:
201
+ --cwd <path> Run against a project directory.
202
+ --dry-run Preview writes without changing files.
203
+ --force Rewrite generated files even when already current.
204
+ --json Print machine-readable output where supported.
205
+ --silent, -s Suppress normal command output.
206
+ --yes, -y Accepted for non-interactive scripts.
207
+ --version, -v Print the installed CLI version.
145
208
  `)
146
209
  }
147
210
 
148
211
  function listComponents() {
149
- const names = Object.keys(registry.components)
212
+ const components = Object.entries(registry.components).map(([name, component]) => ({
213
+ name,
214
+ description: component.description || "",
215
+ dependencies: Object.keys(component.dependencies || {}),
216
+ registryDependencies: component.registryDependencies || [],
217
+ }))
218
+
219
+ if (runtimeOptions.json) {
220
+ writeJson({ components })
221
+ return
222
+ }
223
+
224
+ if (runtimeOptions.silent) return
150
225
 
151
226
  console.log("Available Banhaten design system components:")
152
- for (const name of names) {
153
- const component = registry.components[name]
154
- console.log(`- ${name}: ${component.description}`)
227
+ for (const { name, description } of components) {
228
+ console.log(`- ${name}: ${description}`)
229
+ }
230
+ }
231
+
232
+ function searchComponents(positionals) {
233
+ const query = positionals.join(" ").trim().toLowerCase()
234
+ if (!query) {
235
+ throw new Error("missing search query. Try `banhaten search input`.")
236
+ }
237
+
238
+ const matches = Object.entries(registry.components)
239
+ .filter(([name, component]) => {
240
+ const haystack = [
241
+ name,
242
+ component.description,
243
+ ...(component.parts || []),
244
+ ...(component.registryDependencies || []),
245
+ ]
246
+ .filter(Boolean)
247
+ .join(" ")
248
+ .toLowerCase()
249
+
250
+ return haystack.includes(query)
251
+ })
252
+ .map(([name, component]) => ({
253
+ name,
254
+ description: component.description || "",
255
+ parts: component.parts || [],
256
+ }))
257
+
258
+ if (runtimeOptions.json) {
259
+ writeJson({ query, matches })
260
+ return
261
+ }
262
+
263
+ if (runtimeOptions.silent) return
264
+
265
+ if (matches.length === 0) {
266
+ console.log(`No Banhaten components matched "${query}".`)
267
+ return
268
+ }
269
+
270
+ console.log(`Banhaten components matching "${query}":`)
271
+ for (const match of matches) {
272
+ console.log(`- ${match.name}: ${match.description}`)
273
+ }
274
+ }
275
+
276
+ function showComponentDocs(positionals) {
277
+ const [name] = positionals
278
+ const component = getComponentOrThrow(name)
279
+ const docsPath = getComponentDocPath(name)
280
+ const result = {
281
+ name,
282
+ description: component.description || "",
283
+ install: `npx banhaten add ${name}`,
284
+ docsPath,
285
+ files: component.files || [],
286
+ examples: component.examples || [],
287
+ }
288
+
289
+ if (runtimeOptions.json) {
290
+ writeJson(result)
291
+ return
292
+ }
293
+
294
+ if (runtimeOptions.silent) return
295
+
296
+ console.log(`${name}: ${result.description}`)
297
+ console.log(`Install: ${result.install}`)
298
+ console.log(`Docs: ${docsPath}`)
299
+
300
+ if (result.files.length > 0) {
301
+ console.log("Files:")
302
+ for (const file of result.files) {
303
+ console.log(`- ${file.source} -> ${file.target}`)
304
+ }
305
+ }
306
+
307
+ if (result.examples.length > 0) {
308
+ console.log("Examples:")
309
+ for (const example of result.examples) {
310
+ console.log(`- ${example}`)
311
+ }
312
+ }
313
+ }
314
+
315
+ async function viewComponent(positionals, flags) {
316
+ const [name] = positionals
317
+ const component = getComponentOrThrow(name)
318
+ const files = component.files || []
319
+ const requestedFile = typeof flags.file === "string" ? flags.file : null
320
+ const file =
321
+ (requestedFile
322
+ ? files.find((entry) => entry.source === requestedFile)
323
+ : files.find((entry) => /\.(ts|tsx|css)$/.test(entry.source)) || files[0]) ||
324
+ null
325
+
326
+ if (!file) {
327
+ throw new Error(`component "${name}" has no viewable registry files.`)
328
+ }
329
+
330
+ const sourcePath = path.join(registryRoot, file.source)
331
+ const content = await fs.readFile(
332
+ sourcePath,
333
+ isTextRegistryFile(file.source) ? "utf8" : undefined
334
+ )
335
+
336
+ if (runtimeOptions.json) {
337
+ writeJson({
338
+ name,
339
+ file,
340
+ content: Buffer.isBuffer(content) ? content.toString("base64") : content,
341
+ encoding: Buffer.isBuffer(content) ? "base64" : "utf8",
342
+ })
343
+ return
155
344
  }
345
+
346
+ if (runtimeOptions.silent) return
347
+
348
+ console.log(Buffer.isBuffer(content) ? content.toString("base64") : content)
349
+ }
350
+
351
+ function getComponentOrThrow(name) {
352
+ if (!name) {
353
+ throw new Error("missing component name. Try `banhaten docs button`.")
354
+ }
355
+
356
+ const component = registry.components[name]
357
+ if (!component) {
358
+ throw new Error(`unknown component "${name}". Run \`banhaten list\`.`)
359
+ }
360
+
361
+ return component
362
+ }
363
+
364
+ function getComponentDocPath(name) {
365
+ const docAliases = {
366
+ "onboarding-step": "onboarding-step-list-item",
367
+ }
368
+ const docId = docAliases[name] || name
369
+
370
+ return `docs/design-system/components/${docId}.md`
371
+ }
372
+
373
+ function writeJson(value) {
374
+ console.log(JSON.stringify(value, null, 2))
156
375
  }
157
376
 
158
377
  async function initProject(flags) {
@@ -165,6 +384,7 @@ async function initProject(flags) {
165
384
 
166
385
  writes.push(
167
386
  ...(await writeConfig(cwd, config, { dryRun, force })),
387
+ ...(await writeProjectSchemaFiles(cwd, { dryRun, force })),
168
388
  ...(await writeBaseFiles(cwd, config, { dryRun, force })),
169
389
  ...(await writeGlobalCss(cwd, config, { dryRun, force })),
170
390
  ...(await writeAliasConfig(cwd, config, { dryRun, force }))
@@ -178,7 +398,7 @@ async function initProject(flags) {
178
398
  writes.push(...dependencyWrites)
179
399
 
180
400
  printResult("Initialized Banhaten design system", writes, dryRun)
181
- printInstallHint(writes, dryRun)
401
+ await printInstallHint(cwd, writes, dryRun)
182
402
  }
183
403
 
184
404
  async function addComponents(names, flags) {
@@ -236,7 +456,9 @@ async function writeComponents(requestedNames, flags, options) {
236
456
 
237
457
  writes.push(
238
458
  ...(await writeConfig(cwd, config, { dryRun, force })),
459
+ ...(await writeProjectSchemaFiles(cwd, { dryRun, force })),
239
460
  ...(await writeBaseFiles(cwd, config, { dryRun, force })),
461
+ ...(await writeGlobalCss(cwd, config, { dryRun, force })),
240
462
  ...(await writeAliasConfig(cwd, config, { dryRun, force }))
241
463
  )
242
464
 
@@ -262,7 +484,7 @@ async function writeComponents(requestedNames, flags, options) {
262
484
  )))
263
485
 
264
486
  printResult(options.title, writes, dryRun)
265
- printInstallHint(writes, dryRun)
487
+ await printInstallHint(cwd, writes, dryRun)
266
488
  }
267
489
 
268
490
  async function findInstalledComponents(cwd, config) {
@@ -371,6 +593,24 @@ async function detectCssPath(cwd, fallback) {
371
593
  async function writeConfig(cwd, config, options) {
372
594
  const configPath = path.join(cwd, CONFIG_FILE)
373
595
  if ((await exists(configPath)) && !options.force) {
596
+ const current = await fs.readFile(configPath, "utf8")
597
+ const currentConfig = JSON.parse(current)
598
+
599
+ if (currentConfig.$schema !== DEFAULT_CONFIG.$schema) {
600
+ const nextConfig = {
601
+ $schema: DEFAULT_CONFIG.$schema,
602
+ ...currentConfig,
603
+ }
604
+ nextConfig.$schema = DEFAULT_CONFIG.$schema
605
+ const status = await writeFile(
606
+ configPath,
607
+ `${JSON.stringify(nextConfig, null, 2)}\n`,
608
+ { dryRun: options.dryRun }
609
+ )
610
+
611
+ return [`${status} ${relative(cwd, configPath)}`]
612
+ }
613
+
374
614
  return [`kept ${relative(cwd, configPath)}`]
375
615
  }
376
616
 
@@ -382,6 +622,26 @@ async function writeConfig(cwd, config, options) {
382
622
  return [`${status} ${relative(cwd, configPath)}`]
383
623
  }
384
624
 
625
+ async function writeProjectSchemaFiles(cwd, options) {
626
+ const schemaFiles = [
627
+ "config.schema.json",
628
+ "registry.schema.json",
629
+ "tokens.schema.json",
630
+ ]
631
+ const writes = []
632
+
633
+ for (const schemaFile of schemaFiles) {
634
+ const sourcePath = path.join(schemaRoot, schemaFile)
635
+ const targetPath = path.join(cwd, ".banhaten", "schema", schemaFile)
636
+ const source = await fs.readFile(sourcePath, "utf8")
637
+ const status = await writeFile(targetPath, source, options)
638
+
639
+ writes.push(`${status} ${relative(cwd, targetPath)}`)
640
+ }
641
+
642
+ return writes
643
+ }
644
+
385
645
  async function writeBaseFiles(cwd, config, options) {
386
646
  return writeRegistryFiles(cwd, config, registry.base.files || [], options)
387
647
  }
@@ -413,16 +673,12 @@ async function writeGlobalCss(cwd, config, options) {
413
673
  ? removeExistingTokenBlock(current, relative(cwd, cssPath))
414
674
  : { content: "", found: false }
415
675
 
416
- if (tokenBlock.found && !options.force) {
417
- return [`kept ${relative(cwd, cssPath)}`]
418
- }
419
-
420
676
  const nextCss = current
421
677
  ? `${tokenBlock.content.trimEnd()}\n\n${tokenCss}\n`
422
678
  : `${tokenCss}\n`
423
679
  const next = ensureGlobalCssImports(nextCss)
424
680
 
425
- const status = await writeFile(cssPath, next, { ...options, force: true })
681
+ const status = await writeFile(cssPath, next, options)
426
682
  return [`${status} ${relative(cwd, cssPath)}`]
427
683
  }
428
684
 
@@ -1742,20 +1998,58 @@ function relative(cwd, targetPath) {
1742
1998
  }
1743
1999
 
1744
2000
  function printResult(title, writes, dryRun) {
2001
+ if (runtimeOptions.json) {
2002
+ writeJson({ dryRun, title, writes })
2003
+ return
2004
+ }
2005
+
2006
+ if (runtimeOptions.silent) return
2007
+
1745
2008
  console.log(`${dryRun ? "Dry run: " : ""}${title}`)
1746
2009
  for (const write of writes) {
1747
2010
  console.log(`- ${write}`)
1748
2011
  }
1749
2012
  }
1750
2013
 
1751
- function printInstallHint(writes, dryRun) {
1752
- if (dryRun) return
2014
+ async function printInstallHint(cwd, writes, dryRun) {
2015
+ if (dryRun || runtimeOptions.json || runtimeOptions.silent) return
1753
2016
 
1754
2017
  const packageJsonChanged = writes.some((write) =>
1755
2018
  /^(wrote|updated) package\.json$/.test(write)
1756
2019
  )
1757
2020
 
1758
2021
  if (packageJsonChanged) {
1759
- console.log("\nNext: run npm install to install the added dependencies.")
2022
+ console.log(`\nNext: run ${await getInstallCommand(cwd)} to install the added dependencies.`)
2023
+ }
2024
+ }
2025
+
2026
+ async function getInstallCommand(cwd) {
2027
+ const packageJson = await readJsonIfExists(path.join(cwd, "package.json"))
2028
+ const declaredPackageManager = parsePackageManager(packageJson?.packageManager)
2029
+
2030
+ if (declaredPackageManager) return `${declaredPackageManager} install`
2031
+
2032
+ const lockfileManagers = [
2033
+ ["pnpm-lock.yaml", "pnpm"],
2034
+ ["yarn.lock", "yarn"],
2035
+ ["bun.lockb", "bun"],
2036
+ ["bun.lock", "bun"],
2037
+ ["package-lock.json", "npm"],
2038
+ ["npm-shrinkwrap.json", "npm"],
2039
+ ]
2040
+
2041
+ for (const [lockfile, manager] of lockfileManagers) {
2042
+ if (await exists(path.join(cwd, lockfile))) return `${manager} install`
1760
2043
  }
2044
+
2045
+ return "npm install"
2046
+ }
2047
+
2048
+ function parsePackageManager(packageManager) {
2049
+ if (typeof packageManager !== "string") return null
2050
+
2051
+ const name = packageManager.split("@")[0]
2052
+ if (["npm", "pnpm", "yarn", "bun"].includes(name)) return name
2053
+
2054
+ return null
1761
2055
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "https://banhaten.dev/tokens.schema.json",
2
+ "$schema": "../schema/tokens.schema.json",
3
3
  "name": "Banhaten design system",
4
4
  "sourceStatus": "zip-derived",
5
5
  "sourceFiles": [