@edtools/cli 0.6.2 → 0.7.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.
Files changed (43) hide show
  1. package/dist/adapters/html/index.d.ts +2 -2
  2. package/dist/adapters/html/index.d.ts.map +1 -1
  3. package/dist/adapters/html/index.js +39 -5
  4. package/dist/adapters/html/index.js.map +1 -1
  5. package/dist/adapters/html/templates/base-layout.ejs +173 -0
  6. package/dist/adapters/html/templates/blog-index.html.ejs +170 -101
  7. package/dist/adapters/html/templates/blog-post-enhanced.ejs +655 -0
  8. package/dist/adapters/html/templates/blog-post-new.ejs +612 -0
  9. package/dist/adapters/html/templates/components/footer.ejs +330 -0
  10. package/dist/adapters/html/templates/components/header.ejs +208 -0
  11. package/dist/adapters/html/templates/components/social-share.ejs +202 -0
  12. package/dist/chunk-5N3D47CJ.js +823 -0
  13. package/dist/chunk-EPM74QNH.js +844 -0
  14. package/dist/chunk-OVFZRN7O.js +850 -0
  15. package/dist/chunk-TROAGFSZ.js +824 -0
  16. package/dist/chunk-U77FH5BI.js +823 -0
  17. package/dist/cli/commands/config.d.ts +17 -0
  18. package/dist/cli/commands/config.d.ts.map +1 -0
  19. package/dist/cli/commands/config.js +140 -0
  20. package/dist/cli/commands/config.js.map +1 -0
  21. package/dist/cli/commands/generate.d.ts.map +1 -1
  22. package/dist/cli/commands/generate.js +57 -14
  23. package/dist/cli/commands/generate.js.map +1 -1
  24. package/dist/cli/commands/init.d.ts.map +1 -1
  25. package/dist/cli/commands/init.js +114 -0
  26. package/dist/cli/commands/init.js.map +1 -1
  27. package/dist/cli/index.js +623 -288
  28. package/dist/cli/index.js.map +1 -1
  29. package/dist/core/generator.d.ts +4 -1
  30. package/dist/core/generator.d.ts.map +1 -1
  31. package/dist/core/generator.js +118 -3
  32. package/dist/core/generator.js.map +1 -1
  33. package/dist/index.d.ts +87 -3
  34. package/dist/index.js +1 -1
  35. package/dist/types/adapter.d.ts +17 -2
  36. package/dist/types/adapter.d.ts.map +1 -1
  37. package/dist/types/adapter.js.map +1 -1
  38. package/dist/types/content.d.ts +66 -0
  39. package/dist/types/content.d.ts.map +1 -1
  40. package/dist/ui/banner.d.ts.map +1 -1
  41. package/dist/ui/banner.js +18 -2
  42. package/dist/ui/banner.js.map +1 -1
  43. package/package.json +2 -2
@@ -1,19 +1,40 @@
1
1
  <!DOCTYPE html>
2
- <html lang="en">
2
+ <html lang="<%= blogConfig.language || 'en' %>">
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title><%= siteName %> - Blog</title>
7
- <meta name="description" content="<%= siteDescription %>">
6
+
7
+ <!-- SEO Meta Tags -->
8
+ <title><%= blogConfig.name || siteName %></title>
9
+ <meta name="description" content="<%= blogConfig.description || siteDescription %>">
8
10
  <meta name="robots" content="index, follow">
9
11
 
12
+ <!-- Canonical URL -->
13
+ <link rel="canonical" href="<%= siteUrl %>">
14
+
10
15
  <!-- Open Graph -->
11
- <meta property="og:title" content="<%= siteName %> - Blog">
12
- <meta property="og:description" content="<%= siteDescription %>">
16
+ <meta property="og:title" content="<%= blogConfig.name || siteName %>">
17
+ <meta property="og:description" content="<%= blogConfig.description || siteDescription %>">
13
18
  <meta property="og:type" content="website">
14
19
  <meta property="og:url" content="<%= siteUrl %>">
15
20
 
21
+ <!-- Twitter Card -->
22
+ <meta name="twitter:card" content="summary_large_image">
23
+ <meta name="twitter:title" content="<%= blogConfig.name || siteName %>">
24
+ <meta name="twitter:description" content="<%= blogConfig.description || siteDescription %>">
25
+
26
+ <!-- RSS Feed -->
27
+ <link rel="alternate" type="application/rss+xml" title="<%= blogConfig.name || siteName %> RSS Feed" href="/blog/rss.xml">
28
+
16
29
  <style>
