@nons-dev/uikit 0.1.4 → 0.1.6
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.
- package/dist/font/IRANSansX-ExtraBold.woff +0 -0
- package/dist/font/IRANSansX-Medium.woff +0 -0
- package/dist/font/IRANSansX-Regular.woff +0 -0
- package/dist/font/Satoshi-Bold.woff2 +0 -0
- package/dist/font/Satoshi-Medium.woff2 +0 -0
- package/dist/font/Satoshi-Regular.woff2 +0 -0
- package/dist/index.css +1 -1
- package/generated/components.json +2665 -0
- package/package.json +8 -3
- package/readme.md +107 -16
- package/scripts/cli.mjs +525 -0
package/scripts/cli.mjs
ADDED
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'node:fs'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
import * as yaml from 'js-yaml'
|
|
7
|
+
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
9
|
+
const COMPONENTS_JSON = path.join(__dirname, '..', 'generated', 'components.json')
|
|
10
|
+
const CWD = process.cwd()
|
|
11
|
+
|
|
12
|
+
const RESET = '\x1b[0m'
|
|
13
|
+
const BOLD = '\x1b[1m'
|
|
14
|
+
const DIM = '\x1b[2m'
|
|
15
|
+
const GREEN = '\x1b[32m'
|
|
16
|
+
const CYAN = '\x1b[36m'
|
|
17
|
+
const YELLOW = '\x1b[33m'
|
|
18
|
+
const MAGENTA = '\x1b[35m'
|
|
19
|
+
const GRAY = '\x1b[90m'
|
|
20
|
+
const RED = '\x1b[31m'
|
|
21
|
+
const BLUE = '\x1b[34m'
|
|
22
|
+
|
|
23
|
+
const DEFAULT_CONFIG = `theme:
|
|
24
|
+
preset: nons
|
|
25
|
+
|
|
26
|
+
brand:
|
|
27
|
+
name: Nons
|
|
28
|
+
|
|
29
|
+
direction:
|
|
30
|
+
default: rtl
|
|
31
|
+
|
|
32
|
+
locale:
|
|
33
|
+
default: fa
|
|
34
|
+
supported:
|
|
35
|
+
- fa
|
|
36
|
+
- en
|
|
37
|
+
|
|
38
|
+
font:
|
|
39
|
+
fa:
|
|
40
|
+
family: IRANSansX
|
|
41
|
+
en:
|
|
42
|
+
family: Satoshi
|
|
43
|
+
# Faces loaded automatically from @nons-dev/uikit/dist/font/
|
|
44
|
+
# Uncomment and customize src to use your own font files:
|
|
45
|
+
# faces:
|
|
46
|
+
# - family: Satoshi
|
|
47
|
+
# weight: 400
|
|
48
|
+
# src: fonts/Satoshi-Regular.woff2
|
|
49
|
+
# format: woff2
|
|
50
|
+
# - family: IRANSansX
|
|
51
|
+
# weight: 400
|
|
52
|
+
# src: fonts/IRANSansX-Regular.woff
|
|
53
|
+
# format: woff
|
|
54
|
+
|
|
55
|
+
tokens:
|
|
56
|
+
colors:
|
|
57
|
+
light:
|
|
58
|
+
--color-text-default: '#37352F'
|
|
59
|
+
--color-text-grey: '#787774'
|
|
60
|
+
--color-ui-window: '#FFFFFF'
|
|
61
|
+
--color-ui-sidebar: '#F7F6F3'
|
|
62
|
+
--color-ui-hover: rgba(55, 53, 47, 0.08)
|
|
63
|
+
--color-primary: '#9FE870'
|
|
64
|
+
--color-primary-bg: rgba(159, 232, 112, 0.15)
|
|
65
|
+
--color-primary-dark: '#163300'
|
|
66
|
+
--color-primary-text: var(--color-primary-dark)
|
|
67
|
+
--color-primary-selected-bg: rgba(159, 232, 112, 0.08)
|
|
68
|
+
--color-danger: '#F31260'
|
|
69
|
+
--color-danger-bg: '#FEE7EF'
|
|
70
|
+
--color-success: '#17C964'
|
|
71
|
+
--color-success-bg: '#E8FAF0'
|
|
72
|
+
--color-warning: '#F5A524'
|
|
73
|
+
--color-warning-bg: '#FEFCE8'
|
|
74
|
+
--color-secondary: '#7828C8'
|
|
75
|
+
--color-secondary-bg: '#F2EAFA'
|
|
76
|
+
--color-info: '#006FEE'
|
|
77
|
+
--color-info-bg: '#E7F3F8'
|
|
78
|
+
--color-default: '#11181C'
|
|
79
|
+
--color-default-bg: '#F4F4F5'
|
|
80
|
+
--color-border: '#D5D5D2'
|
|
81
|
+
--color-text-secondary: var(--color-text-grey)
|
|
82
|
+
--color-white: '#FFFFFF'
|
|
83
|
+
dark:
|
|
84
|
+
--color-text-default: '#FFFFFF'
|
|
85
|
+
--color-text-grey: '#979A9B'
|
|
86
|
+
--color-ui-window: '#111111'
|
|
87
|
+
--color-ui-sidebar: '#000000'
|
|
88
|
+
--color-ui-hover: rgba(255, 255, 255, 0.08)
|
|
89
|
+
--color-primary: '#9FE870'
|
|
90
|
+
--color-primary-bg: rgba(159, 232, 112, 0.15)
|
|
91
|
+
--color-primary-dark: '#163300'
|
|
92
|
+
--color-primary-text: var(--color-primary-dark)
|
|
93
|
+
--color-primary-selected-bg: rgba(159, 232, 112, 0.08)
|
|
94
|
+
--color-danger: '#f31260'
|
|
95
|
+
--color-danger-bg: '#310413'
|
|
96
|
+
--color-success: '#17c964'
|
|
97
|
+
--color-success-bg: '#052814'
|
|
98
|
+
--color-warning: '#f5a524'
|
|
99
|
+
--color-warning-bg: '#312107'
|
|
100
|
+
--color-secondary: '#9353d3'
|
|
101
|
+
--color-secondary-bg: '#180828'
|
|
102
|
+
--color-info: '#006fee'
|
|
103
|
+
--color-info-bg: '#001731'
|
|
104
|
+
--color-default: '#006fee'
|
|
105
|
+
--color-default-bg: '#001731'
|
|
106
|
+
--color-border: '#1D1D1D'
|
|
107
|
+
--color-text-secondary: var(--color-text-grey)
|
|
108
|
+
--color-white: '#FFFFFF'
|
|
109
|
+
|
|
110
|
+
spacing:
|
|
111
|
+
--space-0-5: 2px
|
|
112
|
+
--space-1: 4px
|
|
113
|
+
--space-2: 8px
|
|
114
|
+
--space-2-5: 10px
|
|
115
|
+
--space-3: 12px
|
|
116
|
+
--space-4: 16px
|
|
117
|
+
--space-5: 24px
|
|
118
|
+
--space-6: 32px
|
|
119
|
+
--space-8: 48px
|
|
120
|
+
--space-10: 64px
|
|
121
|
+
--space-3-5: 14px
|
|
122
|
+
--space-4-5: 18px
|
|
123
|
+
--border-width: 1px
|
|
124
|
+
--border-width-lg: 2px
|
|
125
|
+
|
|
126
|
+
typography:
|
|
127
|
+
--font-family: "'IRANSansX', 'Satoshi', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif"
|
|
128
|
+
--font-family-mono: "'SF Mono', 'Fira Code', 'Fira Mono', 'Roboto Mono', monospace"
|
|
129
|
+
--font-size-xs: 12px
|
|
130
|
+
--font-size-sm: 12px
|
|
131
|
+
--font-size-md: 14px
|
|
132
|
+
--font-size-lg: 16px
|
|
133
|
+
--font-size-xl: 18px
|
|
134
|
+
--font-size-2xl: 24px
|
|
135
|
+
--font-weight-normal: 400
|
|
136
|
+
--font-weight-medium: 500
|
|
137
|
+
--font-weight-bold: 700
|
|
138
|
+
--line-height-sm: 18px
|
|
139
|
+
--line-height-md: 22px
|
|
140
|
+
--line-height-lg: 30px
|
|
141
|
+
|
|
142
|
+
radius:
|
|
143
|
+
--radius-sm: 4px
|
|
144
|
+
--radius-md: 8px
|
|
145
|
+
--radius-lg: 12px
|
|
146
|
+
--radius-xl: 16px
|
|
147
|
+
--radius-full: 9999px
|
|
148
|
+
|
|
149
|
+
shadows:
|
|
150
|
+
light:
|
|
151
|
+
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06)
|
|
152
|
+
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.08)
|
|
153
|
+
--shadow-lg: 0 10px 25px rgba(0, 0, 0, 0.1)
|
|
154
|
+
dark:
|
|
155
|
+
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3)
|
|
156
|
+
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.35)
|
|
157
|
+
--shadow-lg: 0 10px 25px rgba(0, 0, 0, 0.4)
|
|
158
|
+
|
|
159
|
+
z-index:
|
|
160
|
+
--z-dropdown: 100
|
|
161
|
+
--z-popover: 90
|
|
162
|
+
--z-tooltip: 150
|
|
163
|
+
--z-modal: 1000
|
|
164
|
+
--z-panel: 200
|
|
165
|
+
|
|
166
|
+
opacity:
|
|
167
|
+
--opacity-disabled: 0.5
|
|
168
|
+
--opacity-secondary: 0.6
|
|
169
|
+
--opacity-placeholder: 0.6
|
|
170
|
+
--opacity-hint: 0.8
|
|
171
|
+
--opacity-hover: 0.8
|
|
172
|
+
|
|
173
|
+
button:
|
|
174
|
+
--btn-border-radius: 8px
|
|
175
|
+
--btn-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05)
|
|
176
|
+
--btn-padding: 10px 16px
|
|
177
|
+
--btn-font-size: 14px
|
|
178
|
+
--btn-font-weight: 500
|
|
179
|
+
--btn-font-family: "'IRANSansX', sans-serif"
|
|
180
|
+
|
|
181
|
+
alert:
|
|
182
|
+
--alert-border-radius: 16px
|
|
183
|
+
--alert-padding: 12px
|
|
184
|
+
--alert-gap: 16px
|
|
185
|
+
|
|
186
|
+
theme-mappings:
|
|
187
|
+
light:
|
|
188
|
+
--bg-primary: var(--color-ui-window)
|
|
189
|
+
--bg-sidebar: var(--color-ui-sidebar)
|
|
190
|
+
--bg-hover: var(--color-ui-hover)
|
|
191
|
+
--bg-card: var(--color-ui-window)
|
|
192
|
+
--text-primary: var(--color-text-default)
|
|
193
|
+
--text-secondary: var(--color-text-secondary)
|
|
194
|
+
--border-primary: var(--color-border)
|
|
195
|
+
--overlay-bg: rgba(0, 0, 0, 0.4)
|
|
196
|
+
dark:
|
|
197
|
+
--bg-primary: var(--color-ui-window)
|
|
198
|
+
--bg-sidebar: var(--color-ui-sidebar)
|
|
199
|
+
--bg-hover: var(--color-ui-hover)
|
|
200
|
+
--bg-card: '#161816'
|
|
201
|
+
--text-primary: var(--color-text-default)
|
|
202
|
+
--text-secondary: var(--color-text-grey)
|
|
203
|
+
--border-primary: var(--color-border)
|
|
204
|
+
--overlay-bg: rgba(0, 0, 0, 0.6)
|
|
205
|
+
`
|
|
206
|
+
|
|
207
|
+
// ── Data ──
|
|
208
|
+
|
|
209
|
+
function loadComponents() {
|
|
210
|
+
if (!fs.existsSync(COMPONENTS_JSON)) {
|
|
211
|
+
console.error(`${RED}✗${RESET} Component metadata not found.`)
|
|
212
|
+
console.error(` Expected: ${COMPONENTS_JSON}`)
|
|
213
|
+
process.exit(1)
|
|
214
|
+
}
|
|
215
|
+
return JSON.parse(fs.readFileSync(COMPONENTS_JSON, 'utf-8'))
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function findComponent(data, query) {
|
|
219
|
+
const lower = query.toLowerCase()
|
|
220
|
+
return data.components.find(c =>
|
|
221
|
+
c.name.toLowerCase() === lower || c.registryKey === lower
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function searchComponents(data, query) {
|
|
226
|
+
const lower = query.toLowerCase()
|
|
227
|
+
return data.components.filter(c =>
|
|
228
|
+
c.name.toLowerCase().includes(lower) ||
|
|
229
|
+
c.registryKey.includes(lower) ||
|
|
230
|
+
(c.description || '').toLowerCase().includes(lower)
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ── Generate helpers ──
|
|
235
|
+
|
|
236
|
+
function writeTokenBlock(tokens, indent) {
|
|
237
|
+
return Object.entries(tokens || {}).map(([k, v]) => `${indent}${k}: ${v};`).join('\n')
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function generateFontCSS(config) {
|
|
241
|
+
const faces = config.font?.faces
|
|
242
|
+
if (!faces || !faces.length) return ''
|
|
243
|
+
const lines = []
|
|
244
|
+
for (const face of faces) {
|
|
245
|
+
lines.push('@font-face {')
|
|
246
|
+
lines.push(` font-family: '${face.family}';`)
|
|
247
|
+
lines.push(` src: url('./${face.src}') format('${face.format}');`)
|
|
248
|
+
lines.push(` font-weight: ${face.weight};`)
|
|
249
|
+
lines.push(' font-style: normal;')
|
|
250
|
+
lines.push(' font-display: swap;')
|
|
251
|
+
lines.push('}')
|
|
252
|
+
lines.push('')
|
|
253
|
+
}
|
|
254
|
+
return lines.join('\n')
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function generateTokensCSS(config) {
|
|
258
|
+
const lines = []
|
|
259
|
+
const fonts = generateFontCSS(config)
|
|
260
|
+
if (fonts) lines.push(fonts)
|
|
261
|
+
|
|
262
|
+
const t = config.tokens || {}
|
|
263
|
+
|
|
264
|
+
// Light & dark color schemes
|
|
265
|
+
const hasLight = t.colors?.light && Object.keys(t.colors.light).length
|
|
266
|
+
const hasDark = t.colors?.dark && Object.keys(t.colors.dark).length
|
|
267
|
+
|
|
268
|
+
if (hasLight) {
|
|
269
|
+
lines.push(':root {')
|
|
270
|
+
lines.push(' color-scheme: light;')
|
|
271
|
+
lines.push(writeTokenBlock(t.colors.light, ' '))
|
|
272
|
+
lines.push('}')
|
|
273
|
+
lines.push('')
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (hasDark) {
|
|
277
|
+
lines.push('[data-theme="dark"] {')
|
|
278
|
+
lines.push(' color-scheme: dark;')
|
|
279
|
+
lines.push(writeTokenBlock(t.colors.dark, ' '))
|
|
280
|
+
lines.push('}')
|
|
281
|
+
lines.push('')
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Flat token sections (no light/dark split)
|
|
285
|
+
const flatSections = {
|
|
286
|
+
spacing: 'Spacing', typography: 'Typography', radius: 'Border Radius',
|
|
287
|
+
'z-index': 'Z-Index', opacity: 'Opacity', button: 'Button', alert: 'Alert',
|
|
288
|
+
}
|
|
289
|
+
for (const [section, label] of Object.entries(flatSections)) {
|
|
290
|
+
const tokens = t[section]
|
|
291
|
+
if (!tokens || !Object.keys(tokens).length) continue
|
|
292
|
+
lines.push(`/* ${label} */`)
|
|
293
|
+
lines.push(':root {')
|
|
294
|
+
lines.push(writeTokenBlock(tokens, ' '))
|
|
295
|
+
lines.push('}')
|
|
296
|
+
lines.push('')
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Shadows (light + dark)
|
|
300
|
+
const hasShadowLight = t.shadows?.light && Object.keys(t.shadows.light).length
|
|
301
|
+
const hasShadowDark = t.shadows?.dark && Object.keys(t.shadows.dark).length
|
|
302
|
+
if (hasShadowLight) {
|
|
303
|
+
lines.push(':root {')
|
|
304
|
+
lines.push(' color-scheme: light;')
|
|
305
|
+
lines.push(writeTokenBlock(t.shadows.light, ' '))
|
|
306
|
+
lines.push('}')
|
|
307
|
+
lines.push('')
|
|
308
|
+
}
|
|
309
|
+
if (hasShadowDark) {
|
|
310
|
+
lines.push('[data-theme="dark"] {')
|
|
311
|
+
lines.push(' color-scheme: dark;')
|
|
312
|
+
lines.push(writeTokenBlock(t.shadows.dark, ' '))
|
|
313
|
+
lines.push('}')
|
|
314
|
+
lines.push('')
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return lines.join('\n')
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function generateThemeCSS(config) {
|
|
321
|
+
const m = config['theme-mappings']
|
|
322
|
+
if (!m) return ''
|
|
323
|
+
const lines = []
|
|
324
|
+
|
|
325
|
+
lines.push(':root {')
|
|
326
|
+
lines.push(' color-scheme: light;')
|
|
327
|
+
if (m.light) lines.push(writeTokenBlock(m.light, ' '))
|
|
328
|
+
lines.push('}')
|
|
329
|
+
|
|
330
|
+
lines.push('')
|
|
331
|
+
lines.push('[data-theme="dark"] {')
|
|
332
|
+
lines.push(' color-scheme: dark;')
|
|
333
|
+
if (m.dark) lines.push(writeTokenBlock(m.dark, ' '))
|
|
334
|
+
lines.push('}')
|
|
335
|
+
|
|
336
|
+
return lines.join('\n')
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// ── Commands ──
|
|
340
|
+
|
|
341
|
+
function cmdInit() {
|
|
342
|
+
const dest = path.join(CWD, 'nons.config.yaml')
|
|
343
|
+
if (fs.existsSync(dest)) {
|
|
344
|
+
console.log(`${YELLOW}⚠${RESET} nons.config.yaml already exists — skipping.`)
|
|
345
|
+
return
|
|
346
|
+
}
|
|
347
|
+
fs.writeFileSync(dest, DEFAULT_CONFIG, 'utf-8')
|
|
348
|
+
console.log(`${GREEN}✓${RESET} nons.config.yaml created in`, CWD)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function cmdGenerate() {
|
|
352
|
+
const configPath = path.join(CWD, 'nons.config.yaml')
|
|
353
|
+
if (!fs.existsSync(configPath)) {
|
|
354
|
+
console.error(`${RED}✗${RESET} nons.config.yaml not found. Run \`nons init\` first.`)
|
|
355
|
+
process.exit(1)
|
|
356
|
+
}
|
|
357
|
+
const raw = fs.readFileSync(configPath, 'utf-8')
|
|
358
|
+
const config = yaml.load(raw)
|
|
359
|
+
const generatedDir = path.join(CWD, 'generated')
|
|
360
|
+
if (!fs.existsSync(generatedDir)) fs.mkdirSync(generatedDir, { recursive: true })
|
|
361
|
+
|
|
362
|
+
// tokens.css
|
|
363
|
+
const tokensCSS = generateTokensCSS(config)
|
|
364
|
+
fs.writeFileSync(path.join(generatedDir, 'tokens.css'), tokensCSS, 'utf-8')
|
|
365
|
+
console.log(`${GREEN}✓${RESET} generated/tokens.css`)
|
|
366
|
+
|
|
367
|
+
// theme.css
|
|
368
|
+
const themeCSS = generateThemeCSS(config)
|
|
369
|
+
if (themeCSS) {
|
|
370
|
+
fs.writeFileSync(path.join(generatedDir, 'theme.css'), themeCSS, 'utf-8')
|
|
371
|
+
console.log(`${GREEN}✓${RESET} generated/theme.css`)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
console.log(`${YELLOW}ℹ${RESET} Import in your app:\n import './generated/tokens.css'\n import './generated/theme.css'`)
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function cmdList() {
|
|
378
|
+
const data = loadComponents()
|
|
379
|
+
const grouped = {}
|
|
380
|
+
for (const c of data.components) {
|
|
381
|
+
if (!grouped[c.category]) grouped[c.category] = []
|
|
382
|
+
grouped[c.category].push(c)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
console.log(`\n${BOLD}${CYAN}Components (${data.components.length})${RESET}\n`)
|
|
386
|
+
const order = ['primitive', 'layout', 'component', 'section']
|
|
387
|
+
const labels = { primitive: 'Primitive', layout: 'Layout', component: 'Component', section: 'Section' }
|
|
388
|
+
const colors = { primitive: GREEN, layout: BLUE, component: CYAN, section: MAGENTA }
|
|
389
|
+
|
|
390
|
+
for (const cat of order) {
|
|
391
|
+
const items = grouped[cat]
|
|
392
|
+
if (!items || !items.length) continue
|
|
393
|
+
const color = colors[cat]
|
|
394
|
+
console.log(` ${color}${BOLD}── ${labels[cat]} (${items.length})${RESET}\n`)
|
|
395
|
+
for (const c of items) {
|
|
396
|
+
const desc = c.description ? ` ${GRAY}${c.description.substring(0, 50)}${c.description.length > 50 ? '…' : ''}${RESET}` : ''
|
|
397
|
+
console.log(` ${BOLD}${c.name}${RESET} ${DIM}[${c.registryKey}]${RESET}${desc}`)
|
|
398
|
+
}
|
|
399
|
+
console.log('')
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function cmdShow(name) {
|
|
404
|
+
const data = loadComponents()
|
|
405
|
+
const comp = findComponent(data, name)
|
|
406
|
+
if (!comp) {
|
|
407
|
+
console.log(`\n ${RED}✗ Component "${name}" not found.${RESET}\n`)
|
|
408
|
+
const similar = searchComponents(data, name).filter(c => c.name !== name)
|
|
409
|
+
if (similar.length) {
|
|
410
|
+
console.log(` ${YELLOW}Did you mean:${RESET}`)
|
|
411
|
+
for (const s of similar) console.log(` • ${s.name} ${DIM}[${s.registryKey}]${RESET}`)
|
|
412
|
+
console.log('')
|
|
413
|
+
}
|
|
414
|
+
process.exit(1)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const color = { primitive: GREEN, layout: BLUE, component: CYAN, section: MAGENTA }[comp.category] || CYAN
|
|
418
|
+
const label = { primitive: 'Primitive', layout: 'Layout', component: 'Component', section: 'Section' }[comp.category] || comp.category
|
|
419
|
+
|
|
420
|
+
console.log(`\n${BOLD}${CYAN}${comp.name}${RESET}\n`)
|
|
421
|
+
console.log(` ${DIM}Category:${RESET} ${color}${BOLD}${label}${RESET}`)
|
|
422
|
+
console.log(` ${DIM}Registry Key:${RESET} ${comp.registryKey}`)
|
|
423
|
+
console.log(` ${DIM}File:${RESET} ${comp.filePath}`)
|
|
424
|
+
if (comp.description) console.log(`\n ${comp.description}\n`)
|
|
425
|
+
|
|
426
|
+
if (comp.props.length) {
|
|
427
|
+
console.log(` ${BOLD}Props:${RESET}\n`)
|
|
428
|
+
for (const p of comp.props) {
|
|
429
|
+
const req = p.required ? `${YELLOW}required${RESET}` : `${DIM}optional${RESET}`
|
|
430
|
+
console.log(` ${BOLD}${p.name}${RESET} ${req}`)
|
|
431
|
+
console.log(` ${DIM}type:${RESET} ${CYAN}${p.type}${RESET}`)
|
|
432
|
+
if (p.allowedValues?.length) console.log(` ${DIM}allowed:${RESET} ${p.allowedValues.map(v => `${GREEN}'${v}'${RESET}`).join(' | ')}`)
|
|
433
|
+
if (p.default !== undefined) console.log(` ${DIM}default:${RESET} ${MAGENTA}${p.default}${RESET}`)
|
|
434
|
+
if (p.description) console.log(` ${DIM}desc:${RESET} ${p.description}`)
|
|
435
|
+
console.log('')
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (comp.events.length) {
|
|
440
|
+
console.log(` ${BOLD}Events:${RESET}\n`)
|
|
441
|
+
for (const e of comp.events) {
|
|
442
|
+
const payload = e.payload ? ` ${DIM}→ ${e.payload}${RESET}` : ''
|
|
443
|
+
console.log(` ${YELLOW}@${e.name}${RESET}${payload}`)
|
|
444
|
+
}
|
|
445
|
+
console.log('')
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (comp.slots.length) {
|
|
449
|
+
console.log(` ${BOLD}Slots:${RESET}\n`)
|
|
450
|
+
for (const s of comp.slots) console.log(` ${MAGENTA}#${s.name}${RESET}`)
|
|
451
|
+
console.log('')
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (comp.schemaExample) {
|
|
455
|
+
console.log(` ${BOLD}Schema Example:${RESET}\n`)
|
|
456
|
+
for (const line of comp.schemaExample.split('\n')) console.log(` ${GRAY}${line}${RESET}`)
|
|
457
|
+
console.log('')
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function cmdSchema(name) {
|
|
462
|
+
const data = loadComponents()
|
|
463
|
+
const comp = findComponent(data, name)
|
|
464
|
+
if (!comp) {
|
|
465
|
+
console.log(`\n ${RED}✗ Component "${name}" not found.${RESET}\n`)
|
|
466
|
+
process.exit(1)
|
|
467
|
+
}
|
|
468
|
+
console.log(`\n${BOLD}${CYAN}Schema: ${comp.name}${RESET}\n`)
|
|
469
|
+
console.log(comp.schemaExample || JSON.stringify({ type: comp.registryKey, props: {} }, null, 2))
|
|
470
|
+
console.log('')
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function cmdSearch(query) {
|
|
474
|
+
const data = loadComponents()
|
|
475
|
+
const matches = searchComponents(data, query)
|
|
476
|
+
if (!matches.length) {
|
|
477
|
+
console.log(`\n ${YELLOW}No components matched "${query}"${RESET}\n`)
|
|
478
|
+
return
|
|
479
|
+
}
|
|
480
|
+
const colors = { primitive: GREEN, layout: BLUE, component: CYAN, section: MAGENTA }
|
|
481
|
+
const labels = { primitive: 'Primitive', layout: 'Layout', component: 'Component', section: 'Section' }
|
|
482
|
+
console.log(`\n${BOLD}${CYAN}Search: "${query}"${RESET} ${DIM}(${matches.length} found)${RESET}\n`)
|
|
483
|
+
for (const c of matches) {
|
|
484
|
+
const color = colors[c.category] || CYAN
|
|
485
|
+
console.log(` ${BOLD}${c.name}${RESET} ${color}${labels[c.category] || c.category}${RESET} ${DIM}[${c.registryKey}]${RESET}`)
|
|
486
|
+
if (c.description) console.log(` ${GRAY}${c.description.substring(0, 70)}${c.description.length > 70 ? '…' : ''}${RESET}`)
|
|
487
|
+
}
|
|
488
|
+
console.log('')
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// ── Help ──
|
|
492
|
+
|
|
493
|
+
function printHelp() {
|
|
494
|
+
console.log(`\n${BOLD}${CYAN}Nons UIKit CLI${RESET}\n`)
|
|
495
|
+
console.log(`${BOLD}Commands:${RESET}\n`)
|
|
496
|
+
console.log(` ${GREEN}init${RESET} Create nons.config.yaml in current directory`)
|
|
497
|
+
console.log(` ${GREEN}generate${RESET} Generate tokens.css + theme.css from config`)
|
|
498
|
+
console.log(` ${GREEN}list${RESET} List all components`)
|
|
499
|
+
console.log(` ${GREEN}show${RESET} ${DIM}<name>${RESET} Show full detail for a component`)
|
|
500
|
+
console.log(` ${GREEN}schema${RESET} ${DIM}<name>${RESET} Show schema JSON for a component`)
|
|
501
|
+
console.log(` ${GREEN}search${RESET} ${DIM}<query>${RESET} Search components by name/description`)
|
|
502
|
+
console.log(` ${GREEN}help${RESET} Show this help\n`)
|
|
503
|
+
console.log(`${BOLD}Usage:${RESET}\n`)
|
|
504
|
+
console.log(` ${DIM}npx @nons-dev/uikit init${RESET}`)
|
|
505
|
+
console.log(` ${DIM}npx @nons-dev/uikit generate${RESET}`)
|
|
506
|
+
console.log(` ${DIM}npx @nons-dev/uikit list${RESET}`)
|
|
507
|
+
console.log(` ${DIM}npx @nons-dev/uikit show Button${RESET}`)
|
|
508
|
+
console.log(` ${DIM}npx @nons-dev/uikit schema Input${RESET}`)
|
|
509
|
+
console.log(` ${DIM}npx @nons-dev/uikit search table${RESET}\n`)
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// ── Main ──
|
|
513
|
+
|
|
514
|
+
const command = process.argv[2]
|
|
515
|
+
const param = process.argv[3]
|
|
516
|
+
|
|
517
|
+
switch (command) {
|
|
518
|
+
case 'init': cmdInit(); break
|
|
519
|
+
case 'generate': cmdGenerate(); break
|
|
520
|
+
case 'list': cmdList(); break
|
|
521
|
+
case 'show': param ? cmdShow(param) : (console.error(`${RED}✗${RESET} Missing component name.`), process.exit(1)); break
|
|
522
|
+
case 'schema': param ? cmdSchema(param) : (console.error(`${RED}✗${RESET} Missing component name.`), process.exit(1)); break
|
|
523
|
+
case 'search': param ? cmdSearch(param) : (console.error(`${RED}✗${RESET} Missing search query.`), process.exit(1)); break
|
|
524
|
+
case '--help': case '-h': case 'help': default: printHelp(); break
|
|
525
|
+
}
|