create-template-html-css 1.3.0 → 1.4.1

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/CHANGELOG.md CHANGED
@@ -5,6 +5,54 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.4.1] - 2025-01-29
9
+
10
+ ### Improved
11
+ - **Enhanced Insert Feature**:
12
+ - Better indentation handling - respects existing HTML file formatting
13
+ - Improved duplicate detection - prevents inserting same component twice
14
+ - Unique component IDs - all styles/scripts get unique identifiers
15
+ - File validation - checks if HTML file exists before insertion
16
+ - All 19 components now available in insert command (was only 9 basic ones)
17
+
18
+ - **Improved CLI**:
19
+ - Better visual design with emojis and separators
20
+ - Organized component lists with categories
21
+ - File existence validation before insertion
22
+ - Enhanced help text and examples
23
+ - Better status messages with clear summaries
24
+ - Separated files as default for CSS insertion
25
+
26
+ ### Technical Details
27
+ - Simplified indentation detection using HTML file's existing format
28
+ - Added `path` and `fs` imports for better file handling
29
+ - Improved error messages for better debugging
30
+ - Extended insert command with all template categories
31
+
32
+ ## [1.4.0] - 2025-01-18
33
+
34
+ ### Added
35
+ - **Flexbox Layout Templates**:
36
+ - `flex-layout` - Comprehensive Flexbox patterns and examples (row, column, space-between, space-around, space-evenly, center, wrap, flex-grow, alignment variations, Holy Grail layout)
37
+ - `flex-cards` - Equal-height card layouts with Flexbox (pricing cards, product cards, team cards, testimonial cards with automatic equal heights and gradient backgrounds)
38
+ - `flex-dashboard` - Complete admin dashboard using Flexbox (collapsible sidebar, top bar with search, stats cards, bar chart, activity feed, top products, quick actions)
39
+
40
+ ### Changed
41
+ - Updated template count from 16 to 19 templates
42
+ - Enhanced `generator.js` and `inserter.js` with 3 new Flexbox components
43
+ - Updated CLI choices in `bin/cli.js` with Flexbox templates
44
+ - Enhanced `list` command to show new Flexbox category
45
+ - Updated README.md with detailed Flexbox template documentation
46
+ - Added `flexbox`, `flex-layout`, and `flex-dashboard` keywords to package.json
47
+
48
+ ### Technical Details
49
+ - All Flexbox templates include full HTML, CSS, and JavaScript implementations
50
+ - Interactive sidebar collapse functionality
51
+ - Responsive design using pure Flexbox (no CSS Grid)
52
+ - Modern animations and hover effects
53
+ - Equal-height card systems without hacks
54
+ - Professional dashboard layouts with Flexbox
55
+
8
56
  ## [1.3.0] - 2025-01-18
9
57
 
10
58
  ### Added
package/INSERT-DEMO.md CHANGED
@@ -2,7 +2,16 @@
2
2
 
3
3
  ## How to Use the Insert Command
4
4
 
5
- The `insert` command allows you to add pre-styled components to your existing HTML pages without creating new projects.
5
+ The `insert` command allows you to add pre-styled components to your existing HTML pages without creating new projects. Components are smartly integrated with proper indentation and ID attributes for easy customization.
6
+
7
+ ## How It Works
8
+
9
+ When inserting a component:
10
+ 1. ✅ Detects existing indentation in your HTML file
11
+ 2. ✅ Respects your code formatting style
12
+ 3. ✅ Adds unique IDs to styles and scripts (e.g., `button-styles`, `button-script`)
13
+ 4. ✅ Prevents duplicate insertions (warns if component already exists)
14
+ 5. ✅ Validates HTML structure (checks for `<head>` and `<body>` tags)
6
15
 
7
16
  ## Example Usage
8
17
 
@@ -132,7 +141,9 @@ npm run insert
132
141
 
133
142
  ## Available Components
134
143
 
135
- All 7 templates are available for insertion:
144
+ All 19 templates are available for insertion:
145
+
146
+ ### Basic Components
136
147
  - Button - Styled button variations
