grg-kit-cli 0.6.7 ā 0.6.10
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/bin/grg.js +11 -1
- package/commands/add.js +97 -1
- package/commands/init.js +33 -9
- package/commands/list.js +28 -7
- package/commands/llm-setup.js +318 -20
- package/config/resources.js +0 -17
- package/package.json +1 -1
- package/scripts/README.md +1 -3
- package/config/.catalog-cache.json +0 -1
package/bin/grg.js
CHANGED
|
@@ -21,7 +21,7 @@ program
|
|
|
21
21
|
.option('-t, --theme <name>', 'Theme to install (grg-theme, claude, clean-slate, modern-minimal, amber-minimal, mocks)', 'grg-theme')
|
|
22
22
|
.action(init);
|
|
23
23
|
|
|
24
|
-
// Add command with block
|
|
24
|
+
// Add command with block and component subcommands
|
|
25
25
|
const addCommand = program.command('add').description('Add resources to your project');
|
|
26
26
|
|
|
27
27
|
addCommand
|
|
@@ -31,6 +31,16 @@ addCommand
|
|
|
31
31
|
.option('-o, --output <path>', 'Custom output directory')
|
|
32
32
|
.action(add);
|
|
33
33
|
|
|
34
|
+
addCommand
|
|
35
|
+
.command('component [componentName]')
|
|
36
|
+
.description('Add GRG components to your project (e.g., grg add component file-upload)')
|
|
37
|
+
.option('--all', 'Add all components')
|
|
38
|
+
.option('-o, --output <path>', 'Custom output directory')
|
|
39
|
+
.action(async (componentName, options) => {
|
|
40
|
+
const { addComponent } = require('../commands/add');
|
|
41
|
+
await addComponent(componentName, options);
|
|
42
|
+
});
|
|
43
|
+
|
|
34
44
|
// List command
|
|
35
45
|
program
|
|
36
46
|
.command('list [category]')
|
package/commands/add.js
CHANGED
|
@@ -196,4 +196,100 @@ function showUsage(RESOURCES) {
|
|
|
196
196
|
console.log(chalk.gray('\nRun'), chalk.cyan('grg list blocks'), chalk.gray('for more details'));
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
|
|
199
|
+
/**
|
|
200
|
+
* Add component command - downloads GRG components
|
|
201
|
+
* Format: grg add component <componentName>
|
|
202
|
+
* Examples:
|
|
203
|
+
* grg add component file-upload # Download file-upload component
|
|
204
|
+
* grg add component --all # All components
|
|
205
|
+
*/
|
|
206
|
+
async function addComponent(componentName, options) {
|
|
207
|
+
// Fetch catalog dynamically (with caching)
|
|
208
|
+
const spinner = ora('Fetching catalog...').start();
|
|
209
|
+
const RESOURCES = await fetchCatalog({ silent: true });
|
|
210
|
+
spinner.stop();
|
|
211
|
+
|
|
212
|
+
// Handle --all flag (download all components)
|
|
213
|
+
if (options.all) {
|
|
214
|
+
console.log(chalk.bold.cyan(`\nš¦ Adding all components\n`));
|
|
215
|
+
|
|
216
|
+
for (const component of RESOURCES.components) {
|
|
217
|
+
await downloadComponent(component, options.output);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
console.log(chalk.bold.green('⨠Done!'));
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Validate component name
|
|
225
|
+
if (!componentName) {
|
|
226
|
+
showComponentUsage(RESOURCES);
|
|
227
|
+
process.exit(1);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const component = RESOURCES.components.find(c => c.name === componentName);
|
|
231
|
+
if (!component) {
|
|
232
|
+
console.error(chalk.red(`\nError: Component "${componentName}" not found`));
|
|
233
|
+
console.log(chalk.yellow('\nAvailable components:'));
|
|
234
|
+
RESOURCES.components.forEach(c => {
|
|
235
|
+
console.log(chalk.cyan(` ${c.name}`), chalk.gray(`- ${c.description}`));
|
|
236
|
+
});
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
await downloadComponent(component, options.output);
|
|
241
|
+
console.log(chalk.bold.green('⨠Done!'));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Download a component
|
|
246
|
+
*/
|
|
247
|
+
async function downloadComponent(component, customOutput) {
|
|
248
|
+
const downloadSpinner = ora();
|
|
249
|
+
const outputPath = customOutput
|
|
250
|
+
? `${customOutput}/${component.name}`
|
|
251
|
+
: component.defaultOutput;
|
|
252
|
+
|
|
253
|
+
downloadSpinner.start(`Downloading ${component.title}...`);
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
const emitter = degit(`${REPO}/${component.path}`, {
|
|
257
|
+
cache: false,
|
|
258
|
+
force: true,
|
|
259
|
+
verbose: false,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
await emitter.clone(outputPath);
|
|
263
|
+
|
|
264
|
+
downloadSpinner.succeed(chalk.green(`ā ${component.title} added`));
|
|
265
|
+
console.log(chalk.gray(` Location: ${outputPath}`));
|
|
266
|
+
|
|
267
|
+
if (component.dependencies && component.dependencies.length > 0) {
|
|
268
|
+
console.log(chalk.gray(` Dependencies: ${component.dependencies.join(', ')}`));
|
|
269
|
+
}
|
|
270
|
+
console.log();
|
|
271
|
+
|
|
272
|
+
} catch (error) {
|
|
273
|
+
downloadSpinner.fail(chalk.red(`Failed to download ${component.title}`));
|
|
274
|
+
console.error(chalk.red(error.message));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Show component usage help
|
|
280
|
+
*/
|
|
281
|
+
function showComponentUsage(RESOURCES) {
|
|
282
|
+
console.log(chalk.yellow('\nUsage: grg add component <componentName>\n'));
|
|
283
|
+
console.log(chalk.bold('Examples:'));
|
|
284
|
+
console.log(chalk.cyan(' grg add component file-upload'), chalk.gray(' # Download file-upload'));
|
|
285
|
+
console.log(chalk.cyan(' grg add component --all'), chalk.gray(' # All components'));
|
|
286
|
+
|
|
287
|
+
console.log(chalk.bold('\nAvailable components:'));
|
|
288
|
+
RESOURCES.components.forEach(c => {
|
|
289
|
+
console.log(chalk.cyan(` ${c.name}`), chalk.gray(`- ${c.description}`));
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
console.log(chalk.gray('\nRun'), chalk.cyan('grg list components'), chalk.gray('for more details'));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
module.exports = { add, addComponent };
|
package/commands/init.js
CHANGED
|
@@ -48,10 +48,12 @@ async function init(options) {
|
|
|
48
48
|
// Step 1: Install Tailwind CSS v4
|
|
49
49
|
spinner.start('Installing Tailwind CSS v4...');
|
|
50
50
|
try {
|
|
51
|
-
await execAsync('npm install tailwindcss @tailwindcss/postcss postcss
|
|
51
|
+
await execAsync('npm install tailwindcss @tailwindcss/postcss postcss --force');
|
|
52
52
|
spinner.succeed(chalk.green('ā Tailwind CSS v4 installed'));
|
|
53
53
|
} catch (error) {
|
|
54
|
-
spinner.
|
|
54
|
+
spinner.fail(chalk.red('Failed to install Tailwind CSS v4'));
|
|
55
|
+
console.error(chalk.gray(error.message));
|
|
56
|
+
process.exit(1);
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
// Step 3: Create .postcssrc.json
|
|
@@ -71,10 +73,12 @@ async function init(options) {
|
|
|
71
73
|
// Step 4: Install Spartan-NG CLI
|
|
72
74
|
spinner.start('Installing Spartan-NG CLI...');
|
|
73
75
|
try {
|
|
74
|
-
await execAsync('npm install -D @spartan-ng/cli'
|
|
76
|
+
await execAsync('npm install -D @spartan-ng/cli');
|
|
75
77
|
spinner.succeed(chalk.green('ā Spartan-NG CLI installed'));
|
|
76
78
|
} catch (error) {
|
|
77
|
-
spinner.
|
|
79
|
+
spinner.fail(chalk.red('Failed to install Spartan-NG CLI'));
|
|
80
|
+
console.error(chalk.gray(error.message));
|
|
81
|
+
process.exit(1);
|
|
78
82
|
}
|
|
79
83
|
|
|
80
84
|
// Step 5: Create components.json config file
|
|
@@ -97,9 +101,14 @@ async function init(options) {
|
|
|
97
101
|
let tsconfigContent = await fs.readFile(tsconfigPath, 'utf-8');
|
|
98
102
|
|
|
99
103
|
// Strip comments from tsconfig (Angular generates tsconfig with comments)
|
|
104
|
+
// Be careful not to strip // inside strings (like URLs)
|
|
100
105
|
tsconfigContent = tsconfigContent
|
|
101
106
|
.replace(/\/\*[\s\S]*?\*\//g, '') // Remove block comments
|
|
102
|
-
.replace(
|
|
107
|
+
.replace(/^\s*\/\/.*/gm, ''); // Remove line comments (only at start of line after whitespace)
|
|
108
|
+
|
|
109
|
+
// Remove trailing commas (common in tsconfig files)
|
|
110
|
+
tsconfigContent = tsconfigContent
|
|
111
|
+
.replace(/,(\s*[}\]])/g, '$1');
|
|
103
112
|
|
|
104
113
|
const tsconfig = JSON.parse(tsconfigContent);
|
|
105
114
|
|
|
@@ -123,9 +132,12 @@ async function init(options) {
|
|
|
123
132
|
try {
|
|
124
133
|
const { spawn } = require('child_process');
|
|
125
134
|
await new Promise((resolve, reject) => {
|
|
126
|
-
|
|
135
|
+
// Use 'ng' directly (works with global @angular/cli) instead of 'npx ng'
|
|
136
|
+
// Exit code 127 occurs when npx can't find the command
|
|
137
|
+
const child = spawn('ng', ['g', '@spartan-ng/cli:ui'], {
|
|
127
138
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
128
|
-
shell: true
|
|
139
|
+
shell: true,
|
|
140
|
+
env: { ...process.env } // Inherit PATH to find global ng
|
|
129
141
|
});
|
|
130
142
|
|
|
131
143
|
let output = '';
|
|
@@ -156,7 +168,9 @@ async function init(options) {
|
|
|
156
168
|
}
|
|
157
169
|
});
|
|
158
170
|
|
|
171
|
+
let stderrOutput = '';
|
|
159
172
|
child.stderr.on('data', (data) => {
|
|
173
|
+
stderrOutput += data.toString();
|
|
160
174
|
// Spartan CLI outputs progress to stderr
|
|
161
175
|
const text = data.toString();
|
|
162
176
|
if (text.includes('CREATE') || text.includes('UPDATE')) {
|
|
@@ -168,14 +182,24 @@ async function init(options) {
|
|
|
168
182
|
child.on('close', (code) => {
|
|
169
183
|
console.log(); // New line after progress dots
|
|
170
184
|
if (code === 0) resolve();
|
|
171
|
-
else reject(new Error(`Process exited with code ${code}`));
|
|
185
|
+
else reject(new Error(`Process exited with code ${code}${stderrOutput ? '\n' + stderrOutput : ''}`));
|
|
186
|
+
});
|
|
187
|
+
child.on('error', (err) => {
|
|
188
|
+
if (err.code === 'ENOENT') {
|
|
189
|
+
reject(new Error('Angular CLI (ng) not found. Please install it: npm install -g @angular/cli'));
|
|
190
|
+
} else {
|
|
191
|
+
reject(err);
|
|
192
|
+
}
|
|
172
193
|
});
|
|
173
|
-
child.on('error', reject);
|
|
174
194
|
});
|
|
175
195
|
spinner.succeed(chalk.green('ā All Spartan-NG UI components installed'));
|
|
176
196
|
} catch (error) {
|
|
177
197
|
spinner.fail(chalk.red('Failed to run Spartan-NG UI generator'));
|
|
178
198
|
console.error(chalk.red(error.message));
|
|
199
|
+
console.log(chalk.yellow('\nTroubleshooting:'));
|
|
200
|
+
console.log(chalk.gray(' 1. Ensure Angular CLI is installed:'), chalk.cyan('npm install -g @angular/cli'));
|
|
201
|
+
console.log(chalk.gray(' 2. Ensure @spartan-ng/cli is installed:'), chalk.cyan('npm install -D @spartan-ng/cli'));
|
|
202
|
+
console.log(chalk.gray(' 3. Try running manually:'), chalk.cyan('ng g @spartan-ng/cli:ui'));
|
|
179
203
|
process.exit(1);
|
|
180
204
|
}
|
|
181
205
|
|
package/commands/list.js
CHANGED
|
@@ -3,7 +3,7 @@ const ora = require('ora');
|
|
|
3
3
|
const { fetchCatalog } = require('../config/catalog-fetcher');
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* List command - displays available blocks and themes
|
|
6
|
+
* List command - displays available blocks, components, and themes
|
|
7
7
|
* Usage: grg list [category]
|
|
8
8
|
*/
|
|
9
9
|
async function list(category) {
|
|
@@ -17,16 +17,17 @@ async function list(category) {
|
|
|
17
17
|
console.log(chalk.bold.cyan('\nš¦ GRG Kit Resources\n'));
|
|
18
18
|
|
|
19
19
|
console.log(chalk.bold('Blocks') + chalk.gray(` (${RESOURCES.blocks.length} available)`));
|
|
20
|
-
console.log(chalk.gray(' Add with: grg add block
|
|
20
|
+
console.log(chalk.gray(' Add with: grg add block <name>'));
|
|
21
21
|
console.log(chalk.gray(' Run: grg list blocks\n'));
|
|
22
22
|
|
|
23
|
+
console.log(chalk.bold('Components') + chalk.gray(` (${RESOURCES.components.length} available)`));
|
|
24
|
+
console.log(chalk.gray(' Add with: grg add component <name>'));
|
|
25
|
+
console.log(chalk.gray(' Run: grg list components\n'));
|
|
26
|
+
|
|
23
27
|
console.log(chalk.bold('Themes') + chalk.gray(` (${RESOURCES.themes.length} available)`));
|
|
24
28
|
console.log(chalk.gray(' Set with: grg init --theme <name>'));
|
|
25
29
|
console.log(chalk.gray(' Run: grg list themes\n'));
|
|
26
30
|
|
|
27
|
-
console.log(chalk.gray('Components and spartan-ng examples are installed automatically with'), chalk.cyan('grg init'));
|
|
28
|
-
console.log();
|
|
29
|
-
|
|
30
31
|
return;
|
|
31
32
|
}
|
|
32
33
|
|
|
@@ -36,7 +37,10 @@ async function list(category) {
|
|
|
36
37
|
RESOURCES.blocks.forEach(block => {
|
|
37
38
|
console.log(chalk.bold(` ${block.name}`));
|
|
38
39
|
console.log(chalk.gray(` ${block.description}`));
|
|
39
|
-
console.log(chalk.yellow(` grg add block
|
|
40
|
+
console.log(chalk.yellow(` grg add block ${block.name}`));
|
|
41
|
+
if (block.files && block.files.length > 0) {
|
|
42
|
+
console.log(chalk.gray(` Files: ${block.files.map(f => f.id).join(', ')}`));
|
|
43
|
+
}
|
|
40
44
|
if (block.tags && block.tags.length > 0) {
|
|
41
45
|
console.log(chalk.gray(` Tags: ${block.tags.join(', ')}`));
|
|
42
46
|
}
|
|
@@ -44,6 +48,23 @@ async function list(category) {
|
|
|
44
48
|
});
|
|
45
49
|
break;
|
|
46
50
|
|
|
51
|
+
case 'components':
|
|
52
|
+
console.log(chalk.bold.cyan('\nš§© Available Components\n'));
|
|
53
|
+
console.log(chalk.gray(' Use with: grg add component <name>\n'));
|
|
54
|
+
RESOURCES.components.forEach(component => {
|
|
55
|
+
console.log(chalk.bold(` ${component.name}`));
|
|
56
|
+
console.log(chalk.gray(` ${component.description}`));
|
|
57
|
+
console.log(chalk.yellow(` grg add component ${component.name}`));
|
|
58
|
+
if (component.dependencies && component.dependencies.length > 0) {
|
|
59
|
+
console.log(chalk.gray(` Dependencies: ${component.dependencies.join(', ')}`));
|
|
60
|
+
}
|
|
61
|
+
if (component.tags && component.tags.length > 0) {
|
|
62
|
+
console.log(chalk.gray(` Tags: ${component.tags.join(', ')}`));
|
|
63
|
+
}
|
|
64
|
+
console.log();
|
|
65
|
+
});
|
|
66
|
+
break;
|
|
67
|
+
|
|
47
68
|
case 'themes':
|
|
48
69
|
console.log(chalk.bold.cyan('\nšØ Available Themes\n'));
|
|
49
70
|
console.log(chalk.gray(' Use with: grg init --theme <name>\n'));
|
|
@@ -60,7 +81,7 @@ async function list(category) {
|
|
|
60
81
|
|
|
61
82
|
default:
|
|
62
83
|
console.error(chalk.red(`Error: Unknown category "${category}"`));
|
|
63
|
-
console.log(chalk.yellow('Valid categories: blocks, themes'));
|
|
84
|
+
console.log(chalk.yellow('Valid categories: blocks, components, themes'));
|
|
64
85
|
process.exit(1);
|
|
65
86
|
}
|
|
66
87
|
}
|
package/commands/llm-setup.js
CHANGED
|
@@ -183,7 +183,7 @@ This project uses **GRG Kit**, a comprehensive Angular UI toolkit built on top o
|
|
|
183
183
|
**GRG Kit Components** (in \`libs/grg-ui/\`):
|
|
184
184
|
- Use \`grg-\` prefix for selectors
|
|
185
185
|
- Import via \`GrgComponentImports\`
|
|
186
|
-
- Example: \`
|
|
186
|
+
- Example: \`GrgFileUploadImports\`
|
|
187
187
|
- Path alias: \`@grg-kit/ui/component-name\`
|
|
188
188
|
|
|
189
189
|
### 3. Import Patterns
|
|
@@ -213,14 +213,14 @@ import { HlmDialogImports } from '@spartan-ng/helm/dialog';
|
|
|
213
213
|
|
|
214
214
|
#### GRG Kit Components
|
|
215
215
|
\`\`\`typescript
|
|
216
|
-
import {
|
|
216
|
+
import { GrgFileUploadImports } from '@grg-kit/ui/file-upload';
|
|
217
217
|
|
|
218
218
|
@Component({
|
|
219
|
-
imports: [
|
|
219
|
+
imports: [GrgFileUploadImports],
|
|
220
220
|
template: \`
|
|
221
|
-
<grg-
|
|
222
|
-
<grg-
|
|
223
|
-
</grg-
|
|
221
|
+
<grg-file-upload>
|
|
222
|
+
<grg-file-upload-trigger>Drop files here</grg-file-upload-trigger>
|
|
223
|
+
</grg-file-upload>
|
|
224
224
|
\`
|
|
225
225
|
})
|
|
226
226
|
\`\`\`
|
|
@@ -343,6 +343,178 @@ class="flex flex-col justify-between gap-4 py-4 sm:flex-row sm:items-center"
|
|
|
343
343
|
[attr.data-state]="row.getIsSelected() && 'selected'"
|
|
344
344
|
\`\`\`
|
|
345
345
|
|
|
346
|
+
## TailwindCSS Best Practices (CRITICAL)
|
|
347
|
+
|
|
348
|
+
### NEVER Use Raw Color Classes
|
|
349
|
+
|
|
350
|
+
**FORBIDDEN - Do NOT use these patterns:**
|
|
351
|
+
\`\`\`typescript
|
|
352
|
+
// ā WRONG - Raw Tailwind colors
|
|
353
|
+
class="text-green-600 bg-green-100" // No raw green
|
|
354
|
+
class="text-red-500 bg-red-50" // No raw red
|
|
355
|
+
class="text-yellow-600 bg-yellow-100" // No raw yellow
|
|
356
|
+
class="text-blue-500 bg-blue-100" // No raw blue
|
|
357
|
+
class="border-gray-200" // No raw gray
|
|
358
|
+
class="text-slate-700" // No raw slate
|
|
359
|
+
\`\`\`
|
|
360
|
+
|
|
361
|
+
**REQUIRED - Use semantic color tokens:**
|
|
362
|
+
\`\`\`typescript
|
|
363
|
+
// ā
CORRECT - Semantic colors from design system
|
|
364
|
+
class="text-foreground" // Primary text
|
|
365
|
+
class="text-muted-foreground" // Secondary/muted text
|
|
366
|
+
class="bg-background" // Page background
|
|
367
|
+
class="bg-card" // Card background
|
|
368
|
+
class="bg-muted" // Muted/subtle background
|
|
369
|
+
class="bg-primary text-primary-foreground" // Primary actions
|
|
370
|
+
class="bg-secondary text-secondary-foreground" // Secondary elements
|
|
371
|
+
class="bg-destructive text-destructive-foreground" // Errors/danger
|
|
372
|
+
class="bg-accent text-accent-foreground" // Accents/highlights
|
|
373
|
+
class="border-border" // Standard borders
|
|
374
|
+
class="border-input" // Input borders
|
|
375
|
+
\`\`\`
|
|
376
|
+
|
|
377
|
+
### Available Semantic Colors
|
|
378
|
+
|
|
379
|
+
These are the ONLY colors you should use (defined in \`styles.css\` and theme files):
|
|
380
|
+
|
|
381
|
+
| Token | Usage |
|
|
382
|
+
|-------|-------|
|
|
383
|
+
| \`background\` / \`foreground\` | Page background and primary text |
|
|
384
|
+
| \`card\` / \`card-foreground\` | Card containers |
|
|
385
|
+
| \`popover\` / \`popover-foreground\` | Popovers, dropdowns |
|
|
386
|
+
| \`primary\` / \`primary-foreground\` | Primary buttons, links |
|
|
387
|
+
| \`secondary\` / \`secondary-foreground\` | Secondary actions |
|
|
388
|
+
| \`muted\` / \`muted-foreground\` | Subtle backgrounds, secondary text |
|
|
389
|
+
| \`accent\` / \`accent-foreground\` | Highlights, hover states |
|
|
390
|
+
| \`destructive\` / \`destructive-foreground\` | Errors, delete actions |
|
|
391
|
+
| \`border\` | Standard borders |
|
|
392
|
+
| \`input\` | Form input borders |
|
|
393
|
+
| \`ring\` | Focus rings |
|
|
394
|
+
| \`chart-1\` through \`chart-5\` | Chart/data visualization colors |
|
|
395
|
+
| \`sidebar-*\` | Sidebar-specific colors |
|
|
396
|
+
|
|
397
|
+
### Adding New Semantic Colors
|
|
398
|
+
|
|
399
|
+
If you need a color that doesn't exist (e.g., success, warning, info):
|
|
400
|
+
|
|
401
|
+
**Step 1: Add CSS variables to \`src/styles.css\`:**
|
|
402
|
+
\`\`\`css
|
|
403
|
+
:root {
|
|
404
|
+
/* Existing variables... */
|
|
405
|
+
|
|
406
|
+
/* Add new semantic color with BOTH light and dark values */
|
|
407
|
+
--success: oklch(0.72 0.19 142); /* Green for light mode */
|
|
408
|
+
--success-foreground: oklch(1 0 0); /* White text */
|
|
409
|
+
|
|
410
|
+
--warning: oklch(0.75 0.18 85); /* Amber for light mode */
|
|
411
|
+
--warning-foreground: oklch(0.2 0 0); /* Dark text */
|
|
412
|
+
|
|
413
|
+
--info: oklch(0.65 0.15 250); /* Blue for light mode */
|
|
414
|
+
--info-foreground: oklch(1 0 0); /* White text */
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
.dark {
|
|
418
|
+
/* Dark mode equivalents */
|
|
419
|
+
--success: oklch(0.65 0.17 142);
|
|
420
|
+
--success-foreground: oklch(1 0 0);
|
|
421
|
+
|
|
422
|
+
--warning: oklch(0.70 0.16 85);
|
|
423
|
+
--warning-foreground: oklch(0.15 0 0);
|
|
424
|
+
|
|
425
|
+
--info: oklch(0.60 0.14 250);
|
|
426
|
+
--info-foreground: oklch(1 0 0);
|
|
427
|
+
}
|
|
428
|
+
\`\`\`
|
|
429
|
+
|
|
430
|
+
**Step 2: Register in \`@theme inline\` block (in theme file):**
|
|
431
|
+
\`\`\`css
|
|
432
|
+
@theme inline {
|
|
433
|
+
--color-success: var(--success);
|
|
434
|
+
--color-success-foreground: var(--success-foreground);
|
|
435
|
+
--color-warning: var(--warning);
|
|
436
|
+
--color-warning-foreground: var(--warning-foreground);
|
|
437
|
+
--color-info: var(--info);
|
|
438
|
+
--color-info-foreground: var(--info-foreground);
|
|
439
|
+
}
|
|
440
|
+
\`\`\`
|
|
441
|
+
|
|
442
|
+
**Step 3: Now use in templates:**
|
|
443
|
+
\`\`\`typescript
|
|
444
|
+
class="bg-success text-success-foreground" // Success states
|
|
445
|
+
class="bg-warning text-warning-foreground" // Warning states
|
|
446
|
+
class="bg-info text-info-foreground" // Info states
|
|
447
|
+
\`\`\`
|
|
448
|
+
|
|
449
|
+
### Typography Best Practices
|
|
450
|
+
|
|
451
|
+
**NEVER use arbitrary font sizes:**
|
|
452
|
+
\`\`\`typescript
|
|
453
|
+
// ā WRONG - Arbitrary sizes
|
|
454
|
+
class="text-[13px]" // No arbitrary values
|
|
455
|
+
class="text-[1.1rem]" // No arbitrary values
|
|
456
|
+
\`\`\`
|
|
457
|
+
|
|
458
|
+
**USE the Tailwind typography scale:**
|
|
459
|
+
\`\`\`typescript
|
|
460
|
+
// ā
CORRECT - Standard typography scale
|
|
461
|
+
class="text-xs" // 0.75rem (12px)
|
|
462
|
+
class="text-sm" // 0.875rem (14px)
|
|
463
|
+
class="text-base" // 1rem (16px) - default body
|
|
464
|
+
class="text-lg" // 1.125rem (18px)
|
|
465
|
+
class="text-xl" // 1.25rem (20px)
|
|
466
|
+
class="text-2xl" // 1.5rem (24px)
|
|
467
|
+
class="text-3xl" // 1.875rem (30px)
|
|
468
|
+
class="text-4xl" // 2.25rem (36px)
|
|
469
|
+
\`\`\`
|
|
470
|
+
|
|
471
|
+
**Font weights:**
|
|
472
|
+
\`\`\`typescript
|
|
473
|
+
class="font-normal" // 400
|
|
474
|
+
class="font-medium" // 500
|
|
475
|
+
class="font-semibold" // 600
|
|
476
|
+
class="font-bold" // 700
|
|
477
|
+
\`\`\`
|
|
478
|
+
|
|
479
|
+
**Font families (from theme):**
|
|
480
|
+
\`\`\`typescript
|
|
481
|
+
class="font-sans" // System UI font stack
|
|
482
|
+
class="font-mono" // Monospace for code
|
|
483
|
+
class="font-serif" // Serif for special cases
|
|
484
|
+
\`\`\`
|
|
485
|
+
|
|
486
|
+
### Spacing Best Practices
|
|
487
|
+
|
|
488
|
+
**USE the standard spacing scale:**
|
|
489
|
+
\`\`\`typescript
|
|
490
|
+
// ā
CORRECT - Standard spacing
|
|
491
|
+
class="p-4" // 1rem
|
|
492
|
+
class="gap-2" // 0.5rem
|
|
493
|
+
class="mt-6" // 1.5rem
|
|
494
|
+
class="space-y-4" // 1rem between children
|
|
495
|
+
\`\`\`
|
|
496
|
+
|
|
497
|
+
**AVOID arbitrary spacing unless absolutely necessary:**
|
|
498
|
+
\`\`\`typescript
|
|
499
|
+
// ā AVOID - Arbitrary spacing
|
|
500
|
+
class="p-[13px]" // Use p-3 or p-4 instead
|
|
501
|
+
\`\`\`
|
|
502
|
+
|
|
503
|
+
### Dark Mode Support
|
|
504
|
+
|
|
505
|
+
All semantic colors automatically support dark mode. The theme system handles this via:
|
|
506
|
+
- \`:root\` for light mode values
|
|
507
|
+
- \`.dark\` or \`[data-theme="theme-name"].dark\` for dark mode values
|
|
508
|
+
|
|
509
|
+
**You do NOT need to add \`dark:\` prefixes** when using semantic colors:
|
|
510
|
+
\`\`\`typescript
|
|
511
|
+
// ā
CORRECT - Semantic colors auto-adapt
|
|
512
|
+
class="bg-background text-foreground" // Works in both light and dark
|
|
513
|
+
|
|
514
|
+
// ā WRONG - Manual dark mode with raw colors
|
|
515
|
+
class="bg-white dark:bg-gray-900" // Don't do this
|
|
516
|
+
\`\`\`
|
|
517
|
+
|
|
346
518
|
### Component Variants
|
|
347
519
|
Components support multiple variants through the \`variant\` attribute:
|
|
348
520
|
|
|
@@ -504,7 +676,7 @@ export class FormComponent {
|
|
|
504
676
|
- **Slider**: Range input controls
|
|
505
677
|
|
|
506
678
|
### GRG Kit Custom Components
|
|
507
|
-
- **
|
|
679
|
+
- **File Upload**: Drag and drop file upload component
|
|
508
680
|
|
|
509
681
|
## Package Manager
|
|
510
682
|
|
|
@@ -568,7 +740,7 @@ After adding, restart your IDE for the MCP server to be available.
|
|
|
568
740
|
Use the MCP server for:
|
|
569
741
|
1. **Themes** - Install different color themes
|
|
570
742
|
2. **Blocks** - Pre-built page layouts (auth, shell, settings)
|
|
571
|
-
3. **GRG Kit Components** - Custom components like
|
|
743
|
+
3. **GRG Kit Components** - Custom components like file-upload
|
|
572
744
|
|
|
573
745
|
**Do NOT use MCP for:**
|
|
574
746
|
- Spartan-NG components (button, card, dialog, etc.) - already installed
|
|
@@ -592,7 +764,7 @@ mcp2_search_ui_resources({
|
|
|
592
764
|
**When to use:**
|
|
593
765
|
- User needs a page layout or block
|
|
594
766
|
- Looking for a theme
|
|
595
|
-
- Need a GRG Kit component (
|
|
767
|
+
- Need a GRG Kit component (file-upload)
|
|
596
768
|
|
|
597
769
|
### 2. mcp2_suggest_resources
|
|
598
770
|
|
|
@@ -698,14 +870,14 @@ AI Workflow:
|
|
|
698
870
|
### Example 4: User Wants a Form Component
|
|
699
871
|
|
|
700
872
|
\`\`\`
|
|
701
|
-
User: "I need a
|
|
873
|
+
User: "I need a file upload"
|
|
702
874
|
|
|
703
875
|
AI Workflow:
|
|
704
|
-
1. mcp2_search_ui_resources({ query: "
|
|
705
|
-
ā Finds: component:
|
|
876
|
+
1. mcp2_search_ui_resources({ query: "file upload" })
|
|
877
|
+
ā Finds: component:file-upload
|
|
706
878
|
|
|
707
879
|
2. Components are included automatically with grg init
|
|
708
|
-
ā Just import and use: import {
|
|
880
|
+
ā Just import and use: import { GrgFileUploadImports } from '@grg-kit/ui/file-upload';
|
|
709
881
|
|
|
710
882
|
3. Use with Spartan-NG form components (from design-system.md)
|
|
711
883
|
\`\`\`
|
|
@@ -735,7 +907,7 @@ User request:
|
|
|
735
907
|
āā Need a theme?
|
|
736
908
|
ā āā Use MCP: mcp2_list_available_resources({ category: "themes" })
|
|
737
909
|
ā
|
|
738
|
-
āā Need
|
|
910
|
+
āā Need file-upload or other GRG Kit component?
|
|
739
911
|
āā Use MCP: mcp2_search_ui_resources({ query: "..." })
|
|
740
912
|
\`\`\`
|
|
741
913
|
|
|
@@ -789,7 +961,7 @@ import { HlmDialogImports } from '@spartan-ng/helm/dialog';
|
|
|
789
961
|
|
|
790
962
|
**GRG Kit (grg- prefix):**
|
|
791
963
|
\`\`\`typescript
|
|
792
|
-
import {
|
|
964
|
+
import { GrgFileUploadImports } from '@grg-kit/ui/file-upload';
|
|
793
965
|
\`\`\`
|
|
794
966
|
|
|
795
967
|
### Common Patterns
|
|
@@ -849,15 +1021,56 @@ import { lucideCheck, lucideX } from '@ng-icons/lucide';
|
|
|
849
1021
|
Use MCP only for:
|
|
850
1022
|
- **Blocks** (auth, shell, settings) - \`mcp2_search_ui_resources({ query: "auth" })\`
|
|
851
1023
|
- **Themes** - \`mcp2_list_available_resources({ category: "themes" })\`
|
|
852
|
-
- **GRG Kit components** (
|
|
1024
|
+
- **GRG Kit components** (file-upload) - \`mcp2_search_ui_resources({ query: "file-upload" })\`
|
|
853
1025
|
|
|
854
1026
|
**Do NOT use MCP for Spartan-NG components** - they are already installed!
|
|
855
1027
|
|
|
1028
|
+
## TailwindCSS Rules (CRITICAL)
|
|
1029
|
+
|
|
1030
|
+
### NEVER use raw Tailwind colors:
|
|
1031
|
+
\`\`\`html
|
|
1032
|
+
<!-- ā FORBIDDEN -->
|
|
1033
|
+
<div class="text-green-600 bg-green-100">Success</div>
|
|
1034
|
+
<div class="text-red-500">Error</div>
|
|
1035
|
+
<div class="bg-yellow-50 border-yellow-200">Warning</div>
|
|
1036
|
+
\`\`\`
|
|
1037
|
+
|
|
1038
|
+
### ALWAYS use semantic color tokens:
|
|
1039
|
+
\`\`\`html
|
|
1040
|
+
<!-- ā
CORRECT -->
|
|
1041
|
+
<div class="bg-primary text-primary-foreground">Primary</div>
|
|
1042
|
+
<div class="bg-destructive text-destructive-foreground">Error</div>
|
|
1043
|
+
<div class="text-muted-foreground">Secondary text</div>
|
|
1044
|
+
<div class="bg-muted">Subtle background</div>
|
|
1045
|
+
<div class="border-border">Standard border</div>
|
|
1046
|
+
\`\`\`
|
|
1047
|
+
|
|
1048
|
+
### Available semantic colors:
|
|
1049
|
+
\`background\`, \`foreground\`, \`card\`, \`card-foreground\`, \`popover\`, \`popover-foreground\`,
|
|
1050
|
+
\`primary\`, \`primary-foreground\`, \`secondary\`, \`secondary-foreground\`, \`muted\`, \`muted-foreground\`,
|
|
1051
|
+
\`accent\`, \`accent-foreground\`, \`destructive\`, \`destructive-foreground\`, \`border\`, \`input\`, \`ring\`
|
|
1052
|
+
|
|
1053
|
+
### Need a new color (e.g., success, warning)?
|
|
1054
|
+
1. Add CSS variable to \`src/styles.css\` with light AND dark mode values
|
|
1055
|
+
2. Register in \`@theme inline\` block
|
|
1056
|
+
3. Then use: \`bg-success text-success-foreground\`
|
|
1057
|
+
|
|
1058
|
+
### Typography - use standard scale:
|
|
1059
|
+
\`\`\`html
|
|
1060
|
+
<!-- ā
CORRECT -->
|
|
1061
|
+
<p class="text-sm">Small</p>
|
|
1062
|
+
<p class="text-base">Body</p>
|
|
1063
|
+
<h2 class="text-xl font-semibold">Heading</h2>
|
|
1064
|
+
|
|
1065
|
+
<!-- ā WRONG - arbitrary sizes -->
|
|
1066
|
+
<p class="text-[13px]">Don't do this</p>
|
|
1067
|
+
\`\`\`
|
|
1068
|
+
|
|
856
1069
|
## Remember
|
|
857
1070
|
- Spartan-NG components are pre-installed - just import and use
|
|
858
1071
|
- Follow existing patterns in the codebase
|
|
859
|
-
- Use TailwindCSS v4 for styling
|
|
860
|
-
-
|
|
1072
|
+
- Use TailwindCSS v4 for styling with SEMANTIC colors only
|
|
1073
|
+
- NEVER use raw colors like text-green-600, bg-yellow-100, etc.
|
|
861
1074
|
`;
|
|
862
1075
|
}
|
|
863
1076
|
|
|
@@ -900,7 +1113,7 @@ import { HlmDialogImports } from '@spartan-ng/helm/dialog';
|
|
|
900
1113
|
|
|
901
1114
|
**GRG Kit:**
|
|
902
1115
|
\`\`\`typescript
|
|
903
|
-
import {
|
|
1116
|
+
import { GrgFileUploadImports } from '@grg-kit/ui/file-upload';
|
|
904
1117
|
\`\`\`
|
|
905
1118
|
|
|
906
1119
|
## Common Components
|
|
@@ -982,7 +1195,7 @@ ${blocksList}
|
|
|
982
1195
|
- Need button, card, dialog, form field, table? ā Use Spartan-NG (already installed)
|
|
983
1196
|
- Need page layout (dashboard, auth, settings)? ā Use MCP: \`mcp2_search_ui_resources\`
|
|
984
1197
|
- Need theme? ā Use MCP: \`mcp2_list_available_resources({ category: "themes" })\`
|
|
985
|
-
- Need
|
|
1198
|
+
- Need file-upload? ā Use MCP: \`mcp2_search_ui_resources\`
|
|
986
1199
|
|
|
987
1200
|
## Package Manager
|
|
988
1201
|
|
|
@@ -991,6 +1204,91 @@ Use npm for package management.
|
|
|
991
1204
|
## Styling
|
|
992
1205
|
|
|
993
1206
|
Use TailwindCSS v4 for all styling. Prefer signals for state management.
|
|
1207
|
+
|
|
1208
|
+
## TailwindCSS Best Practices (CRITICAL)
|
|
1209
|
+
|
|
1210
|
+
### NEVER use raw Tailwind colors:
|
|
1211
|
+
\`\`\`html
|
|
1212
|
+
<!-- ā FORBIDDEN - These break theming and dark mode -->
|
|
1213
|
+
<div class="text-green-600 bg-green-100">Success</div>
|
|
1214
|
+
<div class="text-red-500">Error</div>
|
|
1215
|
+
<div class="bg-yellow-50">Warning</div>
|
|
1216
|
+
<div class="border-gray-200">Border</div>
|
|
1217
|
+
\`\`\`
|
|
1218
|
+
|
|
1219
|
+
### ALWAYS use semantic color tokens:
|
|
1220
|
+
\`\`\`html
|
|
1221
|
+
<!-- ā
CORRECT - These respect theming and dark mode -->
|
|
1222
|
+
<div class="bg-primary text-primary-foreground">Primary action</div>
|
|
1223
|
+
<div class="bg-destructive text-destructive-foreground">Error/Delete</div>
|
|
1224
|
+
<div class="text-muted-foreground">Secondary text</div>
|
|
1225
|
+
<div class="bg-muted">Subtle background</div>
|
|
1226
|
+
<div class="bg-accent text-accent-foreground">Highlighted</div>
|
|
1227
|
+
<div class="border-border">Standard border</div>
|
|
1228
|
+
\`\`\`
|
|
1229
|
+
|
|
1230
|
+
### Available semantic colors:
|
|
1231
|
+
| Token | Usage |
|
|
1232
|
+
|-------|-------|
|
|
1233
|
+
| \`background\` / \`foreground\` | Page background, primary text |
|
|
1234
|
+
| \`card\` / \`card-foreground\` | Card containers |
|
|
1235
|
+
| \`primary\` / \`primary-foreground\` | Primary buttons, CTAs |
|
|
1236
|
+
| \`secondary\` / \`secondary-foreground\` | Secondary actions |
|
|
1237
|
+
| \`muted\` / \`muted-foreground\` | Subtle backgrounds, secondary text |
|
|
1238
|
+
| \`accent\` / \`accent-foreground\` | Highlights, hover states |
|
|
1239
|
+
| \`destructive\` / \`destructive-foreground\` | Errors, delete actions |
|
|
1240
|
+
| \`border\` | Standard borders |
|
|
1241
|
+
| \`input\` | Form input borders |
|
|
1242
|
+
|
|
1243
|
+
### Adding new semantic colors (e.g., success, warning, info):
|
|
1244
|
+
|
|
1245
|
+
**Step 1:** Add to \`src/styles.css\` with BOTH light and dark values:
|
|
1246
|
+
\`\`\`css
|
|
1247
|
+
:root {
|
|
1248
|
+
--success: oklch(0.72 0.19 142);
|
|
1249
|
+
--success-foreground: oklch(1 0 0);
|
|
1250
|
+
}
|
|
1251
|
+
.dark {
|
|
1252
|
+
--success: oklch(0.65 0.17 142);
|
|
1253
|
+
--success-foreground: oklch(1 0 0);
|
|
1254
|
+
}
|
|
1255
|
+
\`\`\`
|
|
1256
|
+
|
|
1257
|
+
**Step 2:** Register in \`@theme inline\` block in theme file:
|
|
1258
|
+
\`\`\`css
|
|
1259
|
+
@theme inline {
|
|
1260
|
+
--color-success: var(--success);
|
|
1261
|
+
--color-success-foreground: var(--success-foreground);
|
|
1262
|
+
}
|
|
1263
|
+
\`\`\`
|
|
1264
|
+
|
|
1265
|
+
**Step 3:** Use in templates:
|
|
1266
|
+
\`\`\`html
|
|
1267
|
+
<div class="bg-success text-success-foreground">Success!</div>
|
|
1268
|
+
\`\`\`
|
|
1269
|
+
|
|
1270
|
+
### Typography - use standard scale only:
|
|
1271
|
+
\`\`\`html
|
|
1272
|
+
<!-- ā
CORRECT -->
|
|
1273
|
+
<p class="text-sm">Small (14px)</p>
|
|
1274
|
+
<p class="text-base">Body (16px)</p>
|
|
1275
|
+
<h2 class="text-xl font-semibold">Heading</h2>
|
|
1276
|
+
|
|
1277
|
+
<!-- ā WRONG - arbitrary sizes -->
|
|
1278
|
+
<p class="text-[13px]">Avoid arbitrary values</p>
|
|
1279
|
+
\`\`\`
|
|
1280
|
+
|
|
1281
|
+
Standard scale: \`text-xs\` (12px), \`text-sm\` (14px), \`text-base\` (16px), \`text-lg\` (18px), \`text-xl\` (20px), \`text-2xl\` (24px), \`text-3xl\` (30px), \`text-4xl\` (36px)
|
|
1282
|
+
|
|
1283
|
+
### Dark mode is automatic:
|
|
1284
|
+
Semantic colors automatically adapt to dark mode. Do NOT use \`dark:\` prefix with raw colors.
|
|
1285
|
+
\`\`\`html
|
|
1286
|
+
<!-- ā
Auto-adapts to dark mode -->
|
|
1287
|
+
<div class="bg-background text-foreground">Content</div>
|
|
1288
|
+
|
|
1289
|
+
<!-- ā Don't do this -->
|
|
1290
|
+
<div class="bg-white dark:bg-gray-900">Content</div>
|
|
1291
|
+
\`\`\`
|
|
994
1292
|
`;
|
|
995
1293
|
}
|
|
996
1294
|
|
package/config/resources.js
CHANGED
|
@@ -145,23 +145,6 @@ const RESOURCES = {
|
|
|
145
145
|
"dependencies": [
|
|
146
146
|
"@spartan-ng/helm/button"
|
|
147
147
|
]
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
"name": "stepper",
|
|
151
|
-
"title": "Stepper Component",
|
|
152
|
-
"description": "Multi-step form component with progress indicator",
|
|
153
|
-
"path": "templates/ui/components/stepper",
|
|
154
|
-
"defaultOutput": "src/app/components/stepper",
|
|
155
|
-
"tags": [
|
|
156
|
-
"form",
|
|
157
|
-
"wizard",
|
|
158
|
-
"multi-step",
|
|
159
|
-
"progress"
|
|
160
|
-
],
|
|
161
|
-
"dependencies": [
|
|
162
|
-
"@spartan-ng/helm/button",
|
|
163
|
-
"@spartan-ng/helm/card"
|
|
164
|
-
]
|
|
165
148
|
}
|
|
166
149
|
],
|
|
167
150
|
"blocks": [
|
package/package.json
CHANGED
package/scripts/README.md
CHANGED
|
@@ -187,7 +187,6 @@ grg-kit/
|
|
|
187
187
|
ā ā āāā meta.json ā All themes metadata
|
|
188
188
|
ā ā āāā *.css
|
|
189
189
|
ā āāā libs/grg-ui/
|
|
190
|
-
ā ā āāā stepper/meta.json
|
|
191
190
|
ā ā āāā file-upload/meta.json
|
|
192
191
|
ā āāā scripts/
|
|
193
192
|
ā āāā generate-sources.js ā Stage 1
|
|
@@ -216,8 +215,7 @@ grg-kit/
|
|
|
216
215
|
ā āāā meta.json ā Copied from app
|
|
217
216
|
ā āāā *.css
|
|
218
217
|
āāā components/
|
|
219
|
-
|
|
220
|
-
āāā file-upload/meta.json
|
|
218
|
+
āāā file-upload/meta.json ā Copied from app
|
|
221
219
|
```
|
|
222
220
|
|
|
223
221
|
---
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"timestamp":1765202627293,"data":{"themes":[{"name":"amber-minimal","title":"Amber Minimal","description":"Warm amber accents","file":"amber-minimal.css","tags":["minimal","warm","amber","orange"],"features":["dark-mode","tailwind-v4","spartan-ng","oklch"],"path":"templates/ui/themes/amber-minimal.css","defaultOutput":"src/themes/amber-minimal.css"},{"name":"claude","title":"Claude","description":"Claude-inspired warm tones","file":"claude.css","tags":["warm","orange","brown","claude"],"features":["dark-mode","tailwind-v4","spartan-ng","oklch"],"path":"templates/ui/themes/claude.css","defaultOutput":"src/themes/claude.css"},{"name":"clean-slate","title":"Clean Slate","description":"Minimal grayscale palette","file":"clean-slate.css","tags":["minimal","grayscale","neutral","clean"],"features":["dark-mode","tailwind-v4","spartan-ng","oklch"],"path":"templates/ui/themes/clean-slate.css","defaultOutput":"src/themes/clean-slate.css"},{"name":"grg-theme","title":"Grg Theme","description":"Default theme with purple/orange accents","file":"grg-theme.css","tags":["default","purple","orange","colorful"],"features":["dark-mode","tailwind-v4","spartan-ng","oklch"],"path":"templates/ui/themes/grg-theme.css","defaultOutput":"src/themes/grg-theme.css"},{"name":"mocks","title":"Mocks","description":"Theme for mockups and prototypes","file":"mocks.css","tags":["mockup","prototype","design"],"features":["dark-mode","tailwind-v4","spartan-ng","oklch"],"path":"templates/ui/themes/mocks.css","defaultOutput":"src/themes/mocks.css"},{"name":"modern-minimal","title":"Modern Minimal","description":"Contemporary minimal design","file":"modern-minimal.css","tags":["minimal","modern","contemporary","clean"],"features":["dark-mode","tailwind-v4","spartan-ng","oklch"],"path":"templates/ui/themes/modern-minimal.css","defaultOutput":"src/themes/modern-minimal.css"}],"components":[{"name":"file-upload","title":"File Upload","description":"Drag and drop file upload component","tags":["file","upload","form","drag-drop"],"dependencies":["@spartan-ng/helm/button"],"path":"templates/ui/components/file-upload","defaultOutput":"src/app/components/file-upload"},{"name":"stepper","title":"Stepper","description":"Multi-step form component with progress indicator","tags":["form","wizard","multi-step","progress"],"dependencies":["@spartan-ng/helm/button","@spartan-ng/helm/card"],"path":"templates/ui/components/stepper","defaultOutput":"src/app/components/stepper"}],"blocks":[{"name":"auth","title":"Auth","description":"Authentication pages (login, signup, forgot password)","tags":["auth","login","signup","authentication","form"],"dependencies":["@spartan-ng/helm/button","@spartan-ng/helm/card","@spartan-ng/helm/form-field"],"files":[{"id":"forgot-password","file":"forgot-password.component.ts","title":"Forgot Password","description":"Forgot Password"},{"id":"login","file":"login.component.ts","title":"Login","description":"Login"},{"id":"register","file":"register.component.ts","title":"Register","description":"Register"}],"path":"templates/ui/blocks/auth","defaultOutput":"src/app/blocks/auth"},{"name":"settings","title":"Settings","description":"Settings pages: profile, notifications, security, danger zone","tags":["settings","preferences","account","profile","security"],"dependencies":["@spartan-ng/helm/button","@spartan-ng/helm/card","@spartan-ng/helm/form-field","@spartan-ng/helm/switch"],"files":[{"id":"danger-zone","file":"danger-zone.component.ts","title":"Danger Zone","description":"Danger Zone"},{"id":"notification","file":"notification-settings.component.ts","title":"Notification Settings","description":"Notification Settings"},{"id":"profile","file":"profile-settings.component.ts","title":"Profile Settings","description":"Profile Settings"},{"id":"security","file":"security-settings.component.ts","title":"Security Settings","description":"Security Settings"}],"path":"templates/ui/blocks/settings","defaultOutput":"src/app/blocks/settings"},{"name":"shell","title":"Shell","description":"Application shell layouts: sidebar, topnav, collapsible - each with optional footer variant","tags":["shell","layout","sidebar","header","footer","navigation","topnav","collapsible"],"dependencies":["@spartan-ng/helm/button","@spartan-ng/helm/icon","@spartan-ng/helm/dropdown-menu"],"files":[{"id":"collapsible-footer","file":"collapsible-shell-footer.component.ts","title":"Collapsible Shell Footer","description":"Collapsible Shell Footer"},{"id":"collapsible","file":"collapsible-shell.component.ts","title":"Collapsible Shell","description":"Collapsible Shell"},{"id":"sidebar-footer","file":"sidebar-shell-footer.component.ts","title":"Sidebar Shell Footer","description":"Sidebar Shell Footer"},{"id":"sidebar","file":"sidebar-shell.component.ts","title":"Sidebar Shell","description":"Sidebar Shell"},{"id":"topnav-footer","file":"topnav-shell-footer.component.ts","title":"Topnav Shell Footer","description":"Topnav Shell Footer"},{"id":"topnav","file":"topnav-shell.component.ts","title":"Topnav Shell","description":"Topnav Shell"}],"path":"templates/ui/blocks/shell","defaultOutput":"src/app/blocks/shell"}]}}
|