@nons-dev/uikit 0.1.3 → 0.1.5

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/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@nons-dev/uikit",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
+ "license": "BUSL-1.1",
4
5
  "type": "module",
5
6
  "main": "./dist/index.js",
6
7
  "module": "./dist/index.js",
@@ -13,9 +14,14 @@
13
14
  "registry": "https://registry.npmjs.org/",
14
15
  "access": "public"
15
16
  },
17
+ "bin": {
18
+ "nons": "scripts/cli.mjs"
19
+ },
16
20
  "files": [
17
21
  "dist",
18
- "README.md"
22
+ "generated/components.json",
23
+ "scripts/cli.mjs",
24
+ "readme.md"
19
25
  ],
20
26
  "exports": {
21
27
  ".": {
@@ -30,7 +36,7 @@
30
36
  },
31
37
  "scripts": {
32
38
  "dev": "vite",
33
- "build": "npm run generate && vue-tsc -b && vite build",
39
+ "build": "npm run generate && vue-tsc -b && vite build && node scripts/copy-fonts.mjs",
34
40
  "preview": "vite preview",
35
41
  "generate": "tsx scripts/generate.ts"
36
42
  },
package/readme.md CHANGED
@@ -5,32 +5,80 @@ Schema-driven UI component library for the Nons platform — Vue 3, TypeScript,
5
5
  ## Architecture
6
6
 
7
7
  ```
8
- core/ Schema types, component registry, schema renderer, runtime context
9
- ui/ Vue 3 components (primitives, composites, layouts, sections)
10
- design-system/ CSS custom property tokens, reset, light/dark themes
11
- playground/ Demo/explorer app for component development and testing
12
- plugins/ i18n, direction, permissions
13
- adapters/ Data provider layer (mock API, storage abstraction)
14
- locales/ Persian (fa) and English (en) translation files
15
- docs/ Component contracts, style guide, reference
8
+ nons.config.yaml Single source of truth for theme, tokens, locale, direction, font
9
+ core/ Schema types, component registry, schema renderer, runtime context
10
+ ui/ Vue 3 components (primitives, composites, layouts, sections)
11
+ generated/ Auto-generated files from nons.config.yaml (tokens.css, theme.css, config.ts, locales/)
12
+ design-system/ CSS reset, global styles (imports generated/ tokens)
13
+ playground/ Demo/explorer app for component development and testing
14
+ plugins/ i18n, direction, permissions
15
+ adapters/ Data provider layer (mock API, storage abstraction)
16
+ locales/ Persian (fa) and English (en) translation YAML files
17
+ scripts/ CLI tools (generate, init, component discovery)
16
18
  ```
17
19
 
18
20
  ## Quick Start
19
21
 
22
+ ```bash
23
+ # Install
24
+ npm install @nons-dev/uikit
25
+
26
+ # Generate config file in your project root
27
+ npx @nons-dev/uikit init
28
+
29
+ # Edit nons.config.yaml to customize theme, tokens, locale, direction, font
30
+
31
+ # Use in your app
32
+ import { bootstrapRegistry, loadConfig } from '@nons-dev/uikit'
33
+ import '@nons-dev/uikit/style.css'
34
+ ```
35
+
36
+ ### Development (UI Kit only)
37
+
20
38
  ```bash
21
39
  npm install
22
- npm run dev # Start Vite dev server
23
- npm run build # Type-check + production build (vue-tsc -b && vite build)
24
- npm run preview # Preview production build
40
+ npm run dev # Start Vite dev server (playground app)
41
+ npm run build # Generate tokens + type-check + library build
42
+ npm run generate # Regenerate CSS/theme from nons.config.yaml
43
+ npm run preview # Preview production playground build
44
+ ```
45
+
46
+ ## Config System
47
+
48
+ The `nons.config.yaml` file at your project root is the **single source of truth**:
49
+
50
+ ```yaml
51
+ theme:
52
+ preset: nons # theme preset (nons, custom)
53
+
54
+ brand:
55
+ name: Nons # brand name
56
+
57
+ direction:
58
+ default: rtl # UI direction (rtl, ltr)
59
+
60
+ locale:
61
+ default: fa # default language
62
+ supported: [fa, en] # supported languages
63
+
64
+ font:
65
+ fa:
66
+ family: IRANSansX
67
+ en:
68
+ family: Satoshi
25
69
  ```
26
70
 
71
+ Run `npx @nons-dev/uikit generate` to regenerate CSS variables from your config.
72
+
27
73
  ## CLI Commands
28
74
 
29
75
  ```bash
30
- npm run ui -- components:list # List all discovered UI components
31
- npm run ui -- component:show <name> # Show full detail for a component
32
- npm run ui -- schema <name> # Show schema JSON for a component
33
- npm run ui -- components:search <q> # Search components by name/description
76
+ npx @nons-dev/uikit init # Create nons.config.yaml in project root
77
+ npx @nons-dev/uikit generate # Generate CSS from nons.config.yaml
78
+ npx @nons-dev/uikit list # List all components
79
+ npx @nons-dev/uikit show <name> # Show component details (props, events, slots)
80
+ npx @nons-dev/uikit schema <name> # Show schema JSON for a component
81
+ npx @nons-dev/uikit search <query> # Search components by name or description
34
82
  ```
35
83
 
36
84
  ## Documentation
@@ -43,8 +91,9 @@ npm run ui -- components:search <q> # Search components by name/description
43
91
  ## Key Conventions
44
92
 
45
93
  - **Zero-hardcoding** — all visual values via CSS custom properties (`var(--...)`)
94
+ - **Config-driven** — theme, tokens, direction, locale all from `nons.config.yaml`
46
95
  - **RTL-first** — `<html dir="rtl">`, logical properties (`inset-inline-start`, `margin-inline`)
47
- - **Schema-driven** — every component has a registry key, contract, and schema example
96
+ - **Schema-driven** — every component has a registry key, contract, schema example, and auto-generated metadata (53 components parsed from source)
48
97
  - **modelValue** — all form inputs use `modelValue` prop + `update:modelValue` emit
49
98
  - **No external margins** — component spacing via parent layout `gap`
50
99
  - **Flat UI** — no elevation shadows; separation via themed borders (`--border-primary`)
@@ -56,3 +105,4 @@ npm run ui -- components:search <q> # Search components by name/description
56
105
  - `@lucide/vue` icons
57
106
  - Tabler Icons (CDN)
58
107
  - Plain CSS only (no SCSS/Sass)
108
+ - Font files bundled in `dist/font/` (IRANSansX, Satoshi) — loaded via `@font-face` in generated CSS
@@ -0,0 +1,256 @@
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 WHITE = '\x1b[37m'
20
+ const GRAY = '\x1b[90m'
21
+ const RED = '\x1b[31m'
22
+ const BLUE = '\x1b[34m'
23
+
24
+ const DEFAULT_CONFIG = `theme:
25
+ preset: nons
26
+
27
+ brand:
28
+ name: Nons
29
+
30
+ direction:
31
+ default: rtl
32
+
33
+ locale:
34
+ default: fa
35
+ supported:
36
+ - fa
37
+ - en
38
+
39
+ font:
40
+ fa:
41
+ family: IRANSansX
42
+ en:
43
+ family: Satoshi
44
+ `
45
+
46
+ // ── Data ──
47
+
48
+ function loadComponents() {
49
+ if (!fs.existsSync(COMPONENTS_JSON)) {
50
+ console.error(`${RED}✗${RESET} Component metadata not found.`)
51
+ console.error(` Expected: ${COMPONENTS_JSON}`)
52
+ process.exit(1)
53
+ }
54
+ return JSON.parse(fs.readFileSync(COMPONENTS_JSON, 'utf-8'))
55
+ }
56
+
57
+ function findComponent(data, query) {
58
+ const lower = query.toLowerCase()
59
+ return data.components.find(c =>
60
+ c.name.toLowerCase() === lower || c.registryKey === lower
61
+ )
62
+ }
63
+
64
+ function searchComponents(data, query) {
65
+ const lower = query.toLowerCase()
66
+ return data.components.filter(c =>
67
+ c.name.toLowerCase().includes(lower) ||
68
+ c.registryKey.includes(lower) ||
69
+ (c.description || '').toLowerCase().includes(lower)
70
+ )
71
+ }
72
+
73
+ // ── Commands ──
74
+
75
+ function cmdInit() {
76
+ const dest = path.join(CWD, 'nons.config.yaml')
77
+ if (fs.existsSync(dest)) {
78
+ console.log(`${YELLOW}⚠${RESET} nons.config.yaml already exists — skipping.`)
79
+ return
80
+ }
81
+ fs.writeFileSync(dest, DEFAULT_CONFIG, 'utf-8')
82
+ console.log(`${GREEN}✓${RESET} nons.config.yaml created in`, CWD)
83
+ }
84
+
85
+ function cmdGenerate() {
86
+ const configPath = path.join(CWD, 'nons.config.yaml')
87
+ if (!fs.existsSync(configPath)) {
88
+ console.error(`${RED}✗${RESET} nons.config.yaml not found. Run \`nons init\` first.`)
89
+ process.exit(1)
90
+ }
91
+ const raw = fs.readFileSync(configPath, 'utf-8')
92
+ const config = yaml.load(raw)
93
+ const generatedDir = path.join(CWD, 'generated')
94
+ if (!fs.existsSync(generatedDir)) fs.mkdirSync(generatedDir, { recursive: true })
95
+
96
+ const lines = [':root {']
97
+ const sections = ['colors', 'spacing', 'typography', 'radius', 'z-index', 'opacity', 'button', 'alert']
98
+ for (const section of sections) {
99
+ const tokens = config.tokens?.[section]
100
+ if (!tokens) continue
101
+ for (const [key, val] of Object.entries(tokens)) lines.push(` ${key}: ${val};`)
102
+ }
103
+ lines.push('}')
104
+ fs.writeFileSync(path.join(generatedDir, 'tokens.css'), lines.join('\n'), 'utf-8')
105
+ console.log(`${GREEN}✓${RESET} generated/tokens.css`)
106
+ }
107
+
108
+ function cmdList() {
109
+ const data = loadComponents()
110
+ const grouped = {}
111
+ for (const c of data.components) {
112
+ if (!grouped[c.category]) grouped[c.category] = []
113
+ grouped[c.category].push(c)
114
+ }
115
+
116
+ console.log(`\n${BOLD}${CYAN}Components (${data.components.length})${RESET}\n`)
117
+ const order = ['primitive', 'layout', 'component', 'section']
118
+ const labels = { primitive: 'Primitive', layout: 'Layout', component: 'Component', section: 'Section' }
119
+ const colors = { primitive: GREEN, layout: BLUE, component: CYAN, section: MAGENTA }
120
+
121
+ for (const cat of order) {
122
+ const items = grouped[cat]
123
+ if (!items || !items.length) continue
124
+ const color = colors[cat]
125
+ console.log(` ${color}${BOLD}── ${labels[cat]} (${items.length})${RESET}\n`)
126
+ for (const c of items) {
127
+ const desc = c.description ? ` ${GRAY}${c.description.substring(0, 50)}${c.description.length > 50 ? '…' : ''}${RESET}` : ''
128
+ console.log(` ${BOLD}${c.name}${RESET} ${DIM}[${c.registryKey}]${RESET}${desc}`)
129
+ }
130
+ console.log('')
131
+ }
132
+ }
133
+
134
+ function cmdShow(name) {
135
+ const data = loadComponents()
136
+ const comp = findComponent(data, name)
137
+ if (!comp) {
138
+ console.log(`\n ${RED}✗ Component "${name}" not found.${RESET}\n`)
139
+ const similar = searchComponents(data, name).filter(c => c.name !== name)
140
+ if (similar.length) {
141
+ console.log(` ${YELLOW}Did you mean:${RESET}`)
142
+ for (const s of similar) console.log(` • ${s.name} ${DIM}[${s.registryKey}]${RESET}`)
143
+ console.log('')
144
+ }
145
+ process.exit(1)
146
+ }
147
+
148
+ const color = { primitive: GREEN, layout: BLUE, component: CYAN, section: MAGENTA }[comp.category] || CYAN
149
+ const label = { primitive: 'Primitive', layout: 'Layout', component: 'Component', section: 'Section' }[comp.category] || comp.category
150
+
151
+ console.log(`\n${BOLD}${CYAN}${comp.name}${RESET}\n`)
152
+ console.log(` ${DIM}Category:${RESET} ${color}${BOLD}${label}${RESET}`)
153
+ console.log(` ${DIM}Registry Key:${RESET} ${comp.registryKey}`)
154
+ console.log(` ${DIM}File:${RESET} ${comp.filePath}`)
155
+ if (comp.description) console.log(`\n ${comp.description}\n`)
156
+
157
+ if (comp.props.length) {
158
+ console.log(` ${BOLD}Props:${RESET}\n`)
159
+ for (const p of comp.props) {
160
+ const req = p.required ? `${YELLOW}required${RESET}` : `${DIM}optional${RESET}`
161
+ console.log(` ${BOLD}${p.name}${RESET} ${req}`)
162
+ console.log(` ${DIM}type:${RESET} ${CYAN}${p.type}${RESET}`)
163
+ if (p.allowedValues?.length) console.log(` ${DIM}allowed:${RESET} ${p.allowedValues.map(v => `${GREEN}'${v}'${RESET}`).join(' | ')}`)
164
+ if (p.default !== undefined) console.log(` ${DIM}default:${RESET} ${MAGENTA}${p.default}${RESET}`)
165
+ if (p.description) console.log(` ${DIM}desc:${RESET} ${p.description}`)
166
+ console.log('')
167
+ }
168
+ }
169
+
170
+ if (comp.events.length) {
171
+ console.log(` ${BOLD}Events:${RESET}\n`)
172
+ for (const e of comp.events) {
173
+ const payload = e.payload ? ` ${DIM}→ ${e.payload}${RESET}` : ''
174
+ console.log(` ${YELLOW}@${e.name}${RESET}${payload}`)
175
+ }
176
+ console.log('')
177
+ }
178
+
179
+ if (comp.slots.length) {
180
+ console.log(` ${BOLD}Slots:${RESET}\n`)
181
+ for (const s of comp.slots) console.log(` ${MAGENTA}#${s.name}${RESET}`)
182
+ console.log('')
183
+ }
184
+
185
+ if (comp.schemaExample) {
186
+ console.log(` ${BOLD}Schema Example:${RESET}\n`)
187
+ for (const line of comp.schemaExample.split('\n')) console.log(` ${GRAY}${line}${RESET}`)
188
+ console.log('')
189
+ }
190
+ }
191
+
192
+ function cmdSchema(name) {
193
+ const data = loadComponents()
194
+ const comp = findComponent(data, name)
195
+ if (!comp) {
196
+ console.log(`\n ${RED}✗ Component "${name}" not found.${RESET}\n`)
197
+ process.exit(1)
198
+ }
199
+ console.log(`\n${BOLD}${CYAN}Schema: ${comp.name}${RESET}\n`)
200
+ console.log(comp.schemaExample || JSON.stringify({ type: comp.registryKey, props: {} }, null, 2))
201
+ console.log('')
202
+ }
203
+
204
+ function cmdSearch(query) {
205
+ const data = loadComponents()
206
+ const matches = searchComponents(data, query)
207
+ if (!matches.length) {
208
+ console.log(`\n ${YELLOW}No components matched "${query}"${RESET}\n`)
209
+ return
210
+ }
211
+ const colors = { primitive: GREEN, layout: BLUE, component: CYAN, section: MAGENTA }
212
+ const labels = { primitive: 'Primitive', layout: 'Layout', component: 'Component', section: 'Section' }
213
+ console.log(`\n${BOLD}${CYAN}Search: "${query}"${RESET} ${DIM}(${matches.length} found)${RESET}\n`)
214
+ for (const c of matches) {
215
+ const color = colors[c.category] || CYAN
216
+ console.log(` ${BOLD}${c.name}${RESET} ${color}${labels[c.category] || c.category}${RESET} ${DIM}[${c.registryKey}]${RESET}`)
217
+ if (c.description) console.log(` ${GRAY}${c.description.substring(0, 70)}${c.description.length > 70 ? '…' : ''}${RESET}`)
218
+ }
219
+ console.log('')
220
+ }
221
+
222
+ // ── Help ──
223
+
224
+ function printHelp() {
225
+ console.log(`\n${BOLD}${CYAN}Nons UIKit CLI${RESET}\n`)
226
+ console.log(`${BOLD}Commands:${RESET}\n`)
227
+ console.log(` ${GREEN}init${RESET} Create nons.config.yaml in current directory`)
228
+ console.log(` ${GREEN}generate${RESET} Generate CSS from nons.config.yaml`)
229
+ console.log(` ${GREEN}list${RESET} List all components`)
230
+ console.log(` ${GREEN}show${RESET} ${DIM}<name>${RESET} Show full detail for a component`)
231
+ console.log(` ${GREEN}schema${RESET} ${DIM}<name>${RESET} Show schema JSON for a component`)
232
+ console.log(` ${GREEN}search${RESET} ${DIM}<query>${RESET} Search components by name/description`)
233
+ console.log(` ${GREEN}help${RESET} Show this help\n`)
234
+ console.log(`${BOLD}Usage:${RESET}\n`)
235
+ console.log(` ${DIM}npx @nons-dev/uikit list${RESET}`)
236
+ console.log(` ${DIM}npx @nons-dev/uikit show Button${RESET}`)
237
+ console.log(` ${DIM}npx @nons-dev/uikit schema Input${RESET}`)
238
+ console.log(` ${DIM}npx @nons-dev/uikit search table${RESET}`)
239
+ console.log(` ${DIM}npx @nons-dev/uikit init${RESET}`)
240
+ console.log(` ${DIM}npx @nons-dev/uikit generate${RESET}\n`)
241
+ }
242
+
243
+ // ── Main ──
244
+
245
+ const command = process.argv[2]
246
+ const param = process.argv[3]
247
+
248
+ switch (command) {
249
+ case 'init': cmdInit(); break
250
+ case 'generate': cmdGenerate(); break
251
+ case 'list': cmdList(); break
252
+ case 'show': param ? cmdShow(param) : (console.error(`${RED}✗${RESET} Missing component name.`), process.exit(1)); break
253
+ case 'schema': param ? cmdSchema(param) : (console.error(`${RED}✗${RESET} Missing component name.`), process.exit(1)); break
254
+ case 'search': param ? cmdSearch(param) : (console.error(`${RED}✗${RESET} Missing search query.`), process.exit(1)); break
255
+ case '--help': case '-h': case 'help': default: printHelp(); break
256
+ }