137
148
  - Card - Product/content cards
138
149
  - Form - Contact/input forms
@@ -140,3 +151,21 @@ All 7 templates are available for insertion:
140
151
  - Modal - Dialog popups
141
152
  - Footer - Page footer
142
153
  - Hero - Hero sections with CTA
154
+ - Slider - Image carousel
155
+ - Table - Data tables with features
156
+
157
+ ### Animation Templates
158
+ - Spinner - Loading animations
159
+ - Animated Card - Interactive card effects
160
+ - Typing Effect - Text animations
161
+ - Fade Gallery - Image gallery with effects
162
+
163
+ ### Grid Layouts (CSS Grid)
164
+ - Grid Layout - CSS Grid examples
165
+ - Masonry Grid - Pinterest-style layout
166
+ - Dashboard Grid - Admin dashboard
167
+
168
+ ### Flexbox Layouts
169
+ - Flex Layout - Flexbox patterns
170
+ - Flex Cards - Equal-height cards
171
+ - Flex Dashboard - Admin dashboard with Flexbox
package/README.md CHANGED
@@ -26,7 +26,7 @@ A powerful CLI library to create HTML+CSS element templates. Generate styled UI
26
26
  - 🌈 **Stunning Gradients** - Colorful and attractive designs
27
27
  - ⚡ **Animations** - Smooth and professional effects
28
28
  - 🔧 **Customizable** - Easy to edit and modify the code
29
- - 📦 **16 Templates** - Button, Card, Form, Navigation, Modal, Footer, Hero, Slider, Table, Spinner, Animated Cards, Typing Effect, Fade Gallery, Grid Layouts, Masonry Grid, Dashboard
29
+ - 📦 **19 Templates** - Button, Card, Form, Navigation, Modal, Footer, Hero, Slider, Table, Spinner, Animated Cards, Typing Effect, Fade Gallery, Grid Layouts, Masonry Grid, Dashboard, Flexbox Layouts
30
30
  - 🎯 **Two Modes** - Create new projects or insert into existing HTML pages
31
31
  - 🔒 **Secure** - Input validation and path protection
32
32
  - 📚 **Well Documented** - Comprehensive guides and examples
@@ -407,6 +407,60 @@ Complete admin dashboard template:
407
407
  - Mobile responsive
408
408
  - Professional design
409
409
 
410
+ ### 17. Flex Layout 📏
411
+
412
+ Comprehensive Flexbox patterns and examples:
413
+
414
+ **Includes:**
415
+ - Row and column layouts
416
+ - Space distribution patterns
417
+ - Alignment variations
418
+ - Flex-grow examples
419
+ - Nested flex containers
420
+ - Holy Grail layout
421
+
422
+ **Features:**
423
+ - Interactive examples
424
+ - Visual demonstrations
425
+ - Responsive design
426
+ - Modern Flexbox techniques
427
+
428
+ ### 18. Flex Cards 🃏
429
+
430
+ Equal-height card layouts with Flexbox:
431
+
432
+ **Variations:**
433
+ - Basic equal-height cards
434
+ - Pricing cards
435
+ - Product cards
436
+ - Team member cards
437
+ - Testimonial cards
438
+
439
+ **Features:**
440
+ - Automatic equal heights
441
+ - Hover animations
442
+ - Gradient backgrounds
443
+ - Fully responsive
444
+
445
+ ### 19. Flex Dashboard 🎛️
446
+
447
+ Admin dashboard built entirely with Flexbox:
448
+
449
+ **Components:**
450
+ - Collapsible sidebar
451
+ - Top search bar
452
+ - Stats cards
453
+ - Bar chart visualization
454
+ - Activity feed
455
+ - Top products list
456
+ - Quick actions
457
+
458
+ **Features:**
459
+ - Pure Flexbox layout
460
+ - Mobile responsive
461
+ - Interactive animations
462
+ - Professional design
463
+
410
464
 
411
465
  ## 💡 Examples