30
+ :root {
31
+ --primary-color: <%= blogConfig.primaryColor || '#2563eb' %>;
32
+ --text-color: <%= blogConfig.textColor || '#1f2937' %>;
33
+ --text-light: <%= blogConfig.textLight || '#6b7280' %>;
34
+ --border-color: <%= blogConfig.borderColor || '#e5e7eb' %>;
35
+ --bg-light: <%= blogConfig.bgLight || '#f9fafb' %>;
36
+ }
37
+
17
38
  * {
18
39
  margin: 0;
19
40
  padding: 0;
@@ -21,42 +42,67 @@
21
42
  }
22
43
 
23
44
  body {
24
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica', 'Arial', sans-serif;
45
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
25
46
  line-height: 1.6;
26
- color: #333;
27
- background-color: #f9fafb;
47
+ color: var(--text-color);
48
+ background-color: var(--bg-light);
28
49
  }
29
50
 
30
51
  .container {
31
52
  max-width: 1200px;
32
53
  margin: 0 auto;
33
- padding: 20px;
54
+ padding: 2rem 1rem;
34
55
  }
35
56
 
36
- header {
37
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
38
- color: white;
39
- padding: 60px 20px;
57
+ .breadcrumbs {
58
+ display: flex;
59
+ align-items: center;
60
+ gap: 0.5rem;
61
+ font-size: 0.875rem;
62
+ color: var(--text-light);
63
+ margin-bottom: 2rem;
64
+ }
65
+
66
+ .breadcrumbs a {
67
+ color: var(--primary-color);
68
+ text-decoration: none;
69
+ }
70
+
71
+ .breadcrumbs a:hover {
72
+ text-decoration: underline;
73
+ }
74
+
75
+ .breadcrumbs span {
76
+ color: var(--text-light);
77
+ }
78
+
79
+ .blog-header {
40
80
  text-align: center;
41
- margin-bottom: 40px;
81
+ padding: 3rem 1rem;
82
+ background: linear-gradient(135deg, var(--primary-color) 0%, #764ba2 100%);
83
+ color: white;
84
+ border-radius: 1rem;
85
+ margin-bottom: 3rem;
42
86
  }
43
87
 
44
- header h1 {
45
- font-size: 3em;
46
- margin-bottom: 10px;
88
+ .blog-header h1 {
89
+ font-size: 3rem;
47
90
  font-weight: 700;
91
+ margin-bottom: 1rem;
48
92
  }
49
93
 
50
- header p {
51
- font-size: 1.2em;
52
- opacity: 0.9;
94
+ .blog-header p {
95
+ font-size: 1.25rem;
96
+ opacity: 0.95;
97
+ max-width: 600px;
98
+ margin: 0 auto;
53
99
  }
54
100
 
55
101
  .stats {
56
102
  display: flex;
57
103
  justify-content: center;
58
- gap: 40px;
59
- margin-top: 30px;
104
+ gap: 3rem;
105
+ margin-top: 2rem;
60
106
  flex-wrap: wrap;
61
107
  }
62
108
 
@@ -65,44 +111,46 @@
65
111
  }
66
112
 
67
113
  .stat-number {
68
- font-size: 2.5em;
69
- font-weight: bold;
114
+ font-size: 2.5rem;
115
+ font-weight: 700;
70
116
  display: block;
71
117
  }
72
118
 
73
119
  .stat-label {
74
- font-size: 0.9em;
75
- opacity: 0.8;
120
+ font-size: 0.875rem;
121
+ opacity: 0.9;
76
122
  text-transform: uppercase;
77
- letter-spacing: 1px;
123
+ letter-spacing: 0.05em;
78
124
  }
79
125
 
80
126
  .posts-grid {
81
127
  display: grid;
82
128
  grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
83
- gap: 30px;
84
- margin-top: 40px;
129
+ gap: 2rem;
130
+ margin-top: 2rem;
85
131
  }
86
132
 
87
133
  .post-card {
88
134
  background: white;
89
- border-radius: 12px;
135
+ border: 1px solid var(--border-color);
136
+ border-radius: 0.75rem;
90
137
  overflow: hidden;
91
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
92
- transition: transform 0.3s ease, box-shadow 0.3s ease;
138
+ transition: all 0.3s ease;
93
139
  text-decoration: none;
94
140
  color: inherit;
95
141
  display: flex;
96
142
  flex-direction: column;
143
+ height: 100%;
97
144
  }
98
145
 
99
146
  .post-card:hover {
100
- transform: translateY(-5px);
101
- box-shadow: 0 8px 15px rgba(0, 0, 0, 0.15);
147
+ transform: translateY(-4px);
148
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
149
+ border-color: var(--primary-color);
102
150
  }
103
151
 
104
152
  .post-card-body {
105
- padding: 25px;
153
+ padding: 1.5rem;
106
154
  flex: 1;
107
155
  display: flex;
108
156
  flex-direction: column;
@@ -110,115 +158,112 @@
110
158
 
111
159
  .post-meta {
112
160
  display: flex;
113
- gap: 15px;
114
- font-size: 0.85em;
115
- color: #6b7280;
116
- margin-bottom: 15px;
161
+ gap: 0.75rem;
162
+ font-size: 0.875rem;
163
+ color: var(--text-light);
164
+ margin-bottom: 1rem;
117
165
  flex-wrap: wrap;
166
+ align-items: center;
118
167
  }
119
168
 
120
169
  .post-category {
121
- background: #e0e7ff;
122
- color: #4f46e5;
123
- padding: 4px 12px;
124
- border-radius: 12px;
170
+ background: var(--primary-color);
171
+ color: white;
172
+ padding: 0.25rem 0.75rem;
173
+ border-radius: 9999px;
125
174
  font-weight: 600;
175
+ font-size: 0.75rem;
126
176
  text-transform: uppercase;
127
- font-size: 0.75em;
128
- letter-spacing: 0.5px;
177
+ letter-spacing: 0.025em;
129
178
  }
130
179
 
131
180
  .post-read-time {
132
181
  display: flex;
133
182
  align-items: center;
134
- gap: 5px;
183
+ gap: 0.25rem;
135
184
  }
136
185
 
137
186
  .post-title {
138
- font-size: 1.5em;
187
+ font-size: 1.5rem;
139
188
  font-weight: 700;
140
- margin-bottom: 12px;
141
- color: #1f2937;
189
+ margin-bottom: 0.75rem;
190
+ color: var(--text-color);
142
191
  line-height: 1.3;
143
192
  }
144
193
 
145
194
  .post-description {
146
- color: #4b5563;
147
- margin-bottom: 15px;
195
+ color: var(--text-light);
196
+ margin-bottom: 1rem;
148
197
  flex: 1;
198
+ line-height: 1.6;
149
199
  }
150
200
 
151
201
  .post-tags {
152
202
  display: flex;
153
203
  flex-wrap: wrap;
154
- gap: 8px;
204
+ gap: 0.5rem;
155
205
  margin-top: auto;
156
- padding-top: 15px;
157
- border-top: 1px solid #e5e7eb;
206
+ padding-top: 1rem;
207
+ border-top: 1px solid var(--border-color);
158
208
  }
159
209
 
160
210
  .tag {
161
- background: #f3f4f6;
162
- color: #6b7280;
163
- padding: 4px 10px;
164
- border-radius: 6px;
165
- font-size: 0.8em;
211
+ background: var(--bg-light);
212
+ color: var(--text-light);
213
+ padding: 0.25rem 0.5rem;
214
+ border-radius: 0.25rem;
215
+ font-size: 0.8125rem;
216
+ border: 1px solid var(--border-color);
166
217
  }
167
218
 
168
219
  .seo-score {
169
220
  display: inline-flex;
170
221
  align-items: center;
171
- gap: 5px;
172
- font-size: 0.85em;
222
+ gap: 0.25rem;
223
+ font-size: 0.875rem;
173
224
  }
174
225
 
175
226
  .score-badge {
176
- background: #10b981;
177
- color: white;
178
- padding: 2px 8px;
179
- border-radius: 8px;
227
+ padding: 0.125rem 0.5rem;
228
+ border-radius: 0.25rem;
180
229
  font-weight: 600;
230
+ font-size: 0.75rem;
181
231
  }
182
232
 
183
233
  .score-badge.good {
184
- background: #10b981;
234
+ background: #d1fae5;
235
+ color: #065f46;
185
236
  }
186
237
 
187
238
  .score-badge.medium {
188
- background: #f59e0b;
239
+ background: #fef3c7;
240
+ color: #92400e;
189
241
  }
190
242
 
191
243
  .score-badge.low {
192
- background: #ef4444;
244
+ background: #fee2e2;
245
+ color: #991b1b;
193
246
  }
194
247
 
195
248
  .loading {
196
249
  text-align: center;
197
- padding: 100px 20px;
198
- font-size: 1.2em;
199
- color: #6b7280;
250
+ padding: 5rem 1rem;
251
+ font-size: 1.125rem;
252
+ color: var(--text-light);
200
253
  }
201
254
 
202
255
  .error {
203
256
  background: #fee2e2;
204
257
  border: 1px solid #fecaca;
205
258
  color: #991b1b;
206
- padding: 20px;
207
- border-radius: 8px;
208
- text-align: center;
209
- }
210
-
211
- footer {
212
- margin-top: 80px;
213
- padding: 40px 20px;
214
- background: #1f2937;
215
- color: white;
259
+ padding: 1.5rem;
260
+ border-radius: 0.5rem;
216
261
  text-align: center;
217
262
  }
218
263
 
219
264
  @media (max-width: 768px) {
220
- header h1 {
221
- font-size: 2em;
265
+ .blog-header h1 {
266
+ font-size: 2rem;
222
267
  }
223
268
 
224
269
  .posts-grid {
@@ -226,20 +271,31 @@
226
271
  }
227
272
 
228
273
  .stats {
229
- gap: 20px;
274
+ gap: 1.5rem;
230
275
  }
231
276
 
232
277
  .stat-number {
233
- font-size: 2em;
278
+ font-size: 2rem;
234
279
  }
235
280
  }
236
281
  </style>
237
282
  </head>
238
283
  <body>
239
- <header>
240
- <div class="container">
241
- <h1><%= siteName %></h1>
242
- <p><%= siteDescription %></p>
284
+ <!-- Global Header -->
285
+ <%- include('components/header', { blogConfig }) %>
286
+
287
+ <div class="container">
288
+ <!-- Breadcrumbs -->
289
+ <nav class="breadcrumbs" aria-label="Breadcrumb">
290
+ <a href="<%= blogConfig.websiteUrl || '/' %>">Home</a>
291
+ <span>/</span>
292
+ <span>Blog</span>
293
+ </nav>
294
+
295
+ <!-- Blog Header -->
296
+ <div class="blog-header">
297
+ <h1><%= blogConfig.name || siteName %></h1>
298
+ <p><%= blogConfig.description || siteDescription %></p>
243
299
  <div class="stats" id="stats">
244
300
  <div class="stat">
245
301
  <span class="stat-number" id="total-posts">-</span>
@@ -251,27 +307,29 @@
251
307
  </div>
252
308
  <div class="stat">
253
309
  <span class="stat-number" id="avg-read">-</span>
254
- <span class="stat-label">Avg Read Time</span>
310
+ <span class="stat-label">Avg Read</span>
255
311
  </div>
256
312
  </div>
257
313
  </div>
258
- </header>
259
314
 
260
- <div class="container">
315
+ <!-- Loading State -->
261
316
  <div id="loading" class="loading">
262
317
  Loading posts...
263
318
  </div>
319
+
320
+ <!-- Error State -->
264
321
  <div id="error" class="error" style="display: none;">
265
322
  Failed to load posts. Please try again later.
266
323
  </div>
324
+
325
+ <!-- Posts Grid -->
267
326
  <div id="posts" class="posts-grid" style="display: none;">
268
- <!-- Posts will be loaded here -->
327
+ <!-- Posts will be loaded here via JavaScript -->
269
328
  </div>
270
329
  </div>
271
330
 
272
- <footer>
273
- <p>&copy; <%= new Date().getFullYear() %> <%= siteName %>. All rights reserved.</p>
274
- </footer>
331
+ <!-- Global Footer -->
332
+ <%- include('components/footer', { blogConfig }) %>
275
333
 
276
334
  <script>
277
335
  // Load and display blog posts from index.json
@@ -291,7 +349,12 @@
291
349
  const postsContainer = document.getElementById('posts');
292
350
  postsContainer.innerHTML = '';
293
351
 
294
- data.posts.forEach(post => {
352
+ // Sort posts by date (newest first)
353
+ const sortedPosts = [...data.posts].sort((a, b) =>
354
+ new Date(b.publishDate) - new Date(a.publishDate)
355
+ );
356
+
357
+ sortedPosts.forEach(post => {
295
358
  const card = document.createElement('a');
296
359
  card.href = post.url;
297
360
  card.className = 'post-card';
@@ -301,16 +364,22 @@
301
364
  card.innerHTML = `
302
365
  <div class="post-card-body">
303
366
  <div class="post-meta">
304
- <span class="post-category">${post.category}</span>
305
- <span class="post-read-time">📖 ${post.readTime}</span>
367
+ <span class="post-category">${escapeHtml(post.category)}</span>
368
+ <span class="post-read-time">
369
+ <svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
370
+ <circle cx="12" cy="12" r="10"></circle>
371
+ <polyline points="12 6 12 12 16 14"></polyline>
372
+ </svg>
373
+ ${escapeHtml(post.readTime)}
374
+ </span>
306
375
  <span class="seo-score">
307
- SEO: <span class="score-badge ${scoreClass}">${post.seo.seoScore}</span>
376
+ SEO <span class="score-badge ${scoreClass}">${post.seo.seoScore}</span>
308
377
  </span>
309
378
  </div>
310
379
  <h2 class="post-title">${escapeHtml(post.title)}</h2>
311
380
  <p class="post-description">${escapeHtml(post.shortDescription)}</p>
312
381
  <div class="post-tags">
313
- ${post.tags.map(tag => `<span class="tag">#${escapeHtml(tag)}</span>`).join('')}
382
+ ${post.tags.slice(0, 4).map(tag => `<span class="tag">#${escapeHtml(tag)}</span>`).join('')}
314
383
  </div>
315
384
  </div>
316
385
  `;