lean-spec 0.2.5-dev.20251124045153 → 0.2.5-dev.20251124054130

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 (36) hide show
  1. package/dist/{chunk-IGNO4GX2.js → chunk-6FKLWECL.js} +760 -550
  2. package/dist/chunk-6FKLWECL.js.map +1 -0
  3. package/dist/cli.js +6 -1
  4. package/dist/cli.js.map +1 -1
  5. package/dist/mcp-server.js +1 -1
  6. package/package.json +1 -1
  7. package/templates/examples/api-refactor/README.md +81 -0
  8. package/templates/examples/api-refactor/package.json +16 -0
  9. package/templates/examples/api-refactor/src/app.js +40 -0
  10. package/templates/examples/api-refactor/src/services/currencyService.js +43 -0
  11. package/templates/examples/api-refactor/src/services/timezoneService.js +41 -0
  12. package/templates/examples/api-refactor/src/services/weatherService.js +42 -0
  13. package/templates/examples/dark-theme/README.md +55 -0
  14. package/templates/examples/dark-theme/package.json +16 -0
  15. package/templates/examples/dark-theme/src/public/app.js +92 -0
  16. package/templates/examples/dark-theme/src/public/index.html +38 -0
  17. package/templates/examples/dark-theme/src/public/style.css +163 -0
  18. package/templates/examples/dark-theme/src/server.js +17 -0
  19. package/templates/examples/dashboard-widgets/README.md +70 -0
  20. package/templates/examples/dashboard-widgets/index.html +12 -0
  21. package/templates/examples/dashboard-widgets/package.json +22 -0
  22. package/templates/examples/dashboard-widgets/src/App.css +20 -0
  23. package/templates/examples/dashboard-widgets/src/App.jsx +16 -0
  24. package/templates/examples/dashboard-widgets/src/components/Dashboard.css +17 -0
  25. package/templates/examples/dashboard-widgets/src/components/Dashboard.jsx +15 -0
  26. package/templates/examples/dashboard-widgets/src/components/WidgetWrapper.css +23 -0
  27. package/templates/examples/dashboard-widgets/src/components/WidgetWrapper.jsx +16 -0
  28. package/templates/examples/dashboard-widgets/src/components/widgets/ChartWidget.css +33 -0
  29. package/templates/examples/dashboard-widgets/src/components/widgets/ChartWidget.jsx +28 -0
  30. package/templates/examples/dashboard-widgets/src/components/widgets/StatsWidget.css +24 -0
  31. package/templates/examples/dashboard-widgets/src/components/widgets/StatsWidget.jsx +22 -0
  32. package/templates/examples/dashboard-widgets/src/index.css +13 -0
  33. package/templates/examples/dashboard-widgets/src/main.jsx +10 -0
  34. package/templates/examples/dashboard-widgets/src/utils/mockData.js +30 -0
  35. package/templates/examples/dashboard-widgets/vite.config.js +6 -0
  36. package/dist/chunk-IGNO4GX2.js.map +0 -1
