@mindfiredigital/ignix-lite-cli 1.1.0
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/.turbo/turbo-build.log +13 -0
- package/CHANGELOG.md +13 -0
- package/LICENSE +21 -0
- package/README.md +368 -0
- package/dist/index.js +828 -0
- package/package.json +35 -0
- package/src/commands/add.ts +146 -0
- package/src/commands/build.ts +66 -0
- package/src/commands/check-a11y.ts +61 -0
- package/src/commands/info.ts +66 -0
- package/src/commands/init.ts +91 -0
- package/src/commands/list.ts +22 -0
- package/src/commands/mcp.ts +233 -0
- package/src/commands/preview.ts +79 -0
- package/src/commands/theme.ts +96 -0
- package/src/commands/validate.ts +49 -0
- package/src/index.ts +91 -0
- package/tsconfig.json +15 -0
- package/tsup.config.ts +11 -0
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mindfiredigital/ignix-lite-cli",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "CLI tool for Ignix-Lite project scaffolding, validation, and theming",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"ignix-lite": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"commander": "^13.1.0",
|
|
12
|
+
"node-html-parser": "^7.1.0",
|
|
13
|
+
"ora": "^9.4.0",
|
|
14
|
+
"picocolors": "^1.1.1",
|
|
15
|
+
"prompts": "^2.4.2",
|
|
16
|
+
"zod": "^3.24.2",
|
|
17
|
+
"@mindfiredigital/ignix-lite-engine": "1.1.0",
|
|
18
|
+
"@mindfiredigital/ignix-lite-mcp": "1.3.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^25.8.0",
|
|
22
|
+
"@types/prompts": "^2.4.9",
|
|
23
|
+
"tsup": "^8.5.1",
|
|
24
|
+
"typescript": "^5.4.5"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsup",
|
|
31
|
+
"dev": "tsup --watch",
|
|
32
|
+
"lint": "eslint . --no-error-on-unmatched-pattern",
|
|
33
|
+
"format": "prettier --write ."
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import pc from 'picocolors'
|
|
2
|
+
|
|
3
|
+
const COMPONENT_TEMPLATES: Record<string, string> = {
|
|
4
|
+
accordion: `<details>
|
|
5
|
+
<summary>Accordion Title</summary>
|
|
6
|
+
<p>Accordion contents goes here...</p>
|
|
7
|
+
</details>`,
|
|
8
|
+
alert: `<aside data-intent="info">
|
|
9
|
+
<strong>Head's up!</strong>
|
|
10
|
+
<span>This is a semantic alert component</span>
|
|
11
|
+
</aside>`,
|
|
12
|
+
avatar: `<span data-size="md">JD</span>`,
|
|
13
|
+
badge: `<mark data-intent="success">Saved</mark>`,
|
|
14
|
+
breadcrumb: `<nav aria-label="Breadcrumb">
|
|
15
|
+
<ol>
|
|
16
|
+
<li><a href="/">Home</a></li>
|
|
17
|
+
<li><a href="/docs" aria-current="page">Docs</a></li>
|
|
18
|
+
</ol>
|
|
19
|
+
</nav>`,
|
|
20
|
+
button: `<button data-intent="primary">Save</button>`,
|
|
21
|
+
card: `<article>
|
|
22
|
+
<h2 slot="title">Card Title</h2>
|
|
23
|
+
<p slot="body">
|
|
24
|
+
This is the main card body content.
|
|
25
|
+
</p>
|
|
26
|
+
</article>`,
|
|
27
|
+
checkbox: `<label>
|
|
28
|
+
<input type="checkbox" />
|
|
29
|
+
Remember me
|
|
30
|
+
</label>`,
|
|
31
|
+
codeblock: `<pre><code data-lang="typescript">const greet = () => "Hello World";</code></pre>`,
|
|
32
|
+
combobox: `<ix-combobox>
|
|
33
|
+
<label slot="control">
|
|
34
|
+
Search
|
|
35
|
+
<input type="text" placeholder="Search options..." />
|
|
36
|
+
</label>
|
|
37
|
+
<ul hidden>
|
|
38
|
+
<li data-value="1">Option A</li>
|
|
39
|
+
<li data-value="2">Option B</li>
|
|
40
|
+
</ul>
|
|
41
|
+
</ix-combobox>`,
|
|
42
|
+
dialog: `<dialog id="confirm-modal" data-intent="danger">
|
|
43
|
+
<h2>Confirm Action</h2>
|
|
44
|
+
<p>Are you sure you want to proceed?</p>
|
|
45
|
+
<button>Cancel</button>
|
|
46
|
+
<button>Delete</button>
|
|
47
|
+
</dialog>`,
|
|
48
|
+
divider: `<hr />`,
|
|
49
|
+
dropdown: `<ix-dropdown>
|
|
50
|
+
<button slot="trigger">Options ā¾</button>
|
|
51
|
+
<ul slot="menu" hidden>
|
|
52
|
+
<li><button>Profile</button></li>
|
|
53
|
+
<li><button>Settings</button></li>
|
|
54
|
+
</ul>
|
|
55
|
+
</ix-dropdown>`,
|
|
56
|
+
form: `<form>
|
|
57
|
+
<label>
|
|
58
|
+
Name
|
|
59
|
+
<input type="text" required />
|
|
60
|
+
</label>
|
|
61
|
+
<button type="submit" data-intent="primary">Submit</button>
|
|
62
|
+
</form>`,
|
|
63
|
+
grid: `<section data-grid="3">
|
|
64
|
+
<section>Column 1</section>
|
|
65
|
+
<section>Column 2</section>
|
|
66
|
+
<section>Column 3</section>
|
|
67
|
+
</section>`,
|
|
68
|
+
|
|
69
|
+
input: `<label>
|
|
70
|
+
Username
|
|
71
|
+
<input type="text" required placeholder="Enter Username..." />
|
|
72
|
+
</label>`,
|
|
73
|
+
meter: `<meter value="70" min="0" max="100" low="30" high="70" optimum="100"></meter>`,
|
|
74
|
+
navigation: `<nav aria-label="Main Navigation">
|
|
75
|
+
<ul>
|
|
76
|
+
<li><a href="/" aria-current="page">Home</a></li>
|
|
77
|
+
<li><a href="/docs">Docs</a></li>
|
|
78
|
+
<li><a href="/blog">Blog</a></li>
|
|
79
|
+
</ul>
|
|
80
|
+
</nav>`,
|
|
81
|
+
progress: `<progress value="50" max="100"></progress>`,
|
|
82
|
+
radio: `<label>
|
|
83
|
+
<input type="radio" name="options" value="a" />
|
|
84
|
+
Option A
|
|
85
|
+
</label>`,
|
|
86
|
+
select: `<label>
|
|
87
|
+
Options
|
|
88
|
+
<select data-intent="primary">
|
|
89
|
+
<option value="1">Option 1</option>
|
|
90
|
+
<option value="2">Option 2</option>
|
|
91
|
+
</select>
|
|
92
|
+
</label>`,
|
|
93
|
+
skeleton: `<span aria-busy="true" data-shape="text"></span>`,
|
|
94
|
+
tabs: `<ix-tabs>
|
|
95
|
+
<button slot="tab" aria-selected="true">Tab 1</button>
|
|
96
|
+
<button slot="tab">Tab 2</button>
|
|
97
|
+
<section slot="panel">Content for Tab 1</section>
|
|
98
|
+
<section slot="panel" hidden>Content for Tab 2</section>
|
|
99
|
+
</ix-tabs>`,
|
|
100
|
+
table: `<table>
|
|
101
|
+
<thead>
|
|
102
|
+
<tr>
|
|
103
|
+
<th>Name</th>
|
|
104
|
+
<th>Role</th>
|
|
105
|
+
</tr>
|
|
106
|
+
</thead>
|
|
107
|
+
<tbody>
|
|
108
|
+
<tr>
|
|
109
|
+
<td>Alice</td>
|
|
110
|
+
<td>Developer</td>
|
|
111
|
+
</tr>
|
|
112
|
+
</tbody>
|
|
113
|
+
</table>`,
|
|
114
|
+
textarea: `<label>
|
|
115
|
+
Message
|
|
116
|
+
<textarea data-intent="primary" placeholder="Enter your message..."></textarea>
|
|
117
|
+
</label>`,
|
|
118
|
+
toast: `<ix-toast>
|
|
119
|
+
<aside data-intent="success" data-open="true">
|
|
120
|
+
<strong>Success!</strong>
|
|
121
|
+
<span>Action completed.</span>
|
|
122
|
+
</aside>
|
|
123
|
+
</ix-toast>`,
|
|
124
|
+
tooltip: `<ix-tooltip content="Helpful information">
|
|
125
|
+
<span>Hover me</span>
|
|
126
|
+
</ix-tooltip>`
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export async function addCommand(component: string) {
|
|
130
|
+
const normalized = component.toLowerCase().trim()
|
|
131
|
+
const template = COMPONENT_TEMPLATES[normalized]
|
|
132
|
+
|
|
133
|
+
if (!template) {
|
|
134
|
+
console.log(pc.red(`Component "${component}" not found.`))
|
|
135
|
+
|
|
136
|
+
console.log(
|
|
137
|
+
pc.yellow(
|
|
138
|
+
`Available components : ${Object.keys(COMPONENT_TEMPLATES).join(', ')}`
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
console.log(pc.cyan(`\nTemplate for <${component}>:\n`))
|
|
144
|
+
console.log(template)
|
|
145
|
+
console.log('\n')
|
|
146
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { writeFileSync } from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import pc from 'picocolors'
|
|
4
|
+
import { howToBuild } from '@mindfiredigital/ignix-lite-engine'
|
|
5
|
+
|
|
6
|
+
export async function buildCommand(
|
|
7
|
+
prompt: string,
|
|
8
|
+
options: {
|
|
9
|
+
output?: string
|
|
10
|
+
emmetOnly?: boolean
|
|
11
|
+
}
|
|
12
|
+
) {
|
|
13
|
+
const response = await howToBuild(prompt)
|
|
14
|
+
if (!response.content || response.content.length === 0) {
|
|
15
|
+
console.log(
|
|
16
|
+
pc.red('Error: Intent synthesis engine returned an empty response.')
|
|
17
|
+
)
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let data: {
|
|
22
|
+
source: string
|
|
23
|
+
suggestion?: string
|
|
24
|
+
emmet?: string
|
|
25
|
+
html?: string
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
data = JSON.parse(response.content[0].text)
|
|
29
|
+
} catch {
|
|
30
|
+
console.log(
|
|
31
|
+
pc.red('Error: Failed to parse response from intent synthesis engine.')
|
|
32
|
+
)
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!data || data.source === 'no-match') {
|
|
37
|
+
console.log(pc.red(`\nCould not synthesize UI for: "${prompt}"`))
|
|
38
|
+
console.log(pc.yellow(`Suggestion: ${data?.suggestion || 'Try clarifying your request.'}\n`))
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const outputContent = options.emmetOnly ? data.emmet : data.html
|
|
43
|
+
if (!outputContent) {
|
|
44
|
+
console.log(pc.red('Error: Response does not contain any synthesized code.'))
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (options.output) {
|
|
49
|
+
try {
|
|
50
|
+
const absolutePath = path.resolve(process.cwd(), options.output)
|
|
51
|
+
writeFileSync(absolutePath, outputContent, 'utf8')
|
|
52
|
+
console.log(
|
|
53
|
+
pc.green(`\nSuccessfully wrote output to ${pc.blue(options.output)}\n`)
|
|
54
|
+
)
|
|
55
|
+
} catch (err: unknown) {
|
|
56
|
+
const msg = err instanceof Error ? err.message : String(err)
|
|
57
|
+
console.log(
|
|
58
|
+
pc.red(`Error: Failed to write output to file. ${msg}`)
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
console.log(pc.cyan('\nSynthesized Output:'))
|
|
63
|
+
console.log(outputContent)
|
|
64
|
+
console.log()
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import pc from 'picocolors'
|
|
4
|
+
import { auditA11y } from '@mindfiredigital/ignix-lite-engine'
|
|
5
|
+
|
|
6
|
+
export async function checkA11yCommand(filePath: string) {
|
|
7
|
+
const absolutePath = path.resolve(process.cwd(), filePath)
|
|
8
|
+
|
|
9
|
+
if (!existsSync(absolutePath)) {
|
|
10
|
+
console.log(pc.red(`Error: File not found at ${filePath}`))
|
|
11
|
+
process.exit(1)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const html = readFileSync(absolutePath, 'utf8')
|
|
15
|
+
const result = auditA11y(html)
|
|
16
|
+
|
|
17
|
+
console.log(pc.bold(pc.cyan(`\nāæ Accessibility Audit Report`)))
|
|
18
|
+
console.log(pc.gray('ā'.repeat(60)))
|
|
19
|
+
console.log(`${pc.bold('File:')} ${pc.blue(filePath)}`)
|
|
20
|
+
console.log(`${pc.bold('Standards:')} WCAG 2.2 ${pc.bold(result.wcag)}`)
|
|
21
|
+
console.log(
|
|
22
|
+
`${pc.bold('Summary:')} Passed ${result.passes.length} rules, Found ${result.issues.length} issue(s)`
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
if (result.issues.length === 0) {
|
|
26
|
+
console.log(
|
|
27
|
+
pc.bold(
|
|
28
|
+
pc.green(`\nā PASS: No accessibility issues found! Score: 100/100`)
|
|
29
|
+
)
|
|
30
|
+
)
|
|
31
|
+
} else {
|
|
32
|
+
console.log(
|
|
33
|
+
pc.bold(
|
|
34
|
+
pc.red(
|
|
35
|
+
`\nā FAIL: Accessibility check failed. Score: ${result.score}/100\n`
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
result.issues.forEach((issue, idx) => {
|
|
41
|
+
const isError = issue.type === 'error'
|
|
42
|
+
const labelColor = isError ? pc.red : pc.yellow
|
|
43
|
+
const borderChar = isError ? 'ā' : 'ā '
|
|
44
|
+
|
|
45
|
+
console.log(
|
|
46
|
+
pc.bold(
|
|
47
|
+
labelColor(
|
|
48
|
+
`[Issue ${idx + 1}] ${borderChar} ${issue.rule} (${issue.type.toUpperCase()})`
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
)
|
|
52
|
+
console.log(` ${pc.bold('Element:')} ${issue.element}`)
|
|
53
|
+
console.log(` ${pc.bold('Message:')} ${issue.message}`)
|
|
54
|
+
if (issue.fix) {
|
|
55
|
+
console.log(` ${pc.bold('Fix:')} ${pc.green(issue.fix)}`)
|
|
56
|
+
}
|
|
57
|
+
console.log()
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
console.log()
|
|
61
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import pc from 'picocolors'
|
|
2
|
+
import { manifests } from '@mindfiredigital/ignix-lite-engine'
|
|
3
|
+
|
|
4
|
+
export function infoCommand(component: string) {
|
|
5
|
+
const name = component.toLowerCase().trim()
|
|
6
|
+
const manifest = manifests[name]
|
|
7
|
+
|
|
8
|
+
if (!manifest) {
|
|
9
|
+
console.log(pc.red(`\nError: Component "${component}" does not exist.`))
|
|
10
|
+
console.log(
|
|
11
|
+
`Run ${pc.yellow('ignix-lite list')} to see all available components.\n`
|
|
12
|
+
)
|
|
13
|
+
process.exit(1)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
console.log(pc.bold(pc.cyan(`\nš¦ Component: <${name.toUpperCase()}>`)))
|
|
17
|
+
console.log(pc.gray('ā'.repeat(60)))
|
|
18
|
+
console.log(`${pc.bold('Description:')} ${manifest.description}`)
|
|
19
|
+
console.log(
|
|
20
|
+
`${pc.bold('HTML Wrapper:')} ${pc.yellow(`<${manifest.element}>`)}`
|
|
21
|
+
)
|
|
22
|
+
console.log(`${pc.bold('Default Emmet:')} ${pc.green(manifest.emmet)}`)
|
|
23
|
+
|
|
24
|
+
if (manifest.props && Object.keys(manifest.props).length > 0) {
|
|
25
|
+
console.log(pc.bold(pc.blue('\nā Attributes & Options:')))
|
|
26
|
+
console.log(pc.gray(' ' + 'ā'.repeat(56)))
|
|
27
|
+
Object.entries(manifest.props).forEach(([propName, propDef]) => {
|
|
28
|
+
const typeStr = pc.magenta(propDef.type)
|
|
29
|
+
const valuesStr = propDef.values
|
|
30
|
+
? pc.dim(` [${propDef.values.join(', ')}]`)
|
|
31
|
+
: ''
|
|
32
|
+
const defaultStr =
|
|
33
|
+
propDef.default !== undefined
|
|
34
|
+
? ` (default: ${pc.bold(String(propDef.default))})`
|
|
35
|
+
: ''
|
|
36
|
+
console.log(
|
|
37
|
+
` ${pc.cyan('ā¢')} ${pc.bold(propName.padEnd(15))} ${typeStr}${valuesStr}${defaultStr}`
|
|
38
|
+
)
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (manifest.slots && Object.keys(manifest.slots).length > 0) {
|
|
43
|
+
console.log(pc.bold(pc.blue('\nš„ Slots:')))
|
|
44
|
+
console.log(pc.gray(' ' + 'ā'.repeat(56)))
|
|
45
|
+
Object.entries(manifest.slots).forEach(([slotName, slotDef]) => {
|
|
46
|
+
const elementsStr = slotDef.element
|
|
47
|
+
? pc.dim(` (tags: ${slotDef.element.join(', ')})`)
|
|
48
|
+
: ''
|
|
49
|
+
const requiredStr = slotDef.required ? pc.bold(pc.red(' (required)')) : ''
|
|
50
|
+
console.log(
|
|
51
|
+
` ${pc.cyan('ā¢')} ${pc.bold(slotName.padEnd(15))} ${requiredStr}${elementsStr}`
|
|
52
|
+
)
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (manifest.do && manifest.do.length > 0) {
|
|
57
|
+
console.log(pc.bold(pc.green('\nā Best Practices (Do):')))
|
|
58
|
+
manifest.do.forEach((rule) => console.log(` ${pc.green('+')} ${rule}`))
|
|
59
|
+
}
|
|
60
|
+
if (manifest.dont && manifest.dont.length > 0) {
|
|
61
|
+
console.log(pc.bold(pc.red("\nā Avoid (Don't):")))
|
|
62
|
+
manifest.dont.forEach((rule) => console.log(` ${pc.red('-')} ${rule}`))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log()
|
|
66
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import prompts from 'prompts'
|
|
4
|
+
import pc from 'picocolors'
|
|
5
|
+
import ora from 'ora'
|
|
6
|
+
|
|
7
|
+
export async function initCommand() {
|
|
8
|
+
console.log(
|
|
9
|
+
pc.cyan("Welcome to Ignix-Lite! Let's get your project set up.\n")
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
const response = await prompts([
|
|
13
|
+
{
|
|
14
|
+
type: 'select',
|
|
15
|
+
name: 'framework',
|
|
16
|
+
message: 'Which framework are you using?',
|
|
17
|
+
choices: [
|
|
18
|
+
{ title: 'React', value: 'react' },
|
|
19
|
+
{ title: 'Vue', value: 'vue' },
|
|
20
|
+
{ title: 'Svelte', value: 'svelte' },
|
|
21
|
+
{ title: 'Vanilla HTML', value: 'vanilla' }
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
type: 'text',
|
|
26
|
+
name: 'stylePath',
|
|
27
|
+
message:
|
|
28
|
+
'Path to your main CSS file (where Ignix-Lite variables will be added):',
|
|
29
|
+
initial: 'src/index.css'
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
type: 'text',
|
|
33
|
+
name: 'primaryColor',
|
|
34
|
+
message: 'Default primary theme color (hex):',
|
|
35
|
+
initial: '#6366f1',
|
|
36
|
+
validate: (value: string) => {
|
|
37
|
+
const hexRegex = /^#([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6})$/
|
|
38
|
+
return hexRegex.test(value)
|
|
39
|
+
? true
|
|
40
|
+
: 'Please enter a valid hex color code (e.g. #6366f1 or #fff)'
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
])
|
|
44
|
+
|
|
45
|
+
if (!response.framework || !response.stylePath) {
|
|
46
|
+
console.log(pc.red('\nSetup cancelled.'))
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const spinner = ora('Initializing project...').start()
|
|
51
|
+
|
|
52
|
+
// 1. Create ignix.config.json
|
|
53
|
+
const config = {
|
|
54
|
+
framework: response.framework,
|
|
55
|
+
style: response.stylePath,
|
|
56
|
+
theme: {
|
|
57
|
+
primary: response.primaryColor
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
fs.writeFileSync('ignix.config.json', JSON.stringify(config, null, 2))
|
|
62
|
+
|
|
63
|
+
// 2. Ensure CSS file exists and add initial theme block
|
|
64
|
+
const cssDir = path.dirname(response.stylePath)
|
|
65
|
+
if (!fs.existsSync(cssDir)) {
|
|
66
|
+
fs.mkdirSync(cssDir, { recursive: true })
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const defaultThemeVars = `/* Ignix-Lite Custom Theme Variables */
|
|
70
|
+
:root {
|
|
71
|
+
--ix-primary: ${response.primaryColor};
|
|
72
|
+
--ix-primary-hover: ${response.primaryColor}d0;
|
|
73
|
+
--ix-primary-contrast: #ffffff;
|
|
74
|
+
--ix-primary-bg: ${response.primaryColor}1a;
|
|
75
|
+
}
|
|
76
|
+
`
|
|
77
|
+
|
|
78
|
+
if (fs.existsSync(response.stylePath)) {
|
|
79
|
+
let content = fs.readFileSync(response.stylePath, 'utf-8')
|
|
80
|
+
if (!content.includes('--ix-primary')) {
|
|
81
|
+
content = defaultThemeVars + '\n' + content
|
|
82
|
+
fs.writeFileSync(response.stylePath, content)
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
fs.writeFileSync(response.stylePath, defaultThemeVars)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
spinner.succeed(pc.green('Ignix-Lite initialized successfully!'))
|
|
89
|
+
console.log(`\nCreated ${pc.blue('ignix.config.json')}`)
|
|
90
|
+
console.log(`Updated theme variables in ${pc.blue(response.stylePath)}`)
|
|
91
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import pc from 'picocolors'
|
|
2
|
+
import { manifests } from '@mindfiredigital/ignix-lite-engine'
|
|
3
|
+
|
|
4
|
+
export function listCommand() {
|
|
5
|
+
const components = Object.keys(manifests).sort()
|
|
6
|
+
|
|
7
|
+
console.log(pc.bold(pc.cyan('\nš„ Ignix-Lite Components Catalog')))
|
|
8
|
+
console.log(pc.gray('ā'.repeat(70)))
|
|
9
|
+
|
|
10
|
+
components.forEach((name) => {
|
|
11
|
+
const desc =
|
|
12
|
+
manifests[name].description || pc.gray('No description available')
|
|
13
|
+
console.log(
|
|
14
|
+
` ${pc.bold(pc.green('ā'))} ${pc.bold(name.padEnd(15))} ${pc.dim('ā')} ${desc}`
|
|
15
|
+
)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
console.log(pc.gray('ā'.repeat(70)))
|
|
19
|
+
console.log(
|
|
20
|
+
pc.cyan(`Total: ${pc.bold(components.length)} components ready to use!\n`)
|
|
21
|
+
)
|
|
22
|
+
}
|