412
466
 
package/bin/cli.js CHANGED
@@ -2,26 +2,40 @@
2
2
 
3
3
  const { program } = require('commander');
4
4
  const inquirer = require('inquirer').default || require('inquirer');
5
+ const path = require('path');
6
+ const fs = require('fs').promises;
5
7
  const { generateTemplate } = require('../src/generator');
6
8
  const { insertComponent } = require('../src/inserter');
7
9
  const chalk = require('chalk');
8
10
 
9
11
  program
10
12
  .name('create-template')
11
- .description('CLI tool to generate HTML and CSS templates')
12
- .version('1.0.0');
13
+ .description(chalk.cyan('🎨 Create HTML/CSS UI component templates in seconds'))
14
+ .version('1.4.0');
15
+
16
+ // Add intro message
17
+ program.on('--help', () => {
18
+ console.log('\n' + chalk.cyan('Examples:'));
19
+ console.log(' $ create-template create # Create a new template');
20
+ console.log(' $ create-template insert # Insert into existing HTML');
21
+ console.log(' $ create-template list # List all templates');
22
+ console.log('');
23
+ });
13
24
 
14
25
  program
15
26
  .command('create')
16
- .description('Create a new HTML/CSS template')
27
+ .description(chalk.green('Create a new HTML/CSS template component'))
17
28
  .action(async () => {
18
29
  try {
30
+ console.log(chalk.cyan('\n✨ Creating a new template component...\n'));
31
+
19
32
  const answers = await inquirer.prompt([
20
33
  {
21
34
  type: 'list',
22
35
  name: 'component',
23
36
  message: 'What component would you like to create?',
24
37
  choices: [
38
+ new inquirer.Separator(chalk.gray('─ Basic Components')),
25
39
  { name: 'Button', value: 'button' },
26
40
  { name: 'Card', value: 'card' },
27
41
  { name: 'Form', value: 'form' },
@@ -31,13 +45,19 @@ program
31
45
  { name: 'Hero Section', value: 'hero' },
32
46
  { name: 'Slider', value: 'slider' },
33
47
  { name: 'Table', value: 'table' },
48
+ new inquirer.Separator(chalk.gray('─ Animation Templates')),
34
49
  { name: 'Spinner (Loading Animations)', value: 'spinner' },
35
50
  { name: 'Animated Card (Interactive Cards)', value: 'animated-card' },
36
51
  { name: 'Typing Effect (Text Animations)', value: 'typing-effect' },
37
52
  { name: 'Fade Gallery (Image Gallery)', value: 'fade-gallery' },
38
- { name: 'Grid Layout (CSS Grid)', value: 'grid-layout' },
53
+ new inquirer.Separator(chalk.gray('─ Grid Layouts (CSS Grid)')),
54
+ { name: 'Grid Layout', value: 'grid-layout' },
39
55
  { name: 'Masonry Grid (Pinterest-style)', value: 'masonry-grid' },
40
- { name: 'Dashboard Grid (Admin Panel)', value: 'dashboard-grid' }
56
+ { name: 'Dashboard Grid (Admin Panel)', value: 'dashboard-grid' },
57
+ new inquirer.Separator(chalk.gray('─ Flexbox Layouts')),
58
+ { name: 'Flex Layout (Flexbox Patterns)', value: 'flex-layout' },
59
+ { name: 'Flex Cards (Equal-height cards)', value: 'flex-cards' },
60
+ { name: 'Flex Dashboard (Flexbox Admin)', value: 'flex-dashboard' }
41
61
  ]
42
62
  },
43
63
  {
@@ -62,37 +82,50 @@ program
62
82
  type: 'confirm',
63
83
  name: 'includeJs',
64
84
  message: 'Include JavaScript file?',
65
- default: false
85
+ default: true
66
86
  }
67
87
  ]);
68
88
 
69
89
  await generateTemplate(answers);
70
- console.log(chalk.green('✓ Template created successfully!'));
90
+
91
+ console.log('\n' + chalk.green('✓ Template created successfully!'));
92
+ console.log(chalk.gray(` Location: ./${answers.name}/`));
93
+ console.log(chalk.gray(` Files: index.html, style.css${answers.includeJs ? ', script.js' : ''}`));
94
+ console.log('');
71
95
  } catch (error) {
72
- console.error(chalk.red('Error creating template:'), error.message);
96
+ console.error(chalk.red('Error:'), error.message);
73
97
  process.exit(1);
74
98
  }
75
99
  });