@@ -0,0 +1,163 @@
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ body {
8
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
9
+ background-color: #f5f5f5;
10
+ color: #333;
11
+ line-height: 1.6;
12
+ padding: 20px;
13
+ }
14
+
15
+ .container {
16
+ max-width: 600px;
17
+ margin: 0 auto;
18
+ background: white;
19
+ border-radius: 12px;
20
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
21
+ padding: 30px;
22
+ }
23
+
24
+ header {
25
+ text-align: center;
26
+ margin-bottom: 30px;
27
+ padding-bottom: 20px;
28
+ border-bottom: 2px solid #e0e0e0;
29
+ }
30
+
31
+ h1 {
32
+ font-size: 2em;
33
+ color: #2c3e50;
34
+ margin-bottom: 8px;
35
+ }
36
+
37
+ .subtitle {
38
+ color: #7f8c8d;
39
+ font-size: 0.95em;
40
+ }
41
+
42
+ .task-input {
43
+ display: flex;
44
+ gap: 10px;
45
+ margin-bottom: 25px;
46
+ }
47
+
48
+ #taskInput {
49
+ flex: 1;
50
+ padding: 12px 16px;
51
+ border: 2px solid #e0e0e0;
52
+ border-radius: 8px;
53
+ font-size: 1em;
54
+ transition: border-color 0.2s;
55
+ }
56
+
57
+ #taskInput:focus {
58
+ outline: none;
59
+ border-color: #3498db;
60
+ }
61
+
62
+ #addButton {
63
+ padding: 12px 24px;
64
+ background: #3498db;
65
+ color: white;
66
+ border: none;
67
+ border-radius: 8px;
68
+ font-size: 1em;
69
+ cursor: pointer;
70
+ transition: background 0.2s;
71
+ }
72
+
73
+ #addButton:hover {
74
+ background: #2980b9;
75
+ }
76
+
77
+ .task-list {
78
+ min-height: 200px;
79
+ }
80
+
81
+ #taskList {
82
+ list-style: none;
83
+ }
84
+
85
+ .task-item {
86
+ padding: 15px;
87
+ margin-bottom: 10px;
88
+ background: #f8f9fa;
89
+ border-radius: 8px;
90
+ display: flex;
91
+ align-items: center;
92
+ justify-content: space-between;
93
+ transition: background 0.2s;
94
+ }
95
+
96
+ .task-item:hover {
97
+ background: #e9ecef;
98
+ }
99
+
100
+ .task-item.completed {
101
+ opacity: 0.6;
102
+ }
103
+
104
+ .task-item.completed .task-text {
105
+ text-decoration: line-through;
106
+ }
107
+
108
+ .task-content {
109
+ display: flex;
110
+ align-items: center;
111
+ gap: 12px;
112
+ flex: 1;
113
+ }
114
+
115
+ .task-checkbox {
116
+ width: 20px;
117
+ height: 20px;
118
+ cursor: pointer;
119
+ }
120
+
121
+ .task-text {
122
+ font-size: 1em;
123
+ color: #2c3e50;
124
+ }
125
+
126
+ .delete-button {
127
+ padding: 6px 12px;
128
+ background: #e74c3c;
129
+ color: white;
130
+ border: none;
131
+ border-radius: 6px;
132
+ font-size: 0.85em;
133
+ cursor: pointer;
134
+ transition: background 0.2s;
135
+ }
136
+
137
+ .delete-button:hover {
138
+ background: #c0392b;
139
+ }
140
+
141
+ .stats {
142
+ margin-top: 20px;
143
+ padding-top: 20px;
144
+ border-top: 2px solid #e0e0e0;
145
+ text-align: center;
146
+ color: #7f8c8d;
147
+ font-size: 0.9em;
148
+ }
149
+
150
+ footer {
151
+ margin-top: 30px;
152
+ padding-top: 20px;
153
+ border-top: 2px solid #e0e0e0;
154
+ text-align: center;
155
+ color: #95a5a6;
156
+ font-size: 0.85em;
157
+ }
158
+
159
+ .empty-state {
160
+ text-align: center;
161
+ padding: 40px 20px;
162
+ color: #95a5a6;
163
+ }
@@ -0,0 +1,17 @@
1
+ import express from 'express';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+
8
+ const app = express();
9
+ const PORT = 3000;
10
+
11
+ // Serve static files from public directory
12
+ app.use(express.static(path.join(__dirname, 'public')));
13
+
14
+ app.listen(PORT, () => {
15
+ console.log(`✓ Task Manager running at http://localhost:${PORT}`);
16
+ console.log(`✓ Open your browser and try adding some tasks!`);
17
+ });
@@ -0,0 +1,70 @@
1
+ # Dashboard Widgets Demo
2
+
3
+ > **Tutorial**: [Managing Multiple Features](https://leanspec.dev/docs/tutorials/multiple-features)
4
+
5
+ ## Scenario
6
+
7
+ You're building an analytics dashboard for a SaaS product. The dashboard has some basic widgets (stats cards and a simple chart), but the product team wants to add three new widgets:
8
+ - **Recent Activity Feed** - Show latest user actions
9
+ - **Performance Metrics** - Display system health indicators
10
+ - **Quick Actions Panel** - Common shortcuts for users
11
+
12
+ Each widget needs to be designed, implemented, and integrated into the dashboard grid.
13
+
14
+ ## What's Here
15
+
16
+ A minimal React + Vite dashboard with:
17
+ - Grid layout for widgets
18
+ - Two existing widgets (Stats, Chart)
19
+ - Reusable widget wrapper component
20
+ - Mock data utilities
21
+
22
+ **Files:**
23
+ - `src/App.jsx` - Main dashboard component
24
+ - `src/components/Dashboard.jsx` - Dashboard grid layout
25
+ - `src/components/widgets/` - Existing widgets
26
+ - `src/utils/mockData.js` - Sample data generator
27
+ - `index.html` - Entry point
28
+
29
+ ## Getting Started
30
+
31
+ ```bash
32
+ # Install dependencies
33
+ npm install
34
+
35
+ # Start dev server
36
+ npm run dev
37
+
38
+ # Open http://localhost:5173 in your browser
39
+ ```
40
+
41
+ ## Your Mission
42
+
43
+ Add three new widgets to the dashboard. Follow the tutorial and ask your AI assistant:
44
+
45
+ > "Help me add three new widgets to this dashboard using LeanSpec: Recent Activity Feed, Performance Metrics, and Quick Actions Panel."
46
+
47
+ The AI will guide you through:
48
+ 1. Creating specs for each widget (or one unified spec)
49
+ 2. Designing the widget interfaces
50
+ 3. Implementing components
51
+ 4. Managing dependencies between widgets
52
+ 5. Testing the integrated dashboard
53
+
54
+ ## Current Structure
55
+
56
+ ```
57
+ Dashboard
58
+ ├── StatsWidget (implemented)
59
+ ├── ChartWidget (implemented)
60
+ ├── ActivityWidget (TODO)
61
+ ├── MetricsWidget (TODO)
62
+ └── ActionsWidget (TODO)
63
+ ```
64
+
65
+ ## Tips
66
+
67
+ - Each widget is self-contained with its own component
68
+ - Widgets use the `WidgetWrapper` component for consistent styling
69
+ - Mock data is in `utils/mockData.js` - add new generators as needed
70
+ - Consider which widgets share dependencies (e.g., both might need user data)
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Dashboard Widgets Demo</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.jsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "dashboard-widgets-demo",
3
+ "version": "1.0.0",
4
+ "description": "React Dashboard for LeanSpec Tutorial 2",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "keywords": ["leanspec", "tutorial", "demo"],
12
+ "author": "",
13
+ "license": "MIT",
14
+ "dependencies": {
15
+ "react": "^18.2.0",
16
+ "react-dom": "^18.2.0"
17
+ },
18
+ "devDependencies": {
19
+ "@vitejs/plugin-react": "^4.2.1",
20
+ "vite": "^5.0.0"
21
+ }
22
+ }
@@ -0,0 +1,20 @@
1
+ .app {
2
+ padding: 20px;
3
+ max-width: 1400px;
4
+ margin: 0 auto;
5
+ }
6
+
7
+ header {
8
+ margin-bottom: 30px;
9
+ }
10
+
11
+ header h1 {
12
+ margin: 0 0 5px 0;
13
+ color: #333;
14
+ }
15
+
16
+ header p {
17
+ margin: 0;
18
+ color: #666;
19
+ font-size: 14px;
20
+ }
@@ -0,0 +1,16 @@
1
+ import Dashboard from './components/Dashboard';
2
+ import './App.css';
3
+
4
+ function App() {
5
+ return (
6
+ <div className="app">
7
+ <header>
8
+ <h1>Analytics Dashboard</h1>
9
+ <p>Demo for LeanSpec Tutorial 2</p>
10
+ </header>
11
+ <Dashboard />
12
+ </div>
13
+ );
14
+ }
15
+
16
+ export default App;
@@ -0,0 +1,17 @@
1
+ .dashboard-grid {
2
+ display: grid;
3
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
4
+ gap: 20px;
5
+ }
6
+
7
+ @media (min-width: 768px) {
8
+ .dashboard-grid {
9
+ grid-template-columns: repeat(2, 1fr);
10
+ }
11
+ }
12
+
13
+ @media (min-width: 1200px) {
14
+ .dashboard-grid {
15
+ grid-template-columns: repeat(3, 1fr);
16
+ }
17
+ }
@@ -0,0 +1,15 @@
1
+ import StatsWidget from './widgets/StatsWidget';
2
+ import ChartWidget from './widgets/ChartWidget';
3
+ import './Dashboard.css';
4
+
5
+ function Dashboard() {
6
+ return (
7
+ <div className="dashboard-grid">
8
+ <StatsWidget />
9
+ <ChartWidget />
10
+ {/* TODO: Add new widgets here */}
11
+ </div>
12
+ );
13
+ }
14
+
15
+ export default Dashboard;
@@ -0,0 +1,23 @@
1
+ .widget {
2
+ background: white;
3
+ border-radius: 8px;
4
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
5
+ overflow: hidden;
6
+ }
7
+
8
+ .widget-header {
9
+ padding: 15px 20px;
10
+ border-bottom: 1px solid #eee;
11
+ background: #fafafa;
12
+ }
13
+
14
+ .widget-header h3 {
15
+ margin: 0;
16
+ font-size: 16px;
17
+ font-weight: 600;
18
+ color: #333;
19
+ }
20
+
21
+ .widget-body {
22
+ padding: 20px;
23
+ }
@@ -0,0 +1,16 @@
1
+ import './WidgetWrapper.css';
2
+
3
+ function WidgetWrapper({ title, children }) {
4
+ return (
5
+ <div className="widget">
6
+ <div className="widget-header">
7
+ <h3>{title}</h3>
8
+ </div>
9
+ <div className="widget-body">
10
+ {children}
11
+ </div>
12
+ </div>
13
+ );
14
+ }
15
+
16
+ export default WidgetWrapper;
@@ -0,0 +1,33 @@
1
+ .chart {
2
+ height: 200px;
3
+ }
4
+
5
+ .chart-bars {
6
+ display: flex;
7
+ align-items: flex-end;
8
+ justify-content: space-around;
9
+ height: 100%;
10
+ gap: 8px;
11
+ }
12
+
13
+ .chart-bar-container {
14
+ flex: 1;
15
+ display: flex;
16
+ flex-direction: column;
17
+ align-items: center;
18
+ height: 100%;
19
+ }
20
+
21
+ .chart-bar {
22
+ width: 100%;
23
+ background: linear-gradient(to top, #2563eb, #60a5fa);
24
+ border-radius: 4px 4px 0 0;
25
+ transition: height 0.3s ease;
26
+ min-height: 4px;
27
+ }
28
+
29
+ .chart-label {
30
+ margin-top: 8px;
31
+ font-size: 11px;
32
+ color: #666;
33
+ }
@@ -0,0 +1,28 @@
1
+ import WidgetWrapper from '../WidgetWrapper';
2
+ import { getChartData } from '../../utils/mockData';
3
+ import './ChartWidget.css';
4
+
5
+ function ChartWidget() {
6
+ const data = getChartData();
7
+ const maxValue = Math.max(...data.map(d => d.value));
8
+
9
+ return (
10
+ <WidgetWrapper title="Activity Trend">
11
+ <div className="chart">
12
+ <div className="chart-bars">
13
+ {data.map((item, index) => (
14
+ <div key={index} className="chart-bar-container">
15
+ <div
16
+ className="chart-bar"
17
+ style={{ height: `${(item.value / maxValue) * 100}%` }}
18
+ />
19
+ <div className="chart-label">{item.label}</div>
20
+ </div>
21
+ ))}
22
+ </div>
23
+ </div>
24
+ </WidgetWrapper>
25
+ );
26
+ }
27
+
28
+ export default ChartWidget;
@@ -0,0 +1,24 @@
1
+ .stats-grid {
2
+ display: grid;
3
+ grid-template-columns: repeat(2, 1fr);
4
+ gap: 15px;
5
+ }
6
+
7
+ .stat-item {
8
+ text-align: center;
9
+ padding: 10px;
10
+ }
11
+
12
+ .stat-value {
13
+ font-size: 28px;
14
+ font-weight: bold;
15
+ color: #2563eb;
16
+ margin-bottom: 5px;
17
+ }
18
+
19
+ .stat-label {
20
+ font-size: 12px;
21
+ color: #666;
22
+ text-transform: uppercase;
23
+ letter-spacing: 0.5px;
24
+ }
@@ -0,0 +1,22 @@
1
+ import WidgetWrapper from '../WidgetWrapper';
2
+ import { getStats } from '../../utils/mockData';
3
+ import './StatsWidget.css';
4
+
5
+ function StatsWidget() {
6
+ const stats = getStats();
7
+
8
+ return (
9
+ <WidgetWrapper title="Quick Stats">
10
+ <div className="stats-grid">
11
+ {stats.map((stat, index) => (
12
+ <div key={index} className="stat-item">
13
+ <div className="stat-value">{stat.value}</div>
14
+ <div className="stat-label">{stat.label}</div>
15
+ </div>
16
+ ))}
17
+ </div>
18
+ </WidgetWrapper>
19
+ );
20
+ }
21
+
22
+ export default StatsWidget;
@@ -0,0 +1,13 @@
1
+ body {
2
+ margin: 0;
3
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5
+ sans-serif;
6
+ -webkit-font-smoothing: antialiased;
7
+ -moz-osx-font-smoothing: grayscale;
8
+ background: #f5f5f5;
9
+ }
10
+
11
+ * {
12
+ box-sizing: border-box;
13
+ }
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom/client';
3
+ import App from './App';
4
+ import './index.css';
5
+
6
+ ReactDOM.createRoot(document.getElementById('root')).render(
7
+ <React.StrictMode>
8
+ <App />
9
+ </React.StrictMode>
10
+ );
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Mock data generators for dashboard widgets
3
+ */
4
+
5
+ export function getStats() {
6
+ return [
7
+ { label: 'Users', value: '2,453' },
8
+ { label: 'Revenue', value: '$12.5K' },
9
+ { label: 'Orders', value: '186' },
10
+ { label: 'Growth', value: '+23%' },
11
+ ];
12
+ }
13
+
14
+ export function getChartData() {
15
+ return [
16
+ { label: 'Mon', value: 45 },
17
+ { label: 'Tue', value: 62 },
18
+ { label: 'Wed', value: 38 },
19
+ { label: 'Thu', value: 71 },
20
+ { label: 'Fri', value: 55 },
21
+ { label: 'Sat', value: 28 },
22
+ { label: 'Sun', value: 34 },
23
+ ];
24
+ }
25
+
26
+ // TODO: Add more mock data generators for new widgets
27
+ // Example:
28
+ // export function getActivityFeed() { ... }
29
+ // export function getPerformanceMetrics() { ... }
30
+ // export function getQuickActions() { ... }
@@ -0,0 +1,6 @@
1
+ import { defineConfig } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ });