vending-mocha 0.1.5 → 0.1.7

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/cli.js CHANGED
@@ -232,6 +232,39 @@ async function handleNew(projectName, options) {
232
232
  updatePackageJson(projectDir, config);
233
233
  console.log(chalk.green('✔ package.json updated'));
234
234
 
235
+ // 3. Create .gitignore (since npm pack ignores it)
236
+ const gitignorePath = path.join(projectDir, '.gitignore');
237
+ if (!fs.existsSync(gitignorePath)) {
238
+ const gitignoreContent = `# Logs
239
+ logs
240
+ *.log
241
+ npm-debug.log*
242
+ yarn-debug.log*
243
+ yarn-error.log*
244
+ pnpm-debug.log*
245
+ lerna-debug.log*
246
+
247
+ node_modules
248
+ dist
249
+ dist-ssr
250
+ docs
251
+ *.local
252
+
253
+ # Editor directories and files
254
+ .vscode/*
255
+ !.vscode/extensions.json
256
+ .idea
257
+ .DS_Store
258
+ *.suo
259
+ *.ntvs*
260
+ *.njsproj
261
+ *.sln
262
+ *.sw?
263
+ `;
264
+ fs.writeFileSync(gitignorePath, gitignoreContent);
265
+ console.log(chalk.green('✔ .gitignore created'));
266
+ }
267
+
235
268
  console.log(chalk.green(`\nSuccess! Created ${config.projectName} at ${projectDir}`));
236
269
  console.log('\nInside that directory, you can run:');
237
270
  console.log(chalk.cyan(` cd ${config.projectName}`));
@@ -263,7 +296,7 @@ async function handleUpgrade() {
263
296
  }
264
297
 
265
298
  console.log(chalk.yellow('WARNING: This will overwrite project files to the latest version of vending-mocha.'));
266
- console.log(chalk.yellow('Your content (posts/, projects/) and configuration (src/site.config.ts) will be preserved.'));
299
+ console.log(chalk.yellow('Your content (posts/, projects/, life/) and configuration (src/site.config.ts) will be preserved.'));
267
300
  console.log(chalk.yellow('Please ensure you have committed your changes before proceeding.'));
268
301
 
269
302
  const { proceed } = await inquirer.prompt([
@@ -329,6 +362,71 @@ async function handleUpgrade() {
329
362
  copyUpgradeSync(templateDir, currentDir);
330
363
  spinner.succeed();
331
364
 
365
+ // 2.5 Merge site.config.ts
366
+ try {
367
+ const userConfigPath = path.join(currentDir, 'src', 'site.config.ts');
368
+ const templateConfigPath = path.join(templateDir, 'src', 'site.config.ts');
369
+
370
+ if (fs.existsSync(userConfigPath) && fs.existsSync(templateConfigPath)) {
371
+ console.log(chalk.gray('Merging src/site.config.ts...'));
372
+ const templateConfigRaw = fs.readFileSync(templateConfigPath, 'utf8');
373
+ const tplMatch = templateConfigRaw.match(/([\s\S]*?)(export\s+const\s+siteConfig\s*=\s*)(\{[\s\S]*\});?\s*$/);
374
+
375
+ const userConfigRaw = fs.readFileSync(userConfigPath, 'utf8');
376
+ const userMatch = userConfigRaw.match(/([\s\S]*?)(export\s+const\s+siteConfig\s*=\s*)(\{[\s\S]*\});?\s*$/);
377
+
378
+ if (tplMatch && userMatch) {
379
+ const templateConfig = eval('(' + tplMatch[3] + ')');
380
+ const userPrefix = userMatch[1] + userMatch[2];
381
+ const userConfig = eval('(' + userMatch[3] + ')');
382
+
383
+ function mergeConfigs(target, source) {
384
+ for (const key in source) {
385
+ if (source[key] !== null && typeof source[key] === 'object' && !Array.isArray(source[key])) {
386
+ if (!target[key] || typeof target[key] !== 'object') {
387
+ target[key] = {};
388
+ }
389
+ mergeConfigs(target[key], source[key]);
390
+ } else if (target[key] === undefined) {
391
+ target[key] = source[key];
392
+ }
393
+ }
394
+ return target;
395
+ }
396
+
397
+ const mergedConfig = mergeConfigs(userConfig, templateConfig);
398
+
399
+ function stringifyConfig(obj, indent = 0) {
400
+ if (obj === undefined) return 'undefined';
401
+ if (obj === null) return 'null';
402
+ if (typeof obj === 'string') return JSON.stringify(obj);
403
+ if (typeof obj === 'number' || typeof obj === 'boolean') return String(obj);
404
+ if (Array.isArray(obj)) {
405
+ if (obj.length === 0) return '[]';
406
+ const items = obj.map(item => ' '.repeat(indent + 4) + stringifyConfig(item, indent + 4));
407
+ return `[\n${items.join(',\n')}\n${' '.repeat(indent)}]`;
408
+ }
409
+ if (typeof obj === 'object') {
410
+ const entries = Object.entries(obj);
411
+ if (entries.length === 0) return '{}';
412
+ const lines = entries.map(([key, value]) => {
413
+ const safeKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
414
+ return `${' '.repeat(indent + 4)}${safeKey}: ${stringifyConfig(value, indent + 4)}`;
415
+ });
416
+ return `{\n${lines.join(',\n')}\n${' '.repeat(indent)}}`;
417
+ }
418
+ return String(obj);
419
+ }
420
+
421
+ const outputStr = userPrefix + stringifyConfig(mergedConfig, 0) + ';\n';
422
+ fs.writeFileSync(userConfigPath, outputStr);
423
+ console.log(chalk.green('✔ src/site.config.ts merged'));
424
+ }
425
+ }
426
+ } catch (e) {
427
+ console.warn(chalk.yellow('Failed to merge src/site.config.ts: ' + e.message));
428
+ }
429
+
332
430
  // 3. Merge package.json
333
431
  console.log(chalk.gray('Merging package.json...'));
334
432
  const localPkg = JSON.parse(fs.readFileSync(localPkgPath, 'utf8'));
@@ -0,0 +1,6 @@
1
+ ---
2
+ title: "Initial Release"
3
+ date: "2026-02-23"
4
+ color: "#b3169bff"
5
+ description: "Support for blog posts and projects"
6
+ ---
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vending-mocha",
3
3
  "description": "A personal blogging framework for developers.",
4
- "version": "0.1.5",
4
+ "version": "0.1.7",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "bin": {
@@ -11,7 +11,7 @@
11
11
  "dev": "npm run build:all && vite",
12
12
  "build:client": "tsc -b && vite build --outDir docs",
13
13
  "build:server": "tsc -b && vite build --ssr src/entry-server.tsx --outDir dist/server",
14
- "prebuild": "node scripts/generate-posts-data.js && node scripts/generate-projects-data.js",
14
+ "prebuild": "node scripts/generate-posts-data.js && node scripts/generate-projects-data.js && node scripts/generate-life-data.js",
15
15
  "build": "npm run prebuild && npm run build:client && npm run build:server && node prerender.js && node scripts/generate-rss.js",
16
16
  "build:all": "npm run build && for d in projects/*; do if [ -d \"$d\" ]; then (cd \"$d\" && npm run build); fi; done",
17
17
  "preview": "npm run build:all && vite preview --outDir docs"
@@ -54,4 +54,4 @@
54
54
  "typescript-eslint": "^8.48.0",
55
55
  "vite": "^7.3.1"
56
56
  }
57
- }
57
+ }
@@ -0,0 +1,37 @@
1
+ ---
2
+ title: "Introducing the Life in Weeks Feature ⏳"
3
+ date: "2026-02-22"
4
+ summary: "A new feature to visualize your life in weeks"
5
+ ---
6
+
7
+ **Life in Weeks** ⏳. This feature allows you to visualize your entire life, past, present, and future, as a grid of small squares 🟩, where each square represents a single week.
8
+
9
+ This feature was deeply inspired by 💡: the **[Life in Weeks App](https://lifeweeks.app/)** which in turn was inspired by **[Tim Urban's Wait But Why](https://waitbutwhy.com/2014/05/life-weeks.html)**.
10
+
11
+ ## 📝 How to Add Life Events
12
+
13
+ Adding your life events is simple! ✨ You just need to create Markdown files in your `life/` directory with the relevant frontmatter details. Here's a step-by-step guide:
14
+
15
+ 1. 📁 **Create a file:** In your project's root folder, find the `life/` directory (create it if it doesn't exist). Create a new `.md` file inside, such as `my-birthday.md`.
16
+ 2. ✍️ **Add Frontmatter:** Open the file and add the following frontmatter at the top:
17
+
18
+ ```yaml
19
+ ---
20
+ title: "Turned 18!"
21
+ date: "2010-05-15" # for security reasons, we also support YYYY-MM format
22
+ color: "#0d8427ff"
23
+ description: "Finally an adult! Had a huge party with friends."
24
+ tags:
25
+ - "milestone"
26
+ - "birthday"
27
+ ---
28
+ ```
29
+
30
+ ### 📋 Frontmatter Fields Explanation
31
+ - 🏷️ **`title`**: The title of the event (shown on the timeline).
32
+ - 📅 **`date`**: The date of the event in `YYYY-MM-DD` format (or just `YYYY-MM` if you only know the month).
33
+ - 🎨 **`color`** *(optional)*: A custom hex color code for this specific event's block. Defaults to a dark gray (`#333333`) if not provided. The `site.config.ts` handles light/dark mode overrides if applicable!
34
+ - 📜 **`description`** *(optional)*: A more detailed description of the event that will show up when lingering on the specific week.
35
+ - 🔖 **`tags`** *(optional)*: A list of tags categorized for your event.
36
+
37
+ Once you add the markdown file with these details, our build scripts will pick them up and update your Life in Weeks board automatically. 🚀
package/prerender.js CHANGED
@@ -36,9 +36,17 @@ async function prerender() {
36
36
  }
37
37
  const normalizedBasePath = basePath;
38
38
 
39
- // 3. Determine routes to prerender
40
39
  const routesToPrerender = ['/', '/projects/'];
41
40
 
41
+ // Add life page conditionally
42
+ const lifePath = toAbsolute('src/life.json');
43
+ if (fs.existsSync(lifePath)) {
44
+ const lifeEvents = JSON.parse(fs.readFileSync(lifePath, 'utf-8'));
45
+ if (lifeEvents.length > 0) {
46
+ routesToPrerender.push('/life/');
47
+ }
48
+ }
49
+
42
50
  // Add blog post routes and pagination
43
51
  const postsPath = toAbsolute('src/posts.json');
44
52
  if (fs.existsSync(postsPath)) {
@@ -0,0 +1,46 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import matter from 'gray-matter';
4
+ import { fileURLToPath } from 'url';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+
9
+ const lifeDir = path.join(__dirname, '../life');
10
+ const outputFile = path.join(__dirname, '../src/life.json');
11
+
12
+ function generateLifeData() {
13
+ if (!fs.existsSync(lifeDir)) {
14
+ console.log('No life directory found. Creating empty life.json');
15
+ fs.writeFileSync(outputFile, JSON.stringify([], null, 2));
16
+ return;
17
+ }
18
+
19
+ const files = fs.readdirSync(lifeDir).filter(file => file.endsWith('.md'));
20
+ const events = files.map(file => {
21
+ const filePath = path.join(lifeDir, file);
22
+ const content = fs.readFileSync(filePath, 'utf-8');
23
+ const { data } = matter(content);
24
+
25
+ if (!data.date) {
26
+ console.warn(`Warning: File ${file} is missing a 'date' in its frontmatter.`);
27
+ }
28
+
29
+ return {
30
+ title: data.title || '',
31
+ date: data.date ? (data.date.length === 7 ? `${data.date}-01` : data.date) : '',
32
+ isMonthOnly: data.date ? data.date.length === 7 : false,
33
+ color: data.color || '#333333',
34
+ description: data.description || '',
35
+ tags: data.tags || [],
36
+ };
37
+ }).filter(event => event.date);
38
+
39
+ // Sort by date ascending to find the earliest date
40
+ events.sort((a, b) => new Date(a.date) - new Date(b.date));
41
+
42
+ fs.writeFileSync(outputFile, JSON.stringify(events, null, 2));
43
+ console.log(`Generated ${events.length} life events in ${outputFile}`);
44
+ }
45
+
46
+ generateLifeData();
package/src/App.css CHANGED
@@ -490,4 +490,82 @@ code {
490
490
  .prev-link:hover,
491
491
  .next-link:hover {
492
492
  text-decoration: underline;
493
+ }
494
+
495
+ /* Life in Weeks */
496
+ .life-page {
497
+ text-align: center;
498
+ }
499
+
500
+ .life-visualization {
501
+ display: flex;
502
+ flex-direction: column;
503
+ align-items: center;
504
+ gap: 2rem;
505
+ }
506
+
507
+ .life-grid-container {
508
+ width: 100%;
509
+ overflow-x: auto;
510
+ }
511
+
512
+ .life-grid {
513
+ display: flex;
514
+ flex-wrap: wrap;
515
+ gap: 4px;
516
+ }
517
+
518
+ .life-week {
519
+ min-width: 18px;
520
+ height: 24px;
521
+ border: 1px solid var(--color-life-week, var(--color-border));
522
+ background-color: transparent;
523
+ border-radius: 4px;
524
+ position: relative;
525
+ cursor: pointer;
526
+ transition: transform 0.1s, border-color 0.2s;
527
+ }
528
+
529
+
530
+ .life-week.past {
531
+ background-color: var(--color-life-week, var(--color-border));
532
+ }
533
+
534
+ .life-week.event {
535
+ z-index: 1;
536
+ box-shadow: 0 0 4px rgba(0, 0, 0, 0.5);
537
+ flex: 0 0 auto;
538
+ display: flex;
539
+ align-items: center;
540
+ padding: 0 8px;
541
+ border-radius: 6px;
542
+ }
543
+
544
+ .event-label-text {
545
+ font-size: 0.75rem;
546
+ font-weight: 500;
547
+ white-space: nowrap;
548
+ color: #fff;
549
+ }
550
+
551
+
552
+
553
+ .empty-state {
554
+ margin-top: 4rem;
555
+ padding: 3rem;
556
+ background-color: var(--color-card-bg);
557
+ border-radius: 12px;
558
+ border: 1px dashed var(--color-border);
559
+ }
560
+
561
+ .life-section {
562
+ width: 100%;
563
+ margin-bottom: 1rem;
564
+ }
565
+
566
+ .life-section-title {
567
+ text-align: left;
568
+ font-size: 1.2rem;
569
+ color: var(--color-secondary);
570
+ margin: 0.5rem 0px;
493
571
  }
package/src/App.tsx CHANGED
@@ -2,7 +2,9 @@ import { Routes, Route } from 'react-router-dom';
2
2
  import HomePage from './pages/HomePage';
3
3
  import BlogPost from './pages/BlogPost';
4
4
  import Projects from './pages/Projects';
5
+ import Life from './pages/Life';
5
6
  import Footer from './components/Footer';
7
+ import lifeData from './life.json';
6
8
  import './App.css';
7
9
  import { ThemeProvider } from './context/ThemeContext';
8
10
 
@@ -15,6 +17,7 @@ function AppContent() {
15
17
  <Route path="/page/:pageNumber" element={<HomePage />} />
16
18
  <Route path="/projects" element={<Projects />} />
17
19
  <Route path="/post/:slug" element={<BlogPost />} />
20
+ {lifeData.length > 0 && <Route path="/life" element={<Life />} />}
18
21
  </Routes>
19
22
  </div>
20
23
  <Footer />
@@ -1,9 +1,10 @@
1
1
  import { useState } from 'react';
2
2
  import { Link } from 'react-router-dom';
3
- import { Home, FolderGit2, Sun, Moon, Menu, X } from 'lucide-react';
3
+ import { Home, FolderGit2, Sun, Moon, Menu, X, Activity } from 'lucide-react';
4
4
  import { useTheme } from '../context/ThemeContext';
5
5
  import { siteConfig } from '../site.config';
6
6
  import projects from '../projects.json';
7
+ import lifeData from '../life.json';
7
8
 
8
9
  export default function SiteHeader({ showTitle = true }: { showTitle?: boolean }) {
9
10
  const { theme, toggleTheme } = useTheme();
@@ -27,6 +28,12 @@ export default function SiteHeader({ showTitle = true }: { showTitle?: boolean }
27
28
  <span>Projects</span>
28
29
  </Link>
29
30
  )}
31
+ {lifeData.length > 0 && (
32
+ <Link to="/life" onClick={closeMenu}>
33
+ <Activity size={18} />
34
+ <span>Life</span>
35
+ </Link>
36
+ )}
30
37
  </nav>
31
38
 
32
39
  <div className="header-actions">
@@ -34,6 +34,13 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
34
34
  root.style.setProperty('--color-card-bg', themeConfig.cardBackground);
35
35
  root.style.setProperty('--color-link-hover', themeConfig.linkHover);
36
36
  root.style.setProperty('--color-link', themeConfig.linkColor);
37
+
38
+ if (themeConfig.lifeWeek) {
39
+ root.style.setProperty('--color-life-week', themeConfig.lifeWeek);
40
+ } else {
41
+ root.style.removeProperty('--color-life-week');
42
+ }
43
+
37
44
  root.style.setProperty('--font-family', siteConfig.theme.fontFamily);
38
45
  }, [theme]);
39
46
 
package/src/life.json ADDED
@@ -0,0 +1,10 @@
1
+ [
2
+ {
3
+ "title": "Initial Release",
4
+ "date": "2026-02-23",
5
+ "isMonthOnly": false,
6
+ "color": "#b3169bff",
7
+ "description": "Support for blog posts and projects",
8
+ "tags": []
9
+ }
10
+ ]
@@ -0,0 +1,144 @@
1
+ import { useMemo } from 'react';
2
+ import * as ReactHelmetAsync from 'react-helmet-async';
3
+ const helmetAsync = ReactHelmetAsync as any;
4
+ const { Helmet } = helmetAsync.default || helmetAsync;
5
+ import { siteConfig } from '../site.config';
6
+ import lifeData from '../life.json';
7
+ import { differenceInWeeks, parseISO, addWeeks, format } from 'date-fns';
8
+ import SiteHeader from '../components/SiteHeader';
9
+
10
+ type LifeEvent = {
11
+ title?: string;
12
+ date: string;
13
+ color: string;
14
+ description: string;
15
+ tags: string[];
16
+ isMonthOnly?: boolean;
17
+ };
18
+
19
+ const WEEKS_IN_YEAR = 52;
20
+ const YEARS_IN_LIFE = 100;
21
+ const TOTAL_WEEKS = WEEKS_IN_YEAR * YEARS_IN_LIFE;
22
+
23
+ const LIFE_SECTIONS = [
24
+ { label: 'Childhood', startYear: 0, endYear: 13 },
25
+ { label: 'Teen Years', startYear: 13, endYear: 20 },
26
+ { label: '20s', startYear: 20, endYear: 30 },
27
+ { label: '30s', startYear: 30, endYear: 40 },
28
+ { label: '40s', startYear: 40, endYear: 50 },
29
+ { label: '50s', startYear: 50, endYear: 60 },
30
+ { label: '60s', startYear: 60, endYear: 70 },
31
+ { label: '70s', startYear: 70, endYear: 80 },
32
+ { label: '80s', startYear: 80, endYear: 90 },
33
+ { label: '90s', startYear: 90, endYear: 100 },
34
+ ];
35
+
36
+ export default function Life() {
37
+ const events: LifeEvent[] = lifeData;
38
+
39
+ // The earliest date sets week 0
40
+ const birthDateStr = events.length > 0 ? events[0].date : null;
41
+ const birthDate = birthDateStr ? parseISO(birthDateStr) : null;
42
+ const currentDate = new Date();
43
+
44
+ const currentWeekIndex = birthDate ? Math.max(0, differenceInWeeks(currentDate, birthDate)) : 0;
45
+
46
+ const weeks = useMemo(() => {
47
+ const grid = Array.from({ length: TOTAL_WEEKS }, () => ({
48
+ isPast: false,
49
+ events: [] as LifeEvent[],
50
+ date: null as Date | null,
51
+ index: 0
52
+ }));
53
+
54
+ if (!birthDate) return grid;
55
+
56
+ // Populate base week info
57
+ for (let i = 0; i < TOTAL_WEEKS; i++) {
58
+ grid[i].index = i;
59
+ grid[i].isPast = i <= currentWeekIndex;
60
+ grid[i].date = addWeeks(birthDate, i);
61
+ }
62
+
63
+ // Map events to weeks
64
+ events.forEach(event => {
65
+ const eventDate = parseISO(event.date);
66
+ const weekIndex = differenceInWeeks(eventDate, birthDate);
67
+ if (weekIndex >= 0 && weekIndex < TOTAL_WEEKS) {
68
+ grid[weekIndex].events.push(event);
69
+ }
70
+ });
71
+
72
+ return grid;
73
+ }, [events, birthDate, currentWeekIndex]);
74
+
75
+ return (
76
+ <div className="blog-container">
77
+ <Helmet>
78
+ <title>Life in Weeks | {siteConfig.title}</title>
79
+ <meta name="description" content="My life visualized in weeks." />
80
+ </Helmet>
81
+
82
+ <SiteHeader />
83
+ <section className="projects-section">
84
+ <h2>Life in Weeks</h2>
85
+ <div className="life-page">
86
+ {!birthDate ? (
87
+ <div className="empty-state">
88
+ <p>No life events found. Add events to the `life/` directory with a past `date` in the frontmatter to start your timeline.</p>
89
+ </div>
90
+ ) : (
91
+ <div className="life-visualization">
92
+ <div className="life-grid-container">
93
+ <div className="life-sections">
94
+ {LIFE_SECTIONS.map(section => {
95
+ const sectionWeeks = weeks.slice(section.startYear * WEEKS_IN_YEAR, section.endYear * WEEKS_IN_YEAR);
96
+
97
+ return (
98
+ <div key={section.label} className="life-section">
99
+ <h3 className="life-section-title">{section.label}</h3>
100
+ <div className="life-grid">
101
+ {sectionWeeks.map((week, index) => {
102
+ const isEvent = week.events.length > 0;
103
+ const eventColor = isEvent ? week.events[0].color : undefined;
104
+
105
+ let className = "life-week";
106
+ if (week.isPast) className += " past";
107
+ if (isEvent) className += " event";
108
+
109
+ return (
110
+ <div
111
+ key={`${section.label}-${index}`}
112
+ className={className}
113
+ title={isEvent ? week.events.map(e => {
114
+ const titlePart = e.title || '';
115
+ const descPart = e.description ? (titlePart ? `: ${e.description}` : e.description) : '';
116
+ const datePart = `(${e.isMonthOnly ? format(parseISO(e.date), 'MMMM yyyy') : format(parseISO(e.date), 'MMMM dd, yyyy')})`;
117
+ return `${titlePart}${descPart} ${datePart}`.trim();
118
+ }).join('\n') : (week.date ? format(week.date, 'MMMM dd, yyyy') : undefined)}
119
+ style={isEvent ? { backgroundColor: eventColor, borderColor: eventColor } : undefined}
120
+ >
121
+ {isEvent && (
122
+ <span className="event-label-text">
123
+ {week.events[0].title || ''}
124
+ {week.events[0].title && week.events[0].description ? `: ${week.events[0].description}` : (week.events[0].description || '')}
125
+ </span>
126
+ )}
127
+ </div>
128
+ );
129
+ })}
130
+ </div>
131
+ </div>
132
+ );
133
+ })}
134
+ </div>
135
+ </div>
136
+
137
+
138
+ </div>
139
+ )}
140
+ </div>
141
+ </section>
142
+ </div>
143
+ );
144
+ }
package/src/posts.json CHANGED
@@ -1,4 +1,11 @@
1
1
  [
2
+ {
3
+ "slug": "life-in-weeks-feature",
4
+ "title": "Introducing the Life in Weeks Feature ⏳",
5
+ "date": "2026-02-22",
6
+ "summary": "A new feature to visualize your life in weeks",
7
+ "weight": 0
8
+ },
2
9
  {
3
10
  "slug": "hello-world",
4
11
  "title": "Welcome to Vending Mocha ☕",
@@ -3,7 +3,7 @@
3
3
  export const siteConfig = {
4
4
  title: "Vending Mocha",
5
5
  url: "https://vendingmocha.com",
6
- description: '**Vending Mocha** is a lightweight, strictly typed, and blazing fast personal blogging framework built with React.\n\nIt is designed to be minimal, easy to customize, and deployed within minutes. Create a site, update the config, and start writing!\n\nSimply write your new posts in markdown files and Vending Mocha will publish them as static HTML files to your GitHub Pages site.',
6
+ description: '**Vending Mocha** is a lightweight, blazing fast personal blogging framework built with React.\n\nIt is designed to be minimal, easy to customize, and deployed within minutes. Create a site, update the config, and deploy to Github Pages!\n\nSimply write your new posts in markdown files and Vending Mocha will publish them as static HTML files to your site.\n\nTo get started:\n`npx vending-mocha@latest new <my-blog>`',
7
7
  image: '/images/profile.png',
8
8
  contact: {
9
9
  github: "https://github.com/kcthota/vending-mocha",
@@ -20,6 +20,7 @@ export const siteConfig = {
20
20
  cardBackground: "#eee",
21
21
  linkHover: "#000",
22
22
  linkColor: "#222",
23
+ lifeWeek: "#ddd"
23
24
  },
24
25
  dark: {
25
26
  primary: "#fff",
@@ -30,6 +31,7 @@ export const siteConfig = {
30
31
  cardBackground: "#373636ff",
31
32
  linkHover: "#fff",
32
33
  linkColor: "#ccc",
34
+ lifeWeek: "#bbb",
33
35
  },
34
36
  fontFamily: "Avenir, Open Sans, sans-serif",
35
37
  },
package/vite.config.ts CHANGED
@@ -12,16 +12,21 @@ const watchMarkdownPlugin = () => ({
12
12
  // Use absolute paths or relative checks to ensure we only trigger on relevant files
13
13
  const normalizedFile = file.replace(/\\/g, '/')
14
14
  if (normalizedFile.endsWith('.md')) {
15
- if (normalizedFile.includes('/src/posts/')) {
15
+ if (normalizedFile.includes('/posts/')) {
16
16
  console.log('Post changed, regenerating posts data...')
17
17
  exec('node scripts/generate-posts-data.js', (err) => {
18
18
  if (err) console.error('Error regenerating posts:', err)
19
19
  })
20
- } else if (normalizedFile.includes('/src/projects/')) {
20
+ } else if (normalizedFile.includes('/projects/')) {
21
21
  console.log('Project changed, regenerating projects data...')
22
22
  exec('node scripts/generate-projects-data.js', (err) => {
23
23
  if (err) console.error('Error regenerating projects:', err)
24
24
  })
25
+ } else if (normalizedFile.includes('/life/')) {
26
+ console.log('Life events changed, regenerating projects data...')
27
+ exec('node scripts/generate-life-data.js', (err) => {
28
+ if (err) console.error('Error regenerating life events:', err)
29
+ })
25
30
  }
26
31
  }
27
32
  }