76
100
 
77
101
  program
78
102
  .command('insert')
79
- .description('Insert a component into an existing HTML page')
103
+ .description(chalk.green('Insert a component into an existing HTML page'))
80
104
  .action(async () => {
81
105
  try {
106
+ console.log(chalk.cyan('\n🚀 Inserting component into HTML file...\n'));
107
+
82
108
  const answers = await inquirer.prompt([
83
109
  {
84
110
  type: 'input',
85
111
  name: 'targetFile',
86
112
  message: 'Enter the path to your HTML file:',
87
113
  default: 'index.html',
88
- validate: (input) => {
114
+ validate: async (input) => {
89
115
  if (!input || input.trim().length === 0) {
90
116
  return 'Please enter a file path';
91
117
  }
92
118
  if (!input.toLowerCase().endsWith('.html')) {
93
119
  return 'File must be an HTML file (.html)';
94
120
  }
95
- return true;
121
+
122
+ // Check if file exists
123
+ try {
124
+ await fs.access(path.resolve(process.cwd(), input));
125
+ return true;
126
+ } catch {
127
+ return `File not found: ${input}`;
128
+ }
96
129
  }
97
130
  },
98
131
  {
@@ -100,6 +133,7 @@ program
100
133
  name: 'component',
101
134
  message: 'Which component would you like to insert?',
102
135
  choices: [
136
+ new inquirer.Separator(chalk.gray('─ Basic Components')),
103
137
  { name: 'Button', value: 'button' },
104
138
  { name: 'Card', value: 'card' },
105
139
  { name: 'Form', value: 'form' },
@@ -108,74 +142,106 @@ program
108
142
  { name: 'Footer', value: 'footer' },
109
143
  { name: 'Hero Section', value: 'hero' },
110
144
  { name: 'Slider', value: 'slider' },
111
- { name: 'Table', value: 'table' }
145
+ { name: 'Table', value: 'table' },
146
+ new inquirer.Separator(chalk.gray('─ Animation Templates')),
147
+ { name: 'Spinner', value: 'spinner' },
148
+ { name: 'Animated Card', value: 'animated-card' },
149
+ { name: 'Typing Effect', value: 'typing-effect' },
150
+ { name: 'Fade Gallery', value: 'fade-gallery' },
151
+ new inquirer.Separator(chalk.gray('─ Grid Layouts')),
152
+ { name: 'Grid Layout', value: 'grid-layout' },
153
+ { name: 'Masonry Grid', value: 'masonry-grid' },
154
+ { name: 'Dashboard Grid', value: 'dashboard-grid' },
155
+ new inquirer.Separator(chalk.gray('─ Flexbox Layouts')),
156
+ { name: 'Flex Layout', value: 'flex-layout' },
157
+ { name: 'Flex Cards', value: 'flex-cards' },
158
+ { name: 'Flex Dashboard', value: 'flex-dashboard' }
112
159
  ]
113
160
  },
114
- {
115
- type: 'list',
116
- name: 'styleMode',
117
- message: 'How should the CSS be added?',
118
- choices: [
119
- { name: 'Inline (inside <style> tag)', value: 'inline' },
120
- { name: 'Separate file', value: 'separate' },
121
- { name: 'Skip (I\'ll add it manually)', value: 'skip' }
122
- ],
123
- default: 'inline'
124
- },
125
161
  {
126
162
  type: 'list',
127
163
  name: 'scriptMode',
128
164
  message: 'How should the JavaScript be added?',
129
165
  choices: [
166
+ { name: 'Separate file (recommended)', value: 'separate' },
130
167
  { name: 'Inline (inside <script> tag)', value: 'inline' },
131
- { name: 'Separate file', value: 'separate' },
132
168
  { name: 'Skip (I\'ll add it manually)', value: 'skip' }
133
169
  ],
134
- default: 'inline'
170
+ default: 'separate'
135
171
  }
136
172
  ]);
137
173
 
174
+ // CSS is always separate (external)
175
+ answers.styleMode = 'separate';
176
+
138
177
  const result = await insertComponent(answers);
139
- console.log(chalk.green('\n✓ Component inserted successfully!'));
140
- console.log(chalk.cyan(` File: ${result.targetFile}`));
141
- console.log(chalk.cyan(` Component: ${result.component}`));
142
- console.log(chalk.cyan(` CSS: ${result.styleMode}`));
143
- console.log(chalk.cyan(` JS: ${result.scriptMode}`));
178
+
179
+ console.log('\n' + chalk.green('✓ Component inserted successfully!'));
180
+ console.log(chalk.cyan(' Summary:'));
181
+ console.log(chalk.gray(` File: ${path.relative(process.cwd(), result.targetFile)}`));
182
+ console.log(chalk.gray(` Component: ${chalk.bold(result.component)}`));
183
+ console.log(chalk.gray(` CSS: ${chalk.yellow('external file')}`));
184
+ console.log(chalk.gray(` JS: ${chalk.yellow(result.scriptMode)}`));
185
+ console.log(chalk.gray(`\n Component IDs: ${result.component}-styles, ${result.component}-script`));
186
+ console.log('');
144
187
  } catch (error) {
145
- console.error(chalk.red('Error inserting component:'), error.message);
188
+ console.error('\n' + chalk.red('Error:'), error.message);
146
189
  process.exit(1);
147
190
  }
148
191
  });
149
192
 
150
193
  program
151
194
  .command('list')
152
- .description('List all available templates')
195
+ .description(chalk.green('List all available templates'))
153
196
  .action(() => {
154
- console.log(chalk.blue('\n📦 Available templates (16):\n'));
155
- console.log(chalk.yellow('Basic Components:'));
156
- console.log(' Button - Styled button component');
157
- console.log(' Card - Card component with image and content');
158
- console.log(' Form - Form with input fields');
159
- console.log(' Navigation - Navigation bar');
160
- console.log(' Modal - Modal dialog');
161
- console.log(' Footer - Footer section');
162
- console.log(' Hero - Hero section with CTA');
163
- console.log(' Slider - Image carousel with navigation');
164
- console.log(' Table - Data table with search and filtering');
165
- console.log(chalk.yellow('\nAnimation Templates:'));
166
- console.log(' • Spinner - 5 loading spinner variations');
167
- console.log(' Animated Card - 6 interactive card animations');
168
- console.log(' Typing Effect - Text typing animations');
169
- console.log(' • Fade Gallery - Image gallery with fade effects');
170
- console.log(chalk.yellow('\nGrid Layouts:'));
171
- console.log(' Grid Layout - CSS Grid examples');
172
- console.log(' • Masonry Grid - Pinterest-style layout');
173
- console.log(' Dashboard Grid - Complete admin dashboard');
197
+ console.log('\n' + chalk.blue('📦 Available Components (19 total)\n'));
198
+
199
+ console.log(chalk.yellow(' Basic Components (9)'));
200
+ console.log(' button Styled button component');
201
+ console.log(' card Card component with image and content');
202
+ console.log(' form Form with input fields and validation');
203
+ console.log(' navigation Responsive navigation bar');
204
+ console.log(' modal Modal dialog component');
205
+ console.log(' footer Footer section');
206
+ console.log(' hero Hero section with CTA button');
207
+ console.log(' slider Image carousel with navigation');
208
+ console.log(' table Data table with search and filtering');
209
+
210
+ console.log('\n' + chalk.magenta('━ Animation Templates (4)'));
211
+ console.log(' spinner 5 loading spinner variations');
212
+ console.log(' animated-card 6 interactive card animations');
213
+ console.log(' typing-effect Text typing animations');
214
+ console.log(' fade-gallery Image gallery with fade effects');
215
+
216
+ console.log('\n' + chalk.cyan('━ Grid Layouts (3)'));
217
+ console.log(' grid-layout CSS Grid patterns and examples');
218
+ console.log(' masonry-grid Pinterest-style masonry layout');
219
+ console.log(' dashboard-grid Complete admin dashboard (Grid)');
220
+
221
+ console.log('\n' + chalk.green('━ Flexbox Layouts (3)'));
222
+ console.log(' flex-layout Flexbox patterns and examples');
223
+ console.log(' flex-cards Equal-height card layouts');
224
+ console.log(' flex-dashboard Complete admin dashboard (Flexbox)');
225
+
226
+ console.log('\n' + chalk.gray('Usage:'));
227
+ console.log(' create-template create Create a new component');
228
+ console.log(' create-template insert Insert into HTML file');
174
229
  console.log('');
175
230
  });
176
231
 
177
232
  program.parse(process.argv);
178
233
 
179
234
  if (!process.argv.slice(2).length) {
180
- program.outputHelp();
235
+ console.log('\n' + chalk.cyan('🎨 Create HTML/CSS UI Templates\n'));
236
+ console.log(chalk.white('Usage: create-template [command]') + '\n');
237
+ console.log(chalk.yellow('Commands:'));
238
+ console.log(' create Create a new template component');
239
+ console.log(' insert Insert component into existing HTML file');
240
+ console.log(' list Show all available templates');
241
+ console.log(' help Display help information\n');
242
+ console.log(chalk.gray('Examples:'));
243
+ console.log(' $ create-template create # Interactive template creation');
244
+ console.log(' $ create-template insert # Interactive component insertion');
245
+ console.log(' $ create-template list # View all 19 templates');
246
+ console.log(' $ create-template --help # Show full help\n');
181
247
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-template-html-css",
3
- "version": "1.3.0",
3
+ "version": "1.4.1",
4
4
  "description": "CLI tool to generate HTML and CSS templates for common UI elements",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -35,7 +35,10 @@
35
35
  "typing-effect",
36
36
  "gallery",
37
37
  "responsive",
38
- "modern-ui"
38
+ "modern-ui",
39
+ "flexbox",
40
+ "flex-layout",
41
+ "flex-dashboard"
39
42
  ],
40
43
  "author": "Ben Shabbat <benshabbat@example.com> (https://github.com/benshabbat)",
41
44
  "license": "MIT",
package/src/generator.js CHANGED
@@ -2,7 +2,7 @@ const fs = require('fs').promises;
2
2
  const path = require('path');
3
3
 
4
4
  // Security: Validate component name against whitelist
5
- const VALID_COMPONENTS = ['button', 'card', 'form', 'navigation', 'modal', 'footer', 'hero', 'slider', 'table', 'spinner', 'animated-card', 'typing-effect', 'fade-gallery', 'grid-layout', 'masonry-grid', 'dashboard-grid'];
5
+ const VALID_COMPONENTS = ['button', 'card', 'form', 'navigation', 'modal', 'footer', 'hero', 'slider', 'table', 'spinner', 'animated-card', 'typing-effect', 'fade-gallery', 'grid-layout', 'masonry-grid', 'dashboard-grid', 'flex-layout', 'flex-cards', 'flex-dashboard'];
6
6
 
7
7
  // Security: Sanitize filename to prevent path traversal
8
8
  function sanitizeFilename(filename) {
package/src/inserter.js CHANGED
@@ -2,7 +2,32 @@ const fs = require('fs').promises;
2
2
  const path = require('path');
3
3
 
4
4
  // Security: Validate component name against whitelist
5
- const VALID_COMPONENTS = ['button', 'card', 'form', 'navigation', 'modal', 'footer', 'hero', 'slider', 'table', 'spinner', 'animated-card', 'typing-effect', 'fade-gallery', 'grid-layout', 'masonry-grid', 'dashboard-grid'];
5
+ const VALID_COMPONENTS = ['button', 'card', 'form', 'navigation', 'modal', 'footer', 'hero', 'slider', 'table', 'spinner', 'animated-card', 'typing-effect', 'fade-gallery', 'grid-layout', 'masonry-grid', 'dashboard-grid', 'flex-layout', 'flex-cards', 'flex-dashboard'];
6
+
7
+ /**
8
+ * Extracts indentation from a line
9
+ */
10
+ function getIndentation(line) {
11
+ const match = line.match(/^(\s*)/);
12
+ return match ? match[1] : '';
13
+ }
14
+
15
+ /**
16
+ * Checks if a component is already inserted in the HTML
17
+ */
18
+ function isComponentAlreadyInserted(htmlContent, component) {
19
+ const commentPattern = new RegExp(`<!-- ${component.toUpperCase()} Component -->`, 'i');
20
+ return commentPattern.test(htmlContent);
21
+ }
22
+
23
+ /**
24
+ * Gets the indentation level used in an HTML file
25
+ */
26
+ function getHtmlIndentation(htmlContent) {
27
+ // Look for any indented line to determine the standard indentation
28
+ const match = htmlContent.match(/\n(\s+)\S/);
29
+ return match ? match[1] : ' '; // default to 4 spaces
30
+ }
6
31
 
7
32
  async function insertComponent(options) {
8
33
  const { component, targetFile, styleMode, scriptMode } = options;
@@ -23,12 +48,26 @@ async function insertComponent(options) {
23
48
  // Read target HTML file
24
49
  let htmlContent = await fs.readFile(targetPath, 'utf-8');
25
50
 
51
+ // Check if component is already inserted
52
+ if (isComponentAlreadyInserted(htmlContent, component)) {
53
+ throw new Error(`Component "${component}" is already inserted in this file`);
54
+ }
55
+
56
+ // Validate HTML structure
57
+ if (!htmlContent.includes('</body>')) {
58
+ throw new Error('Target HTML file does not have a closing </body> tag');
59
+ }
60
+
61
+ if (!htmlContent.includes('</head>')) {
62
+ throw new Error('Target HTML file does not have a </head> tag');
63
+ }
64
+
26
65
  // Get component templates
27
66
  const templateDir = path.join(__dirname, '..', 'templates', component);
28
67
  const componentHtml = await fs.readFile(path.join(templateDir, 'index.html'), 'utf-8');
29
68
  const componentCss = await fs.readFile(path.join(templateDir, 'style.css'), 'utf-8');
30
69
 
31
- // Extract component body content (between <body> tags, excluding container if needed)
70
+ // Extract component body content
32
71
  const bodyMatch = componentHtml.match(/<body[^>]*>([\s\S]*)<\/body>/i);
33
72
  if (!bodyMatch) {
34
73
  throw new Error('Invalid component template structure');
@@ -36,31 +75,36 @@ async function insertComponent(options) {
36
75
 
37
76
  let componentBody = bodyMatch[1].trim();
38
77
 
78
+ // Get indentation used in the HTML file
79
+ const baseIndent = getHtmlIndentation(htmlContent);
80
+
81
+ // Normalize component body indentation
82
+ const lines = componentBody.split('\n').map(line => {
83
+ if (line.trim() === '') return '';
84
+ return baseIndent + line.trim();
85
+ }).join('\n');
86
+ componentBody = lines;
87
+
39
88
  // Insert component HTML before closing </body> tag
40
- if (htmlContent.includes('</body>')) {
41
- htmlContent = htmlContent.replace('</body>', `\n <!-- ${component.toUpperCase()} Component -->\n${componentBody}\n</body>`);
42
- } else {
43
- throw new Error('Target HTML file does not have a closing </body> tag');
44
- }
89
+ htmlContent = htmlContent.replace('</body>', `${baseIndent}<!-- ${component.toUpperCase()} Component -->\n${componentBody}\n\n</body>`);
45
90
 
46
91
  // Handle CSS
47
92
  if (styleMode === 'inline') {
48
- // Add CSS in <style> tag before </head>
49
- const styleTag = `\n <style>\n /* ${component.toUpperCase()} Component Styles */\n${componentCss}\n </style>`;
50
- if (htmlContent.includes('</head>')) {
51
- htmlContent = htmlContent.replace('</head>', `${styleTag}\n</head>`);
52
- }
93
+ // Normalize CSS indentation
94
+ const normalizedCss = componentCss.split('\n').map(line => {
95
+ if (line.trim() === '') return '';
96
+ return baseIndent + ' ' + line.trim();
97
+ }).join('\n');
98
+
99
+ htmlContent = htmlContent.replace('</head>', `${baseIndent}<style id="${component}-styles">\n${baseIndent} /* ${component.toUpperCase()} Component Styles */\n${normalizedCss}\n${baseIndent}</style>\n</head>`);
53
100
  } else if (styleMode === 'separate') {
54
101
  // Create separate CSS file
55
102
  const cssFileName = `${component}-component.css`;
56
103
  const cssPath = path.join(path.dirname(targetPath), cssFileName);
57
- await fs.writeFile(cssPath, `/* ${component.toUpperCase()} Component Styles */\n${componentCss}`);
104
+ await fs.writeFile(cssPath, `/* ${component.toUpperCase()} Component Styles */\n\n${componentCss}`);
58
105
 
59
106
  // Add link to CSS file
60
- const linkTag = `\n <link rel="stylesheet" href="${cssFileName}">`;
61
- if (htmlContent.includes('</head>')) {
62
- htmlContent = htmlContent.replace('</head>', `${linkTag}\n</head>`);
63
- }
107
+ htmlContent = htmlContent.replace('</head>', `${baseIndent}<link rel="stylesheet" href="${cssFileName}">\n</head>`);
64
108
  }
65
109
 
66
110
  // Handle JavaScript
@@ -68,18 +112,21 @@ async function insertComponent(options) {
68
112
  const componentJs = await fs.readFile(path.join(templateDir, 'script.js'), 'utf-8');
69
113
 
70
114
  if (scriptMode === 'inline') {
71
- // Add JS in <script> tag before </body>
72
- const scriptTag = `\n <script>\n // ${component.toUpperCase()} Component Script\n${componentJs}\n </script>\n`;
73
- htmlContent = htmlContent.replace('</body>', `${scriptTag}</body>`);
115
+ // Normalize JS indentation
116
+ const normalizedJs = componentJs.split('\n').map(line => {
117
+ if (line.trim() === '') return '';
118
+ return baseIndent + ' ' + line.trim();
119
+ }).join('\n');
120
+
121
+ htmlContent = htmlContent.replace('</body>', `${baseIndent}<script id="${component}-script">\n${baseIndent} // ${component.toUpperCase()} Component Script\n${normalizedJs}\n${baseIndent}</script>\n</body>`);
74
122
  } else if (scriptMode === 'separate') {
75
123
  // Create separate JS file
76
124
  const jsFileName = `${component}-component.js`;
77
125
  const jsPath = path.join(path.dirname(targetPath), jsFileName);
78
- await fs.writeFile(jsPath, `// ${component.toUpperCase()} Component Script\n${componentJs}`);
126
+ await fs.writeFile(jsPath, `// ${component.toUpperCase()} Component Script\n\n${componentJs}`);
79
127
 
80
128
  // Add script tag
81
- const scriptTag = `\n <script src="${jsFileName}"></script>\n`;
82
- htmlContent = htmlContent.replace('</body>', `${scriptTag}</body>`);
129
+ htmlContent = htmlContent.replace('</body>', `${baseIndent}<script src="${jsFileName}" id="${component}-script"></script>\n</body>`);
83
130
  }
84
131
  } catch (error) {
85
132
  // No JavaScript file for this component, skip