@programinglive/dev-workflow-mcp-server 1.5.7 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.husky/pre-commit CHANGED
@@ -1 +1,2 @@
1
- npm test
1
+ #!/usr/bin/env sh
2
+ # Pre-commit hook - tests are run only during release
package/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ## [1.6.0](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.5.11...v1.6.0) (2025-12-06)
6
+
7
+
8
+ ### ✨ Features
9
+
10
+ * add responsive vertical monitor support and dark/light mode toggle ([5565765](https://github.com/programinglive/dev-workflow-mcp-server/commit/5565765f5e5db63d5239c9b82371ae1853a72085))
11
+
12
+ ### [1.5.11](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.5.10...v1.5.11) (2025-12-05)
13
+
14
+ ### [1.5.10](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.5.9...v1.5.10) (2025-12-05)
15
+
16
+ ### [1.5.9](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.5.8...v1.5.9) (2025-12-05)
17
+
18
+ ### [1.5.8](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.5.7...v1.5.8) (2025-12-05)
19
+
20
+
21
+ ### 🐛 Bug Fixes
22
+
23
+ * **release:** amend tag to include release notes, remove build-docs.js and dist/docs ([a3cea9b](https://github.com/programinglive/dev-workflow-mcp-server/commit/a3cea9b612ee2d8f970bf7dd93bcbeef98ba0924))
24
+
5
25
  ### [1.5.7](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.5.6...v1.5.7) (2025-12-05)
6
26
 
7
27
 
@@ -4,6 +4,21 @@ Authoritative history of user-visible changes for the Development Workflow MCP S
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 1.5.11 — 2025-12-05
8
+ - See CHANGELOG for details.
9
+
10
+ ## 1.5.10 — 2025-12-05
11
+ - See CHANGELOG for details.
12
+
13
+ ## 1.5.9 — 2025-12-05
14
+ - See CHANGELOG for details.
15
+
16
+ ## 1.5.8 — 2025-12-05 — 🐛 Bug Fixes
17
+ - **release:** amend tag to include release notes, remove build-docs.js and dist/docs (a3cea9b)
18
+
19
+ ## 1.5.7 — 2025-12-05 — 🐛 Bug Fixes
20
+ - **ci:** checkout main branch for release notes and remove RELEASE_NOTES.html generation (5d50dc0)
21
+
7
22
  ## 1.5.6 — 2025-12-05 — 🐛 Bug Fixes
8
23
  - **ci:** update github release workflow to use github-script (0990808)
9
24
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@programinglive/dev-workflow-mcp-server",
3
- "version": "1.5.7",
3
+ "version": "1.6.0",
4
4
  "description": "MCP server to enforce development workflow discipline",
5
5
  "homepage": "https://devworkflow.programinglive.com/",
6
6
  "mcpName": "io.github.programinglive/dev-workflow-mcp-server",
@@ -14,7 +14,6 @@
14
14
  "build:server": "vite build",
15
15
  "build:web": "npm run build --prefix web",
16
16
  "watch:web": "npm run dev --prefix web",
17
- "postbuild": "node scripts/build-docs.js",
18
17
  "dev": "node --watch index.js",
19
18
  "start": "node index.js",
20
19
  "local": "node index.js",
@@ -54,7 +53,7 @@
54
53
  "devDependencies": {
55
54
  "@commitlint/cli": "^20.2.0",
56
55
  "@commitlint/config-conventional": "^20.2.0",
57
- "@programinglive/commiter": "^1.1.8",
56
+ "@programinglive/commiter": "^1.2.8",
58
57
  "autoprefixer": "^10.4.22",
59
58
  "husky": "^9.1.7",
60
59
  "postcss": "^8.5.6",
@@ -190,7 +190,6 @@ function runRelease({
190
190
 
191
191
  return releaseResult;
192
192
  }
193
-
194
193
  if (require.main === module) {
195
194
  try {
196
195
  const result = runRelease();
@@ -67,29 +67,33 @@ function updateReleaseNotes({
67
67
 
68
68
  function insertReleaseNotesEntry({ content, version, releaseDate, highlight, sectionHeading, detailBullets }) {
69
69
  const lines = content.split('\n');
70
-
71
- // Find the "## Unreleased" section
72
- const unreleasedIndex = lines.findIndex((line) => line.trim().startsWith('## Unreleased'));
73
- if (unreleasedIndex === -1) {
74
- throw new Error('Unable to locate "## Unreleased" section in release notes.');
70
+ const headerSeparatorIndex = lines.findIndex((line) => line.trim().startsWith('|---------'));
71
+ if (headerSeparatorIndex === -1) {
72
+ throw new Error('Unable to locate release notes table header.');
75
73
  }
76
74
 
75
+ const newRow = `| ${version} | ${releaseDate} | ${escapePipes(highlight)} |`;
76
+ lines.splice(headerSeparatorIndex + 1, 0, newRow);
77
+
77
78
  const sectionLines = [];
78
79
  sectionLines.push('');
79
80
  const trimmedHeading = typeof sectionHeading === 'string' ? sectionHeading.trim() : '';
80
- const headingSuffix = trimmedHeading ? ` — ${trimmedHeading}` : '';
81
-
82
- // Format: ## [Version] — [Date]
83
- sectionLines.push(`## ${version} — ${releaseDate}${headingSuffix}`);
84
-
81
+ const headingSuffix = trimmedHeading ? ` – ${trimmedHeading}` : '';
82
+ sectionLines.push(`## ${version}${headingSuffix}`);
83
+ sectionLines.push('');
84
+ sectionLines.push(`Released on **${releaseDate}**.`);
85
+ sectionLines.push('');
85
86
  for (const bullet of detailBullets) {
86
87
  sectionLines.push(`- ${bullet}`);
87
88
  }
89
+ sectionLines.push('');
88
90
 
89
- // Insert after the Unreleased header (and potentially some blank lines)
90
- // We want to keep "## Unreleased" empty and add the new version below it.
91
- // So we insert after the Unreleased line.
92
- lines.splice(unreleasedIndex + 1, 0, ...sectionLines);
91
+ const firstSectionIndex = lines.findIndex((line, idx) => idx > headerSeparatorIndex && line.startsWith('## '));
92
+ if (firstSectionIndex === -1) {
93
+ lines.push(...sectionLines);
94
+ } else {
95
+ lines.splice(firstSectionIndex, 0, ...sectionLines);
96
+ }
93
97
 
94
98
  return lines.join('\n');
95
99
  }
@@ -8,7 +8,7 @@ import path from 'path';
8
8
  const exec = promisify(execCallback);
9
9
 
10
10
  test('build generates dist/index.mjs file', async () => {
11
- const { stderr } = await exec('npm run build:server && npm run postbuild');
11
+ const { stderr } = await exec('npm run build:server');
12
12
  // Allow the known vite warning about node:process
13
13
  const knownWarning = '[plugin vite:resolve] Module "node:process" has been externalized for browser compatibility';
14
14
  const tailwindOutput = '≈ tailwindcss';
@@ -27,15 +27,6 @@ test('build generates dist/index.mjs file', async () => {
27
27
  const content = await readFile(distPath, 'utf-8');
28
28
  assert(content.includes('main'), 'Bundle should contain main function');
29
29
  assert(content.length > 1000, 'Bundle should be substantial');
30
-
31
- const docDir = path.join(process.cwd(), 'dist', 'docs');
32
- const docFiles = ['README.html', 'web-dashboard.html'];
33
- for (const file of docFiles) {
34
- const fullPath = path.join(docDir, file);
35
- await access(fullPath);
36
- const docContent = await readFile(fullPath, 'utf-8');
37
- assert(docContent.includes('<h1'), `${file} should contain HTML output`);
38
- }
39
30
  });
40
31
 
41
32
  test('build script exists in package.json', async () => {
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Build Verification Script
5
+ * Verifies that the website builds successfully and key features are present
6
+ */
7
+
8
+ import { execSync } from 'child_process';
9
+ import fs from 'fs';
10
+ import path from 'path';
11
+ import { fileURLToPath } from 'url';
12
+
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = path.dirname(__filename);
15
+
16
+ const webDir = path.join(__dirname, 'web');
17
+ const outDir = path.join(webDir, 'out');
18
+
19
+ let passedTests = 0;
20
+ let failedTests = 0;
21
+
22
+ function test(name, fn) {
23
+ try {
24
+ fn();
25
+ console.log(`✓ ${name}`);
26
+ passedTests++;
27
+ } catch (error) {
28
+ console.error(`✗ ${name}`);
29
+ console.error(` Error: ${error.message}`);
30
+ failedTests++;
31
+ }
32
+ }
33
+
34
+ console.log('\n🧪 Running Build Verification Tests...\n');
35
+
36
+ // Test 1: Build succeeds
37
+ test('Build completes successfully', () => {
38
+ try {
39
+ execSync('npm run build', { cwd: webDir, stdio: 'pipe' });
40
+ } catch (error) {
41
+ throw new Error('Build failed');
42
+ }
43
+ });
44
+
45
+ // Test 2: Output directory exists
46
+ test('Output directory exists', () => {
47
+ if (!fs.existsSync(outDir)) {
48
+ throw new Error('Output directory not found');
49
+ }
50
+ });
51
+
52
+ // Test 3: Index HTML exists
53
+ test('Homepage HTML generated', () => {
54
+ const indexPath = path.join(outDir, 'index.html');
55
+ if (!fs.existsSync(indexPath)) {
56
+ throw new Error('index.html not found');
57
+ }
58
+ });
59
+
60
+ // Test 4: Check for key page content
61
+ test('Homepage contains "Dev Workflow"', () => {
62
+ const indexPath = path.join(outDir, 'index.html');
63
+ const content = fs.readFileSync(indexPath, 'utf-8');
64
+ if (!content.includes('Dev Workflow')) {
65
+ throw new Error('Dev Workflow text not found');
66
+ }
67
+ });
68
+
69
+ // Test 5: Check for hero section
70
+ test('Homepage contains hero section', () => {
71
+ const indexPath = path.join(outDir, 'index.html');
72
+ const content = fs.readFileSync(indexPath, 'utf-8');
73
+ if (!content.includes('Master Your') || !content.includes('Workflow')) {
74
+ throw new Error('Hero section not found');
75
+ }
76
+ });
77
+
78
+ // Test 6: Check for dark mode support
79
+ test('Dark mode classes included', () => {
80
+ const indexPath = path.join(outDir, 'index.html');
81
+ const content = fs.readFileSync(indexPath, 'utf-8');
82
+ if (!content.includes('dark:')) {
83
+ throw new Error('Dark mode classes not found');
84
+ }
85
+ });
86
+
87
+ // Test 7: Check for responsive design
88
+ test('Responsive design classes included', () => {
89
+ const indexPath = path.join(outDir, 'index.html');
90
+ const content = fs.readFileSync(indexPath, 'utf-8');
91
+ if (!content.includes('sm:') || !content.includes('md:') || !content.includes('lg:')) {
92
+ throw new Error('Responsive classes not found');
93
+ }
94
+ });
95
+
96
+ // Test 8: Check for features section
97
+ test('Features section present', () => {
98
+ const indexPath = path.join(outDir, 'index.html');
99
+ const content = fs.readFileSync(indexPath, 'utf-8');
100
+ if (!content.includes('Your Coding Conscience')) {
101
+ throw new Error('Features section not found');
102
+ }
103
+ });
104
+
105
+ // Test 9: Check for history section
106
+ test('History section present', () => {
107
+ const indexPath = path.join(outDir, 'index.html');
108
+ const content = fs.readFileSync(indexPath, 'utf-8');
109
+ if (!content.includes('Workflow History')) {
110
+ throw new Error('History section not found');
111
+ }
112
+ });
113
+
114
+ // Test 10: Check for theme toggle
115
+ test('Theme toggle button present', () => {
116
+ const indexPath = path.join(outDir, 'index.html');
117
+ const content = fs.readFileSync(indexPath, 'utf-8');
118
+ // Check for theme toggle in minified form or readable form
119
+ if (!content.includes('toggleTheme') && !content.includes('theme') && !content.includes('Moon') && !content.includes('Sun')) {
120
+ throw new Error('Theme toggle not found');
121
+ }
122
+ });
123
+
124
+ // Test 11: Check for viewport meta tag
125
+ test('Viewport meta tag for mobile', () => {
126
+ const indexPath = path.join(outDir, 'index.html');
127
+ const content = fs.readFileSync(indexPath, 'utf-8');
128
+ if (!content.includes('viewport') || !content.includes('width=device-width')) {
129
+ throw new Error('Viewport meta tag not found');
130
+ }
131
+ });
132
+
133
+ // Test 12: Check for localStorage theme persistence
134
+ test('Theme persistence in Navbar component', () => {
135
+ const navbarPath = path.join(webDir, 'components', 'Navbar.tsx');
136
+ const content = fs.readFileSync(navbarPath, 'utf-8');
137
+ if (!content.includes('localStorage')) {
138
+ throw new Error('localStorage not found in Navbar');
139
+ }
140
+ });
141
+
142
+ // Test 13: Check for system preference detection
143
+ test('System color scheme preference detection', () => {
144
+ const navbarPath = path.join(webDir, 'components', 'Navbar.tsx');
145
+ const content = fs.readFileSync(navbarPath, 'utf-8');
146
+ if (!content.includes('prefers-color-scheme')) {
147
+ throw new Error('prefers-color-scheme not found');
148
+ }
149
+ });
150
+
151
+ // Test 14: Check for responsive Navbar
152
+ test('Responsive Navbar implementation', () => {
153
+ const navbarPath = path.join(webDir, 'components', 'Navbar.tsx');
154
+ const content = fs.readFileSync(navbarPath, 'utf-8');
155
+ if (!content.includes('md:hidden') && !content.includes('hidden md:')) {
156
+ throw new Error('Responsive Navbar not found');
157
+ }
158
+ });
159
+
160
+ // Test 15: Check for vertical monitor optimization
161
+ test('Vertical monitor responsive design', () => {
162
+ const heroPath = path.join(webDir, 'components', 'Hero.tsx');
163
+ const content = fs.readFileSync(heroPath, 'utf-8');
164
+ if (!content.includes('xl:grid-cols-2')) {
165
+ throw new Error('Vertical monitor optimization not found');
166
+ }
167
+ });
168
+
169
+ console.log(`\n📊 Test Results: ${passedTests} passed, ${failedTests} failed\n`);
170
+
171
+ if (failedTests > 0) {
172
+ process.exit(1);
173
+ } else {
174
+ console.log('✅ All tests passed!\n');
175
+ process.exit(0);
176
+ }
package/web/README.md CHANGED
@@ -1,36 +1,133 @@
1
- This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
1
+ # Dev Workflow Website
2
+
3
+ A modern, responsive website for the Dev Workflow MCP Server built with Next.js, React, and Tailwind CSS.
4
+
5
+ ## Features
6
+
7
+ - ✅ **Responsive Design** - Optimized for all screen sizes including vertical/portrait monitors
8
+ - 🌓 **Dark/Light Mode Toggle** - User preference persisted to localStorage with system preference detection
9
+ - 📱 **Mobile First** - Fully responsive with adaptive layouts for sm, md, lg, and xl breakpoints
10
+ - ⚡ **Static Export** - Pre-rendered static HTML for fast Netlify deployment
11
+ - 🎨 **Modern UI** - Built with Tailwind CSS and Lucide React icons
12
+ - ♿ **Accessible** - Semantic HTML and proper ARIA labels
2
13
 
3
14
  ## Getting Started
4
15
 
5
- First, run the development server:
16
+ ### Development
6
17
 
7
18
  ```bash
8
19
  npm run dev
9
- # or
10
- yarn dev
11
- # or
12
- pnpm dev
13
- # or
14
- bun dev
15
20
  ```
16
21
 
17
- Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
22
+ Open [http://localhost:3000](http://localhost:3000) to see the site.
23
+
24
+ ### Build
25
+
26
+ ```bash
27
+ npm run build
28
+ ```
29
+
30
+ Generates static HTML in the `out/` directory.
31
+
32
+ ## Features Overview
33
+
34
+ ### Responsive Vertical Monitor Support
35
+ - Grid layout switches from 2-column to single-column on smaller screens
36
+ - Terminal visualization hidden on screens smaller than xl breakpoint
37
+ - Responsive padding and text sizes scale appropriately
38
+
39
+ ### Dark/Light Mode
40
+ - Toggle button in navbar (Sun/Moon icons)
41
+ - Theme preference stored in localStorage
42
+ - Respects system color scheme preference on first visit
43
+ - Smooth transitions between modes
44
+
45
+ ## Deployment
46
+
47
+ ### Netlify
18
48
 
19
- You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
49
+ 1. **Prerequisites**: Install Netlify CLI
50
+ ```bash
51
+ npm install -g netlify-cli
52
+ ```
20
53
 
21
- This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
54
+ 2. **Build the site**
55
+ ```bash
56
+ npm run build
57
+ ```
22
58
 
23
- ## Learn More
59
+ 3. **Deploy**
60
+ ```bash
61
+ netlify deploy --prod --dir=out
62
+ ```
63
+
64
+ 4. **Or connect GitHub for automatic deployments**
65
+ - Push to GitHub
66
+ - Connect repo to Netlify
67
+ - Netlify automatically builds and deploys on push
68
+
69
+ ### Environment Setup
70
+
71
+ Create a `.env.local` file if needed for environment variables (currently not required).
72
+
73
+ ## Testing
74
+
75
+ Run the build verification tests:
76
+
77
+ ```bash
78
+ node verify-build.js
79
+ ```
80
+
81
+ Tests verify:
82
+ - Build completes successfully
83
+ - All pages generate correctly
84
+ - Dark mode classes are included
85
+ - Responsive design classes present
86
+ - Theme toggle functionality
87
+ - Viewport meta tags for mobile
88
+ - Vertical monitor optimization
89
+
90
+ ## Project Structure
91
+
92
+ ```
93
+ web/
94
+ ├── app/
95
+ │ ├── layout.tsx # Root layout with ThemeProvider
96
+ │ ├── page.tsx # Homepage
97
+ │ ├── globals.css # Global styles
98
+ │ └── docs/ # Documentation pages
99
+ ├── components/
100
+ │ ├── Navbar.tsx # Navigation with theme toggle
101
+ │ ├── Hero.tsx # Hero section
102
+ │ ├── Features.tsx # Features grid
103
+ │ ├── History.tsx # Workflow history section
104
+ │ ├── Terminal.tsx # Animated terminal demo
105
+ │ ├── Footer.tsx # Footer
106
+ │ └── ThemeProvider.tsx # Theme context provider
107
+ ├── public/ # Static assets
108
+ ├── tailwind.config.ts # Tailwind configuration
109
+ ├── next.config.ts # Next.js configuration
110
+ └── package.json
111
+ ```
24
112
 
25
- To learn more about Next.js, take a look at the following resources:
113
+ ## Technologies
26
114
 
27
- - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28
- - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
115
+ - **Framework**: Next.js 16 with Turbopack
116
+ - **Styling**: Tailwind CSS 3
117
+ - **Icons**: Lucide React
118
+ - **Language**: TypeScript
119
+ - **Deployment**: Netlify (static export)
29
120
 
30
- You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
121
+ ## Browser Support
31
122
 
32
- ## Deploy on Vercel
123
+ - Chrome/Edge (latest)
124
+ - Firefox (latest)
125
+ - Safari (latest)
126
+ - Mobile browsers (iOS Safari, Chrome Mobile)
33
127
 
34
- The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
128
+ ## Performance
35
129
 
36
- Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
130
+ - Static HTML export for instant page loads
131
+ - Optimized images with Next.js Image component
132
+ - Minimal JavaScript bundle
133
+ - CSS-in-JS with Tailwind for efficient styling
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
2
2
  import "./globals.css";
3
3
  import Navbar from "@/components/Navbar";
4
4
  import Footer from "@/components/Footer";
5
+ import { ThemeProvider } from "@/components/ThemeProvider";
5
6
 
6
7
  export const metadata: Metadata = {
7
8
  title: "Dev Workflow Dashboard",
@@ -26,10 +27,12 @@ export default function RootLayout({
26
27
  }>) {
27
28
  return (
28
29
  <html lang="en" className="scroll-smooth">
29
- <body className="antialiased bg-[#0a0a0f] text-white">
30
- <Navbar />
31
- {children}
32
- <Footer />
30
+ <body className="antialiased bg-white dark:bg-[#0a0a0f] text-gray-900 dark:text-white transition-colors duration-300">
31
+ <ThemeProvider>
32
+ <Navbar />
33
+ {children}
34
+ <Footer />
35
+ </ThemeProvider>
33
36
  </body>
34
37
  </html>
35
38
  );
@@ -2,10 +2,10 @@ import { ShieldCheck, TestTube, FileText, GitBranch, History, Zap } from "lucide
2
2
 
3
3
  export default function Features() {
4
4
  return (
5
- <section id="features" className="py-20 px-4">
5
+ <section id="features" className="py-12 sm:py-16 lg:py-20 px-3 sm:px-4 bg-gray-50 dark:bg-transparent transition-colors duration-300">
6
6
  <div className="max-w-7xl mx-auto">
7
- <h2 className="text-3xl md:text-4xl font-bold text-center mb-16">Your Coding Conscience</h2>
8
- <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
7
+ <h2 className="text-2xl sm:text-3xl md:text-4xl font-bold text-center mb-8 sm:mb-12 lg:mb-16 text-gray-900 dark:text-white">Your Coding Conscience</h2>
8
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
9
9
  <FeatureCard
10
10
  icon={<ShieldCheck className="w-6 h-6 text-indigo-400" />}
11
11
  title="Enforced Discipline"
@@ -50,12 +50,12 @@ export default function Features() {
50
50
 
51
51
  function FeatureCard({ icon, title, description, bg }: { icon: React.ReactNode, title: string, description: string, bg: string }) {
52
52
  return (
53
- <div className="p-6 rounded-xl bg-white/5 border border-white/10 hover:bg-white/10 transition">
53
+ <div className="p-6 rounded-xl bg-white dark:bg-white/5 border border-gray-200 dark:border-white/10 hover:bg-gray-100 dark:hover:bg-white/10 transition">
54
54
  <div className={`w-12 h-12 rounded-lg ${bg} flex items-center justify-center mb-4`}>
55
55
  {icon}
56
56
  </div>
57
- <h3 className="text-xl font-semibold mb-2">{title}</h3>
58
- <p className="text-gray-400">{description}</p>
57
+ <h3 className="text-xl font-semibold mb-2 text-gray-900 dark:text-white">{title}</h3>
58
+ <p className="text-gray-600 dark:text-gray-400">{description}</p>
59
59
  </div>
60
60
  );
61
61
  }
@@ -1,7 +1,7 @@
1
1
  export default function Footer() {
2
2
  return (
3
- <footer className="py-12 px-4 border-t border-white/10">
4
- <div className="max-w-7xl mx-auto text-center text-gray-400">
3
+ <footer className="py-12 px-4 border-t border-gray-200 dark:border-white/10 bg-white dark:bg-transparent transition-colors duration-300">
4
+ <div className="max-w-7xl mx-auto text-center text-gray-600 dark:text-gray-400">
5
5
  <p>Built with ❤️ for developers who care about quality</p>
6
6
  </div>
7
7
  </footer>
@@ -27,9 +27,9 @@ export default function Hero() {
27
27
  };
28
28
 
29
29
  return (
30
- <section className="min-h-screen flex items-center justify-center px-4 pt-16 relative overflow-hidden">
31
- <div className="absolute inset-0 bg-gradient-to-br from-indigo-500/10 via-purple-500/10 to-pink-500/10"></div>
32
- <div className="max-w-7xl mx-auto grid lg:grid-cols-2 gap-12 items-center relative z-10">
30
+ <section className="min-h-screen flex items-center justify-center px-3 sm:px-4 pt-16 relative overflow-hidden bg-white dark:bg-transparent transition-colors duration-300">
31
+ <div className="absolute inset-0 bg-gradient-to-br from-indigo-500/5 dark:from-indigo-500/10 via-purple-500/5 dark:via-purple-500/10 to-pink-500/5 dark:to-pink-500/10"></div>
32
+ <div className="max-w-7xl mx-auto grid xl:grid-cols-2 gap-6 sm:gap-8 lg:gap-12 items-center relative z-10">
33
33
  {/* Left Column - Content */}
34
34
  <div>
35
35
  <div className="animate-fade-in-up" style={{ animationDelay: "0.1s" }}>
@@ -39,47 +39,47 @@ export default function Hero() {
39
39
  </div>
40
40
  </div>
41
41
 
42
- <h1 className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold mb-6 leading-tight animate-fade-in-up" style={{ animationDelay: "0.2s" }}>
42
+ <h1 className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl xl:text-7xl font-bold mb-4 sm:mb-6 leading-tight animate-fade-in-up" style={{ animationDelay: "0.2s" }}>
43
43
  Master Your<br />
44
44
  <span className="bg-gradient-to-r from-indigo-400 via-purple-400 to-pink-400 bg-clip-text text-transparent">Workflow</span>
45
45
  </h1>
46
- <p className="text-lg md:text-xl text-gray-400 mb-8 max-w-xl animate-fade-in-up" style={{ animationDelay: "0.3s" }}>
46
+ <p className="text-base sm:text-lg md:text-xl text-gray-600 dark:text-gray-400 mb-6 sm:mb-8 max-w-xl animate-fade-in-up" style={{ animationDelay: "0.3s" }}>
47
47
  An MCP server that acts as your coding conscience. Enforce test-driven development, ensure documentation, and never skip a step in your release process.
48
48
  </p>
49
49
  <div className="flex flex-col sm:flex-row gap-4 mb-8 animate-fade-in-up" style={{ animationDelay: "0.4s" }}>
50
- <Link href="#history" className="px-6 py-3 bg-gradient-to-r from-indigo-600 to-purple-600 rounded-lg font-semibold hover:shadow-lg hover:shadow-indigo-500/50 transition inline-flex items-center justify-center gap-2">
50
+ <Link href="#history" className="px-6 py-3 bg-gradient-to-r from-indigo-600 to-purple-600 rounded-lg font-semibold hover:shadow-lg hover:shadow-indigo-500/50 transition inline-flex items-center justify-center gap-2 text-white">
51
51
  View History <ArrowRight className="w-4 h-4" />
52
52
  </Link>
53
- <Link href="/docs" className="px-6 py-3 bg-white/5 border border-white/10 rounded-lg font-semibold hover:bg-white/10 transition text-center">
53
+ <Link href="/docs" className="px-6 py-3 bg-gray-200 dark:bg-white/5 border border-gray-300 dark:border-white/10 rounded-lg font-semibold hover:bg-gray-300 dark:hover:bg-white/10 transition text-center text-gray-900 dark:text-white">
54
54
  Documentation
55
55
  </Link>
56
56
  </div>
57
- <div className="flex flex-wrap gap-6 sm:gap-8 text-sm animate-fade-in-up" style={{ animationDelay: "0.5s" }}>
57
+ <div className="flex flex-wrap gap-4 sm:gap-6 lg:gap-8 text-xs sm:text-sm animate-fade-in-up" style={{ animationDelay: "0.5s" }}>
58
58
  <div>
59
- <div className="text-2xl font-bold">{version ?? "..."}</div>
60
- <div className="text-gray-500">Latest Version</div>
59
+ <div className="text-xl sm:text-2xl font-bold text-gray-900 dark:text-white">{version ?? "..."}</div>
60
+ <div className="text-gray-600 dark:text-gray-500">Latest Version</div>
61
61
  </div>
62
62
  <div>
63
- <div className="text-2xl font-bold">MIT</div>
64
- <div className="text-gray-500">License</div>
63
+ <div className="text-xl sm:text-2xl font-bold text-gray-900 dark:text-white">MIT</div>
64
+ <div className="text-gray-600 dark:text-gray-500">License</div>
65
65
  </div>
66
66
  <div>
67
- <div className="text-2xl font-bold">Node 18+</div>
68
- <div className="text-gray-500">Compatibility</div>
67
+ <div className="text-xl sm:text-2xl font-bold text-gray-900 dark:text-white">Node 18+</div>
68
+ <div className="text-gray-600 dark:text-gray-500">Compatibility</div>
69
69
  </div>
70
70
  {downloads !== null && (
71
71
  <div>
72
- <div className="text-2xl font-bold">{formatDownloads(downloads)}</div>
73
- <div className="text-gray-500">Downloads/mo</div>
72
+ <div className="text-xl sm:text-2xl font-bold text-gray-900 dark:text-white">{formatDownloads(downloads)}</div>
73
+ <div className="text-gray-600 dark:text-gray-500">Downloads/mo</div>
74
74
  </div>
75
75
  )}
76
76
  </div>
77
77
  </div>
78
78
  {/* Right Column - Terminal */}
79
- <div className="hidden lg:block">
79
+ <div className="hidden xl:block">
80
80
  <div className="relative">
81
81
  <div className="absolute inset-0 bg-gradient-to-r from-indigo-500/20 to-purple-500/20 blur-3xl"></div>
82
- <div className="relative bg-[#1a1a24] rounded-xl border border-white/10 p-6 shadow-2xl">
82
+ <div className="relative bg-[#1a1a24] rounded-xl border border-white/10 p-4 sm:p-6 shadow-2xl">
83
83
  <div className="flex items-center gap-2 mb-4">
84
84
  <div className="w-3 h-3 rounded-full bg-red-500"></div>
85
85
  <div className="w-3 h-3 rounded-full bg-yellow-500"></div>
@@ -25,30 +25,15 @@ export default function History() {
25
25
  });
26
26
 
27
27
  const loadData = async () => {
28
- const params = new URLSearchParams({
29
- page: page.toString(),
30
- pageSize: "20",
31
- ...(filters.userId && { user: filters.userId }),
32
- ...(filters.startDate && { startDate: filters.startDate }),
33
- ...(filters.endDate && { endDate: filters.endDate }),
28
+ // Placeholder for future API integration
29
+ // For now, show empty state
30
+ setHistory([]);
31
+ setTotalPages(1);
32
+ setSummary({
33
+ summary: {
34
+ totalTasks: 0,
35
+ },
34
36
  });
35
-
36
- try {
37
- const res = await fetch(`/api/history?${params}`);
38
- const data = await res.json();
39
- setHistory(data.data || []);
40
- setTotalPages(data.totalPages || 1);
41
- } catch (err) {
42
- console.error("Failed to load history", err);
43
- }
44
-
45
- try {
46
- const res = await fetch(`/api/summary?${params}`);
47
- const data = await res.json();
48
- setSummary(data);
49
- } catch (err) {
50
- console.error("Failed to load summary", err);
51
- }
52
37
  };
53
38
 
54
39
  useEffect(() => {
@@ -61,34 +46,34 @@ export default function History() {
61
46
  };
62
47
 
63
48
  return (
64
- <section id="history" className="py-20 px-4 bg-white/5">
49
+ <section id="history" className="py-12 sm:py-16 lg:py-20 px-3 sm:px-4 bg-gray-50 dark:bg-white/5 transition-colors duration-300">
65
50
  <div className="max-w-7xl mx-auto">
66
- <h2 className="text-3xl md:text-4xl font-bold mb-8">Workflow History</h2>
67
- <p className="text-gray-400 mb-12">Track your development progress and analyze your workflow patterns.</p>
68
- <div className="bg-[#0a0a0f] rounded-xl border border-white/10 p-6">
69
- <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4 mb-6">
51
+ <h2 className="text-2xl sm:text-3xl md:text-4xl font-bold mb-4 sm:mb-8 text-gray-900 dark:text-white">Workflow History</h2>
52
+ <p className="text-sm sm:text-base text-gray-600 dark:text-gray-400 mb-8 sm:mb-12">Track your development progress and analyze your workflow patterns.</p>
53
+ <div className="bg-white dark:bg-[#0a0a0f] rounded-xl border border-gray-200 dark:border-white/10 p-4 sm:p-6 transition-colors duration-300">
54
+ <div className="grid gap-3 sm:gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 mb-6">
70
55
  <input
71
56
  type="text"
72
57
  placeholder="User ID"
73
58
  value={filters.userId}
74
59
  onChange={(e) => setFilters({ ...filters, userId: e.target.value })}
75
- className="px-4 py-2 bg-white/5 border border-white/10 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 text-white"
60
+ className="px-4 py-2 bg-gray-100 dark:bg-white/5 border border-gray-300 dark:border-white/10 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 text-gray-900 dark:text-white placeholder-gray-500 dark:placeholder-gray-400"
76
61
  />
77
62
  <input
78
63
  type="date"
79
64
  value={filters.startDate}
80
65
  onChange={(e) => setFilters({ ...filters, startDate: e.target.value })}
81
- className="px-4 py-2 bg-white/5 border border-white/10 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 [color-scheme:dark]"
66
+ className="px-4 py-2 bg-gray-100 dark:bg-white/5 border border-gray-300 dark:border-white/10 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 text-gray-900 dark:text-white [color-scheme:light] dark:[color-scheme:dark]"
82
67
  />
83
68
  <input
84
69
  type="date"
85
70
  value={filters.endDate}
86
71
  onChange={(e) => setFilters({ ...filters, endDate: e.target.value })}
87
- className="px-4 py-2 bg-white/5 border border-white/10 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 [color-scheme:dark]"
72
+ className="px-4 py-2 bg-gray-100 dark:bg-white/5 border border-gray-300 dark:border-white/10 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 text-gray-900 dark:text-white [color-scheme:light] dark:[color-scheme:dark]"
88
73
  />
89
74
  <button
90
75
  onClick={handleApplyFilters}
91
- className="px-4 py-2 bg-gradient-to-r from-indigo-600 to-purple-600 rounded-lg font-semibold hover:shadow-lg hover:shadow-indigo-500/50 transition"
76
+ className="px-4 py-2 bg-gradient-to-r from-indigo-600 to-purple-600 rounded-lg font-semibold hover:shadow-lg hover:shadow-indigo-500/50 transition text-white"
92
77
  >
93
78
  Apply Filters
94
79
  </button>
@@ -96,25 +81,25 @@ export default function History() {
96
81
 
97
82
  {summary && (
98
83
  <div className="mb-6">
99
- <h3 className="text-lg font-semibold mb-4">Project Summary</h3>
100
- <div className="text-gray-400">
84
+ <h3 className="text-lg font-semibold mb-4 text-gray-900 dark:text-white">Project Summary</h3>
85
+ <div className="text-gray-600 dark:text-gray-400">
101
86
  <p>Total tasks: {summary.summary?.totalTasks || 0}</p>
102
87
  </div>
103
88
  </div>
104
89
  )}
105
90
 
106
91
  <div>
107
- <h3 className="text-lg font-semibold mb-4">Recent History</h3>
92
+ <h3 className="text-lg font-semibold mb-4 text-gray-900 dark:text-white">Recent History</h3>
108
93
  <div className="space-y-3">
109
94
  {history.length > 0 ? (
110
95
  history.map((row, i) => (
111
- <div key={i} className="p-4 rounded-lg bg-white/5 border border-white/10">
112
- <div className="font-semibold">{row.task_description}</div>
113
- <div className="text-sm text-gray-400 mt-1">{new Date(row.created_at).toLocaleString()}</div>
96
+ <div key={i} className="p-4 rounded-lg bg-gray-100 dark:bg-white/5 border border-gray-200 dark:border-white/10">
97
+ <div className="font-semibold text-gray-900 dark:text-white">{row.task_description}</div>
98
+ <div className="text-sm text-gray-600 dark:text-gray-400 mt-1">{new Date(row.created_at).toLocaleString()}</div>
114
99
  </div>
115
100
  ))
116
101
  ) : (
117
- <p className="text-gray-500">No history yet.</p>
102
+ <p className="text-gray-500 dark:text-gray-400">No history yet.</p>
118
103
  )}
119
104
  </div>
120
105
 
@@ -122,15 +107,15 @@ export default function History() {
122
107
  <button
123
108
  onClick={() => setPage(p => Math.max(1, p - 1))}
124
109
  disabled={page <= 1}
125
- className="px-4 py-2 bg-white/5 border border-white/10 rounded-lg hover:bg-white/10 disabled:opacity-50"
110
+ className="px-4 py-2 bg-gray-100 dark:bg-white/5 border border-gray-300 dark:border-white/10 rounded-lg hover:bg-gray-200 dark:hover:bg-white/10 disabled:opacity-50 text-gray-900 dark:text-white transition"
126
111
  >
127
112
  Previous
128
113
  </button>
129
- <span className="text-sm text-gray-400">Page {page} of {totalPages}</span>
114
+ <span className="text-sm text-gray-600 dark:text-gray-400">Page {page} of {totalPages}</span>
130
115
  <button
131
116
  onClick={() => setPage(p => Math.min(totalPages, p + 1))}
132
117
  disabled={page >= totalPages}
133
- className="px-4 py-2 bg-white/5 border border-white/10 rounded-lg hover:bg-white/10 disabled:opacity-50"
118
+ className="px-4 py-2 bg-gray-100 dark:bg-white/5 border border-gray-300 dark:border-white/10 rounded-lg hover:bg-gray-200 dark:hover:bg-white/10 disabled:opacity-50 text-gray-900 dark:text-white transition"
134
119
  >
135
120
  Next
136
121
  </button>
@@ -1,57 +1,100 @@
1
1
  "use client";
2
2
 
3
- import { useState } from "react";
3
+ import { useState, useEffect } from "react";
4
4
  import Link from "next/link";
5
- import { Github, Menu, X } from "lucide-react";
5
+ import { Github, Menu, X, Moon, Sun } from "lucide-react";
6
6
  import Image from "next/image";
7
7
 
8
8
  export default function Navbar() {
9
9
  const [isOpen, setIsOpen] = useState(false);
10
+ const [theme, setTheme] = useState<"light" | "dark">("dark");
11
+
12
+ useEffect(() => {
13
+ // Get theme from localStorage or system preference
14
+ const savedTheme = localStorage.getItem("theme") as "light" | "dark" | null;
15
+ if (savedTheme) {
16
+ setTheme(savedTheme);
17
+ } else {
18
+ const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
19
+ setTheme(prefersDark ? "dark" : "light");
20
+ }
21
+ }, []);
22
+
23
+ const toggleTheme = () => {
24
+ const newTheme = theme === "dark" ? "light" : "dark";
25
+ setTheme(newTheme);
26
+ localStorage.setItem("theme", newTheme);
27
+ document.documentElement.classList.toggle("dark", newTheme === "dark");
28
+ };
10
29
 
11
30
  return (
12
- <nav className="fixed top-0 left-0 right-0 z-50 bg-[#0a0a0f]/80 backdrop-blur-lg border-b border-white/10">
13
- <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
14
- <div className="flex items-center justify-between h-16">
31
+ <nav className="fixed top-0 left-0 right-0 z-50 bg-white/80 dark:bg-[#0a0a0f]/80 backdrop-blur-lg border-b border-gray-200 dark:border-white/10 transition-colors duration-300">
32
+ <div className="max-w-7xl mx-auto px-3 sm:px-4 lg:px-8">
33
+ <div className="flex items-center justify-between h-14 sm:h-16">
15
34
  <Link href="/" className="flex items-center gap-2">
16
- <Image src="/assets/logo.png" alt="Dev Workflow" width={32} height={32} className="rounded-lg" />
17
- <span className="text-lg font-bold">Dev Workflow</span>
35
+ <Image src="/assets/logo.png" alt="Dev Workflow" width={28} height={28} className="rounded-lg w-7 h-7 sm:w-8 sm:h-8" />
36
+ <span className="text-sm sm:text-lg font-bold text-gray-900 dark:text-white">Dev Workflow</span>
18
37
  </Link>
19
- <div className="hidden md:flex items-center gap-6">
20
- <Link href="/#features" className="text-gray-300 hover:text-white transition">Features</Link>
21
- <Link href="/#history" className="text-gray-300 hover:text-white transition">History</Link>
22
- <Link href="/#api" className="text-gray-300 hover:text-white transition">API</Link>
23
- <Link href="/docs" className="text-gray-300 hover:text-white transition">Docs</Link>
38
+ <div className="hidden md:flex items-center gap-3 lg:gap-6">
39
+ <Link href="/#features" className="text-xs lg:text-sm text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition">Features</Link>
40
+ <Link href="/#history" className="text-xs lg:text-sm text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition">History</Link>
41
+ <Link href="/#api" className="text-xs lg:text-sm text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition">API</Link>
42
+ <Link href="/docs" className="text-xs lg:text-sm text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition">Docs</Link>
43
+ <button
44
+ onClick={toggleTheme}
45
+ className="flex items-center justify-center w-8 h-8 rounded-lg bg-gray-100 dark:bg-white/10 hover:bg-gray-200 dark:hover:bg-white/20 transition text-gray-900 dark:text-white"
46
+ aria-label="Toggle theme"
47
+ >
48
+ {theme === "dark" ? (
49
+ <Sun className="w-4 h-4" />
50
+ ) : (
51
+ <Moon className="w-4 h-4" />
52
+ )}
53
+ </button>
24
54
  <a
25
55
  href="https://github.com/programinglive/dev-workflow-mcp-server"
26
56
  target="_blank"
27
57
  rel="noopener noreferrer"
28
- className="flex items-center gap-2 px-4 py-2 bg-white/5 hover:bg-white/10 rounded-lg transition"
58
+ className="flex items-center gap-1.5 px-3 py-1.5 lg:px-4 lg:py-2 bg-gray-100 dark:bg-white/5 hover:bg-gray-200 dark:hover:bg-white/10 rounded-lg transition text-xs lg:text-sm text-gray-900 dark:text-white"
29
59
  >
30
- <Github className="w-4 h-4" />
60
+ <Github className="w-3.5 h-3.5 lg:w-4 lg:h-4" />
31
61
  GitHub
32
62
  </a>
33
63
  </div>
34
- <button
35
- className="md:hidden p-2 rounded-lg hover:bg-white/5"
36
- onClick={() => setIsOpen(!isOpen)}
37
- >
38
- {isOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
39
- </button>
64
+ <div className="md:hidden flex items-center gap-2">
65
+ <button
66
+ onClick={toggleTheme}
67
+ className="p-2 rounded-lg bg-gray-100 dark:bg-white/10 hover:bg-gray-200 dark:hover:bg-white/20 transition text-gray-900 dark:text-white"
68
+ aria-label="Toggle theme"
69
+ >
70
+ {theme === "dark" ? (
71
+ <Sun className="w-5 h-5" />
72
+ ) : (
73
+ <Moon className="w-5 h-5" />
74
+ )}
75
+ </button>
76
+ <button
77
+ className="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-white/5 transition text-gray-900 dark:text-white"
78
+ onClick={() => setIsOpen(!isOpen)}
79
+ >
80
+ {isOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
81
+ </button>
82
+ </div>
40
83
  </div>
41
84
  </div>
42
85
  {/* Mobile Menu */}
43
86
  {isOpen && (
44
- <div className="md:hidden border-t border-white/10 bg-[#0a0a0f]">
87
+ <div className="md:hidden border-t border-gray-200 dark:border-white/10 bg-white dark:bg-[#0a0a0f] transition-colors duration-300">
45
88
  <div className="px-4 py-4 space-y-3">
46
- <Link href="/#features" className="block text-gray-300 hover:text-white transition" onClick={() => setIsOpen(false)}>Features</Link>
47
- <Link href="/#history" className="block text-gray-300 hover:text-white transition" onClick={() => setIsOpen(false)}>History</Link>
48
- <Link href="/#api" className="block text-gray-300 hover:text-white transition" onClick={() => setIsOpen(false)}>API</Link>
49
- <Link href="/docs" className="block text-gray-300 hover:text-white transition" onClick={() => setIsOpen(false)}>Docs</Link>
89
+ <Link href="/#features" className="block text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition" onClick={() => setIsOpen(false)}>Features</Link>
90
+ <Link href="/#history" className="block text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition" onClick={() => setIsOpen(false)}>History</Link>
91
+ <Link href="/#api" className="block text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition" onClick={() => setIsOpen(false)}>API</Link>
92
+ <Link href="/docs" className="block text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition" onClick={() => setIsOpen(false)}>Docs</Link>
50
93
  <a
51
94
  href="https://github.com/programinglive/dev-workflow-mcp-server"
52
95
  target="_blank"
53
96
  rel="noopener noreferrer"
54
- className="flex items-center gap-2 text-gray-300 hover:text-white transition"
97
+ className="flex items-center gap-2 text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition"
55
98
  >
56
99
  <Github className="w-4 h-4" />
57
100
  GitHub
@@ -107,7 +107,7 @@ export default function Terminal() {
107
107
  };
108
108
 
109
109
  return (
110
- <div ref={containerRef} className="font-mono text-sm space-y-2 min-h-[400px]">
110
+ <div ref={containerRef} className="font-mono text-xs sm:text-sm space-y-1.5 sm:space-y-2 min-h-[300px] sm:min-h-[400px]">
111
111
  {lines}
112
112
  </div>
113
113
  );
@@ -0,0 +1,58 @@
1
+ "use client";
2
+
3
+ import { createContext, useContext, useEffect, useState } from "react";
4
+
5
+ type Theme = "light" | "dark";
6
+
7
+ interface ThemeContextType {
8
+ theme: Theme;
9
+ toggleTheme: () => void;
10
+ }
11
+
12
+ const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
13
+
14
+ export function ThemeProvider({ children }: { children: React.ReactNode }) {
15
+ const [theme, setTheme] = useState<Theme>("dark");
16
+ const [mounted, setMounted] = useState(false);
17
+
18
+ useEffect(() => {
19
+ setMounted(true);
20
+ // Check localStorage for saved theme preference
21
+ const savedTheme = localStorage.getItem("theme") as Theme | null;
22
+ if (savedTheme) {
23
+ setTheme(savedTheme);
24
+ document.documentElement.classList.toggle("dark", savedTheme === "dark");
25
+ } else {
26
+ // Check system preference
27
+ const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
28
+ const initialTheme = prefersDark ? "dark" : "light";
29
+ setTheme(initialTheme);
30
+ document.documentElement.classList.toggle("dark", prefersDark);
31
+ }
32
+ }, []);
33
+
34
+ const toggleTheme = () => {
35
+ const newTheme = theme === "dark" ? "light" : "dark";
36
+ setTheme(newTheme);
37
+ localStorage.setItem("theme", newTheme);
38
+ document.documentElement.classList.toggle("dark", newTheme === "dark");
39
+ };
40
+
41
+ if (!mounted) {
42
+ return <>{children}</>;
43
+ }
44
+
45
+ return (
46
+ <ThemeContext.Provider value={{ theme, toggleTheme }}>
47
+ {children}
48
+ </ThemeContext.Provider>
49
+ );
50
+ }
51
+
52
+ export function useTheme() {
53
+ const context = useContext(ThemeContext);
54
+ if (context === undefined) {
55
+ throw new Error("useTheme must be used within a ThemeProvider");
56
+ }
57
+ return context;
58
+ }
@@ -6,6 +6,7 @@ const config: Config = {
6
6
  "./components/**/*.{js,ts,jsx,tsx,mdx}",
7
7
  "./app/**/*.{js,ts,jsx,tsx,mdx}",
8
8
  ],
9
+ darkMode: "class",
9
10
  theme: {
10
11
  extend: {
11
12
  colors: {
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Build Verification Tests
3
+ * Tests that the website builds successfully and key features are present
4
+ */
5
+
6
+ import { execSync } from 'child_process';
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import { fileURLToPath } from 'url';
10
+
11
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
+ const webDir = path.join(__dirname, '..');
13
+ const outDir = path.join(webDir, 'out');
14
+
15
+ describe('Build Tests', () => {
16
+ test('should build successfully', () => {
17
+ execSync('npm run build', { cwd: webDir, stdio: 'pipe' });
18
+ expect(fs.existsSync(outDir)).toBe(true);
19
+ });
20
+
21
+ test('should generate homepage', () => {
22
+ const indexPath = path.join(outDir, 'index.html');
23
+ expect(fs.existsSync(indexPath)).toBe(true);
24
+ const content = fs.readFileSync(indexPath, 'utf-8');
25
+ expect(content).toContain('Dev Workflow');
26
+ });
27
+
28
+ test('should include dark mode support', () => {
29
+ const indexPath = path.join(outDir, 'index.html');
30
+ const content = fs.readFileSync(indexPath, 'utf-8');
31
+ expect(content).toContain('dark:');
32
+ });
33
+
34
+ test('should include responsive design', () => {
35
+ const indexPath = path.join(outDir, 'index.html');
36
+ const content = fs.readFileSync(indexPath, 'utf-8');
37
+ expect(content).toContain('sm:');
38
+ expect(content).toContain('md:');
39
+ expect(content).toContain('lg:');
40
+ });
41
+ });
42
+
43
+ describe('Theme Toggle Tests', () => {
44
+ test('should have theme persistence', () => {
45
+ const navbarPath = path.join(webDir, 'components', 'Navbar.tsx');
46
+ const content = fs.readFileSync(navbarPath, 'utf-8');
47
+ expect(content).toContain('localStorage');
48
+ });
49
+
50
+ test('should detect system preference', () => {
51
+ const navbarPath = path.join(webDir, 'components', 'Navbar.tsx');
52
+ const content = fs.readFileSync(navbarPath, 'utf-8');
53
+ expect(content).toContain('prefers-color-scheme');
54
+ });
55
+ });
56
+
57
+ describe('Responsive Design Tests', () => {
58
+ test('should optimize for vertical monitors', () => {
59
+ const heroPath = path.join(webDir, 'components', 'Hero.tsx');
60
+ const content = fs.readFileSync(heroPath, 'utf-8');
61
+ expect(content).toContain('xl:grid-cols-2');
62
+ });
63
+
64
+ test('should have responsive navbar', () => {
65
+ const navbarPath = path.join(webDir, 'components', 'Navbar.tsx');
66
+ const content = fs.readFileSync(navbarPath, 'utf-8');
67
+ expect(content).toContain('md:hidden');
68
+ });
69
+ });
@@ -1,58 +0,0 @@
1
- import { readFile, writeFile, mkdir } from 'node:fs/promises';
2
- import path from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
- import { marked } from 'marked';
5
-
6
- const __filename = fileURLToPath(import.meta.url);
7
- const __dirname = path.dirname(__filename);
8
- const projectRoot = path.join(__dirname, '..');
9
-
10
- const docs = [
11
- {
12
- key: 'README.md',
13
- source: path.join(projectRoot, 'README.md'),
14
- output: 'README.html',
15
- },
16
- {
17
- key: 'web-dashboard.md',
18
- source: path.join(projectRoot, 'docs', 'web-dashboard.md'),
19
- output: 'web-dashboard.html',
20
- },
21
- {
22
- key: 'GETTING-STARTED.md',
23
- source: path.join(projectRoot, 'docs', 'antigravity', 'GETTING-STARTED.md'),
24
- output: 'GETTING-STARTED.html',
25
- },
26
- {
27
- key: 'EXAMPLES.md',
28
- source: path.join(projectRoot, 'docs', 'antigravity', 'EXAMPLES.md'),
29
- output: 'EXAMPLES.html',
30
- },
31
- {
32
- key: 'WORKFLOW-TEMPLATES.md',
33
- source: path.join(projectRoot, 'docs', 'antigravity', 'WORKFLOW-TEMPLATES.md'),
34
- output: 'WORKFLOW-TEMPLATES.html',
35
- },
36
- {
37
- key: 'TROUBLESHOOTING.md',
38
- source: path.join(projectRoot, 'docs', 'antigravity', 'TROUBLESHOOTING.md'),
39
- output: 'TROUBLESHOOTING.html',
40
- },
41
- ];
42
-
43
- const distDocDir = path.join(projectRoot, 'dist', 'docs');
44
-
45
- async function buildDocs() {
46
- await mkdir(distDocDir, { recursive: true });
47
-
48
- for (const doc of docs) {
49
- const markdown = await readFile(doc.source, 'utf-8');
50
- const html = marked.parse(markdown);
51
- await writeFile(path.join(distDocDir, doc.output), html, 'utf-8');
52
- }
53
- }
54
-
55
- buildDocs().catch((error) => {
56
- console.error('Failed to build documentation assets:', error);
57
- process.exitCode = 1;
58
- });