nextpy-framework 2.3.1__tar.gz → 2.4.1__tar.gz

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 (82) hide show
  1. nextpy_framework-2.4.1/.nextpy_framework/nextpy/cli.py +2418 -0
  2. nextpy_framework-2.4.1/.nextpy_framework/nextpy/config.py +68 -0
  3. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/core/component_renderer.py +3 -1
  4. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/core/component_router.py +4 -2
  5. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/main.py +5 -3
  6. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/plugins/builtin.py +5 -4
  7. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/server/app.py +21 -15
  8. nextpy_framework-2.4.1/.nextpy_framework/nextpy_framework.egg-info/PKG-INFO +1574 -0
  9. nextpy_framework-2.4.1/PKG-INFO +1574 -0
  10. nextpy_framework-2.4.1/README.md +1533 -0
  11. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/pyproject.toml +1 -1
  12. nextpy_framework-2.3.1/.nextpy_framework/nextpy/cli.py +0 -1257
  13. nextpy_framework-2.3.1/.nextpy_framework/nextpy/config.py +0 -75
  14. nextpy_framework-2.3.1/.nextpy_framework/nextpy_framework.egg-info/PKG-INFO +0 -999
  15. nextpy_framework-2.3.1/PKG-INFO +0 -999
  16. nextpy_framework-2.3.1/README.md +0 -958
  17. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/__init__.py +0 -0
  18. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/auth.py +0 -0
  19. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/builder.py +0 -0
  20. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/components/__init__.py +0 -0
  21. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/components/debug/AutoDebug.py +0 -0
  22. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/components/debug/DebugIcon.py +0 -0
  23. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/components/debug/DebugIconFixed.py +0 -0
  24. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/components/feedback.py +0 -0
  25. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/components/form.py +0 -0
  26. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/components/head.py +0 -0
  27. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/components/hooks_provider.py +0 -0
  28. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/components/image.py +0 -0
  29. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/components/layout.py +0 -0
  30. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/components/link.py +0 -0
  31. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/components/loader.py +0 -0
  32. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/components/navigation.py +0 -0
  33. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/components/toast.py +0 -0
  34. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/components/ui.py +0 -0
  35. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/components/visual.py +0 -0
  36. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/components.py +0 -0
  37. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/core/__init__.py +0 -0
  38. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/core/builder.py +0 -0
  39. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/core/data_fetching.py +0 -0
  40. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/core/demo_pages_simple.py +0 -0
  41. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/core/demo_router.py +0 -0
  42. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/core/renderer.py +0 -0
  43. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/core/router.py +0 -0
  44. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/core/sync.py +0 -0
  45. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/db.py +0 -0
  46. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/dev_server.py +0 -0
  47. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/dev_tools.py +0 -0
  48. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/errors.py +0 -0
  49. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/hooks.py +0 -0
  50. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/hooks_provider.py +0 -0
  51. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/hooks_provider_new.py +0 -0
  52. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/jsx.py +0 -0
  53. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/jsx_preprocessor.py +0 -0
  54. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/jsx_transformer.py +0 -0
  55. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/performance.py +0 -0
  56. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/plugins/__init__.py +0 -0
  57. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/plugins/base.py +0 -0
  58. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/plugins/config.py +0 -0
  59. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/plugins.py +0 -0
  60. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/py.typed +0 -0
  61. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/security.py +0 -0
  62. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/server/__init__.py +0 -0
  63. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/server/debug.py +0 -0
  64. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/server/middleware.py +0 -0
  65. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/true_jsx.py +0 -0
  66. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/utils/__init__.py +0 -0
  67. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/utils/cache.py +0 -0
  68. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/utils/email.py +0 -0
  69. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/utils/file_upload.py +0 -0
  70. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/utils/logging.py +0 -0
  71. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/utils/search.py +0 -0
  72. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/utils/seo.py +0 -0
  73. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/utils/validators.py +0 -0
  74. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy/websocket.py +0 -0
  75. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy_framework.egg-info/SOURCES.txt +0 -0
  76. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy_framework.egg-info/dependency_links.txt +0 -0
  77. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy_framework.egg-info/entry_points.txt +0 -0
  78. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy_framework.egg-info/requires.txt +0 -0
  79. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/.nextpy_framework/nextpy_framework.egg-info/top_level.txt +0 -0
  80. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/LICENSE +0 -0
  81. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/setup.cfg +0 -0
  82. {nextpy_framework-2.3.1 → nextpy_framework-2.4.1}/tests/test_routing.py +0 -0
@@ -0,0 +1,2418 @@
1
+ """
2
+ NextPy CLI - Command-line interface for NextPy projects
3
+ Commands: dev, build, start
4
+ """
5
+
6
+ import os
7
+ import sys
8
+ import time
9
+ import asyncio
10
+ import signal
11
+ from pathlib import Path
12
+ from typing import Optional
13
+
14
+ import click
15
+ import uvicorn
16
+
17
+ try:
18
+ from watchdog.observers import Observer
19
+ from watchdog.events import FileSystemEventHandler
20
+ WATCHDOG_AVAILABLE = True
21
+ except ImportError:
22
+ WATCHDOG_AVAILABLE = False
23
+ Observer = None
24
+ FileSystemEventHandler = None
25
+
26
+
27
+ class HotReloadHandler:
28
+ """Handles file system changes for hot reload with enhanced JSX support"""
29
+
30
+ def __init__(self, reload_callback, debug: bool = False):
31
+ self.reload_callback = reload_callback
32
+ self._debounce_timer = None
33
+ self.debug = debug
34
+ self.last_reload_time = 0
35
+ self.reload_cooldown = 0.5 # 500ms cooldown between reloads
36
+
37
+ # Enhanced file patterns for better JSX detection
38
+ self.file_patterns = {
39
+ "python": [".py"],
40
+ "jsx": [".py.jsx", ".jsx"],
41
+ "templates": [".html", ".htm", ".jinja2", ".j2"],
42
+ "styles": [".css", ".scss", ".sass", ".less"],
43
+ "scripts": [".js", ".ts", ".mjs", ".cjs"],
44
+ "assets": [".json", ".yaml", ".yml", ".toml", ".ini"],
45
+ "config": [".env", ".env.example", "requirements.txt", "package.json", "tailwind.config.js", "postcss.config.js"]
46
+ }
47
+
48
+ # Directories to watch
49
+ self.watch_dirs = {
50
+ "pages", "components", "templates", "public",
51
+ "static", "assets", "styles", "scripts", ".nextpy_framework"
52
+ }
53
+
54
+ # Files that should always trigger reload
55
+ self.critical_files = {
56
+ "main.py", "app.py", "config.py", "settings.py",
57
+ "requirements.txt", "package.json", "pyproject.toml"
58
+ }
59
+
60
+ def _should_reload_file(self, file_path: str) -> bool:
61
+ """Determine if a file change should trigger reload"""
62
+ file_path = Path(file_path)
63
+
64
+ # Always reload critical files
65
+ if file_path.name in self.critical_files:
66
+ return True
67
+
68
+ # Check if file is in a watched directory
69
+ parent_dirs = [part.name for part in file_path.parents]
70
+ if not any(dir_name in self.watch_dirs for dir_name in parent_dirs):
71
+ # If not in watched directories, check if it's in root
72
+ if len(parent_dirs) == 0 or parent_dirs[-1] == ".":
73
+ return any(file_path.name.endswith(pattern) for patterns in self.file_patterns.values() for pattern in patterns)
74
+ return False
75
+
76
+ # Check file extension against all patterns
77
+ all_extensions = []
78
+ for patterns in self.file_patterns.values():
79
+ all_extensions.extend(patterns)
80
+
81
+ return any(file_path.name.endswith(ext) for ext in all_extensions)
82
+
83
+ def _get_file_type(self, file_path: str) -> str:
84
+ """Categorize file type for logging"""
85
+ file_path = Path(file_path)
86
+
87
+ for file_type, extensions in self.file_patterns.items():
88
+ if any(file_path.name.endswith(ext) for ext in extensions):
89
+ return file_type
90
+
91
+ return "unknown"
92
+
93
+ def _debounce_reload(self, file_path: str = None):
94
+ """Debounce reload calls to prevent excessive reloading"""
95
+ current_time = time.time()
96
+
97
+ if current_time - self.last_reload_time < self.reload_cooldown:
98
+ return
99
+
100
+ self.last_reload_time = current_time
101
+
102
+ if self.debug and file_path:
103
+ file_type = self._get_file_type(file_path)
104
+ click.echo(f" 🔄 Hot reload triggered by {file_type} file: {Path(file_path).name}", dim=True)
105
+
106
+ self._trigger_reload()
107
+
108
+ def on_modified(self, event):
109
+ """Handle file modification events"""
110
+ if event.is_directory:
111
+ return
112
+
113
+ if self._should_reload_file(event.src_path):
114
+ self._debounce_reload(event.src_path)
115
+
116
+ def on_created(self, event):
117
+ """Handle file creation events"""
118
+ if not event.is_directory and self._should_reload_file(event.src_path):
119
+ self._debounce_reload(event.src_path)
120
+
121
+ def on_deleted(self, event):
122
+ """Handle file deletion events"""
123
+ if not event.is_directory and self._should_reload_file(event.src_path):
124
+ self._debounce_reload(event.src_path)
125
+
126
+ def on_moved(self, event):
127
+ """Handle file move/rename events"""
128
+ if not event.is_directory:
129
+ # Handle both source and destination
130
+ if hasattr(event, 'dest_path') and event.dest_path:
131
+ if self._should_reload_file(event.src_path) or self._should_reload_file(event.dest_path):
132
+ self._debounce_reload(event.dest_path or event.src_path)
133
+ else:
134
+ if self._should_reload_file(event.src_path):
135
+ self._debounce_reload(event.src_path)
136
+
137
+ def _trigger_reload(self):
138
+ """Trigger the reload callback"""
139
+ if self.reload_callback:
140
+ self.reload_callback()
141
+
142
+ def setup_file_watcher(self, project_dir: str = "."):
143
+ """Setup enhanced file watcher with specific patterns"""
144
+ if not WATCHDOG_AVAILABLE:
145
+ click.echo(" ⚠️ Watchdog not installed. Hot reload disabled.", fg="yellow")
146
+ click.echo(" Install with: pip install watchdog", fg="yellow")
147
+ return None
148
+
149
+ observer = Observer()
150
+ event_handler = WatchdogHotReloadHandler(self._trigger_reload, debug=self.debug)
151
+
152
+ # Watch specific directories with recursive monitoring
153
+ for watch_dir in self.watch_dirs:
154
+ dir_path = Path(project_dir) / watch_dir
155
+ if dir_path.exists():
156
+ observer.schedule(event_handler, str(dir_path), recursive=True)
157
+ if self.debug:
158
+ click.echo(f" 📁 Watching directory: {watch_dir}", dim=True)
159
+
160
+ # Also watch root directory for critical files
161
+ root_path = Path(project_dir)
162
+ if root_path.exists():
163
+ observer.schedule(event_handler, str(root_path), recursive=False)
164
+
165
+ return observer
166
+
167
+
168
+ if WATCHDOG_AVAILABLE:
169
+ class WatchdogHotReloadHandler(HotReloadHandler, FileSystemEventHandler):
170
+ """Enhanced watchdog handler with better file filtering"""
171
+ pass
172
+ else:
173
+ class WatchdogHotReloadHandler(HotReloadHandler):
174
+ """Fallback handler when watchdog is not available"""
175
+ pass
176
+
177
+ def find_main_module():
178
+ # main.py is always expected at the project root for NextPy projects
179
+ # We ensure the current directory is in sys.path before calling uvicorn.run
180
+ return "main:app"
181
+
182
+
183
+ def _format_size(size_bytes: int) -> str:
184
+ """Format file size in human-readable format"""
185
+ if size_bytes == 0:
186
+ return "0 B"
187
+
188
+ size_names = ["B", "KB", "MB", "GB", "TB"]
189
+ i = 0
190
+ size = float(size_bytes)
191
+
192
+ while size >= 1024.0 and i < len(size_names) - 1:
193
+ size /= 1024.0
194
+ i += 1
195
+
196
+ return f"{size:.1f} {size_names[i]}"
197
+
198
+ @click.group()
199
+ @click.version_option(version="1.0.0", prog_name="NextPy")
200
+ def cli():
201
+ """NextPy - A Python web framework inspired by Next.js"""
202
+ pass
203
+
204
+
205
+ @cli.command()
206
+ @click.option("--port", "-p", default=5000, help="Port to run the server on")
207
+ @click.option("--host", "-h", default="0.0.0.0", help="Host to bind to")
208
+ @click.option("--reload/--no-reload", default=True, help="Enable hot reload")
209
+ @click.option("--debug/--no-debug", default=True, help="Enable debug mode")
210
+ def dev(port: int, host: str, reload: bool, debug: bool):
211
+ """Start the development server with enhanced hot reload"""
212
+ click.echo(click.style("\n NextPy Development Server", fg="cyan", bold=True))
213
+ click.echo(click.style(" ========================\n", fg="cyan"))
214
+
215
+ # Set debug environment variable
216
+ if debug:
217
+ os.environ["NEXTPY_DEBUG"] = "true"
218
+ os.environ["DEBUG"] = "true"
219
+ os.environ["DEVELOPMENT"] = "true"
220
+ else:
221
+ os.environ.pop("NEXTPY_DEBUG", None)
222
+ os.environ.pop("DEBUG", None)
223
+ os.environ.pop("DEVELOPMENT", None)
224
+
225
+ _ensure_project_structure()
226
+
227
+ click.echo(f" - Mode: {'Development' if debug else 'Production'}")
228
+ click.echo(f" - Host: {host} (accessible at http://localhost:{port})")
229
+ click.echo(f" - Port: {port}")
230
+ click.echo(f" - Reload: {'Enabled' if reload else 'Disabled'}")
231
+ click.echo(f" - Debug: {'Enabled' if debug else 'Disabled'}")
232
+
233
+ if reload and not WATCHDOG_AVAILABLE:
234
+ click.echo(click.style(" - Watchdog: Not Available (install: pip install watchdog)", fg="yellow"))
235
+ elif reload:
236
+ click.echo(f" - Watchdog: Available")
237
+
238
+ if debug:
239
+ click.echo(f" - Debug Icon: ✅ Auto-enabled")
240
+ click.echo(f" - Console Capture: ✅ Enabled")
241
+ click.echo(f" - Performance Monitoring: ✅ Enabled")
242
+
243
+ click.echo(f"\n ✨ Server ready at http://0.0.0.0:{port}")
244
+ click.echo(f" 🌐 Open http://localhost:{port} in your browser\n")
245
+
246
+ project_dir = Path('.')
247
+ os.chdir(project_dir)
248
+
249
+ # Ensure the current directory is in sys.path for module discovery
250
+ if str(project_dir.resolve()) not in sys.path:
251
+ sys.path.insert(0, str(project_dir.resolve()))
252
+
253
+ main_module = find_main_module()
254
+
255
+ if reload:
256
+ # Enhanced reload configuration with JSX support
257
+ reload_dirs = [
258
+ "pages",
259
+ "components",
260
+ "templates",
261
+ "public",
262
+ "static",
263
+ "styles",
264
+ "scripts",
265
+ ".nextpy_framework"
266
+ ]
267
+
268
+ # Filter to only existing directories
269
+ existing_reload_dirs = []
270
+ for reload_dir in reload_dirs:
271
+ dir_path = project_dir / reload_dir
272
+ if dir_path.exists():
273
+ existing_reload_dirs.append(reload_dir)
274
+ if debug:
275
+ click.echo(click.style(f" 📁 Watching: {reload_dir}/"))
276
+
277
+ # Enhanced reload patterns for JSX files
278
+ reload_includes = [
279
+ "*.py",
280
+ "*.py.jsx",
281
+ "*.jsx",
282
+ "*.html",
283
+ "*.htm",
284
+ "*.css",
285
+ "*.scss",
286
+ "*.sass",
287
+ "*.less",
288
+ "*.js",
289
+ "*.ts",
290
+ "*.json",
291
+ "*.yaml",
292
+ "*.yml",
293
+ "*.env",
294
+ "requirements.txt",
295
+ "package.json",
296
+ "tailwind.config.js",
297
+ "postcss.config.js"
298
+ ]
299
+
300
+ uvicorn.run(
301
+ main_module,
302
+ host=host,
303
+ port=port,
304
+ reload=True,
305
+ reload_dirs=existing_reload_dirs,
306
+ reload_includes=reload_includes,
307
+ log_level="info",
308
+ )
309
+ else:
310
+ uvicorn.run(
311
+ main_module,
312
+ host=host,
313
+ port=port,
314
+ log_level="info",
315
+ )
316
+
317
+
318
+ @cli.command()
319
+ @click.option("--out", "-o", default="out", help="Output directory for static files")
320
+ @click.option("--clean/--no-clean", default=True, help="Clean output directory first")
321
+ def build(out: str, clean: bool):
322
+ """Build the project for production with enhanced feedback"""
323
+ click.echo(click.style("\n 🔨 NextPy Static Build", fg="green", bold=True))
324
+ click.echo(click.style(" ===================\n", fg="green"))
325
+
326
+ try:
327
+ from nextpy.core.builder import Builder
328
+
329
+ click.echo(f" 📂 Output directory: {out}/")
330
+ if clean:
331
+ click.echo(f" 🧹 Cleaning output directory...")
332
+
333
+ click.echo(f" ⚙️ Initializing builder...")
334
+ builder = Builder(out_dir=out)
335
+
336
+ click.echo(f" 🏗️ Building static files...")
337
+
338
+ async def run_build():
339
+ manifest = await builder.build(clean=clean)
340
+ return manifest
341
+
342
+ manifest = asyncio.run(run_build())
343
+
344
+ pages_count = len(manifest.get("pages", {}))
345
+ assets_count = len(manifest.get("assets", []))
346
+ total_size = manifest.get("total_size", 0)
347
+
348
+ click.echo()
349
+ click.echo(click.style(f" ✅ Build completed successfully!", fg="green", bold=True))
350
+ click.echo(f" 📄 Pages built: {pages_count}")
351
+ click.echo(f" 🎨 Assets processed: {assets_count}")
352
+ click.echo(f" 💾 Total size: {_format_size(total_size)}")
353
+ click.echo(f" 📍 Output: {out}/")
354
+ click.echo()
355
+ click.echo(click.style(f" 🚀 Ready for deployment!", fg="cyan", bold=True))
356
+ click.echo(f" 📖 Serve with: nextpy start --port 5000")
357
+ click.echo()
358
+
359
+ except Exception as e:
360
+ click.echo(click.style(f" ❌ Build failed: {str(e)}", fg="red"))
361
+ if "Builder" not in str(e):
362
+ click.echo(click.style(f" 💡 Make sure you're in a NextPy project directory", fg="yellow"))
363
+
364
+
365
+ @cli.command()
366
+ @click.option("--port", "-p", default=5000, help="Port to run the server on")
367
+ @click.option("--host", "-h", default="0.0.0.0", help="Host to bind to")
368
+ def start(port: int, host: str):
369
+ """Start the production server with enhanced feedback"""
370
+ click.echo(click.style("\n 🚀 NextPy Production Server", fg="green", bold=True))
371
+ click.echo(click.style(" ========================\n", fg="green"))
372
+
373
+ click.echo(f" 🏭 Mode: Production")
374
+ click.echo(f" 🌐 Host: {host} (accessible at http://localhost:{port})")
375
+ click.echo(f" 🔌 Port: {port}")
376
+ click.echo(f" 👥 Workers: 4 (multi-process)")
377
+ click.echo(f" 📝 Logging: Warning level only")
378
+
379
+ click.echo(f"\n ✨ Production server ready at http://0.0.0.0:{port}")
380
+ click.echo(f" 🌐 Open http://localhost:{port} in your browser\n")
381
+ click.echo(click.style(f" 💡 Press Ctrl+C to stop the server", fg="yellow"))
382
+ click.echo()
383
+
384
+ try:
385
+ os.chdir(Path.cwd())
386
+
387
+ uvicorn.run(
388
+ "main:app",
389
+ host=host,
390
+ port=port,
391
+ workers=4,
392
+ log_level="warning",
393
+ )
394
+
395
+ except KeyboardInterrupt:
396
+ click.echo(click.style("\n 👋 Server stopped gracefully", fg="cyan"))
397
+ except Exception as e:
398
+ click.echo(click.style(f"\n ❌ Server error: {str(e)}", fg="red"))
399
+ click.echo(click.style(f" 💡 Make sure you have a main.py file with an app instance", fg="yellow"))
400
+
401
+
402
+ @cli.command()
403
+ @click.argument("name")
404
+ def create(name: str):
405
+ """Create a new NextPy project with True JSX support"""
406
+ click.echo(click.style(f"\n 🚀 Creating NextPy project: {name}", fg="cyan", bold=True))
407
+ click.echo(click.style(" " + "=" * (25 + len(name)) + "\n", fg="cyan"))
408
+
409
+ project_dir = Path(name)
410
+
411
+ if project_dir.exists():
412
+ click.echo(click.style(f" ❌ Error: Directory '{name}' already exists", fg="red"))
413
+ click.echo(click.style(f" 💡 Try a different name or remove the existing directory", fg="yellow"))
414
+ return
415
+
416
+ click.echo(f" 📁 Creating project structure...")
417
+
418
+ try:
419
+ _create_project_structure(project_dir)
420
+
421
+ click.echo(click.style(f" ✅ Project successfully created!", fg="green", bold=True))
422
+ click.echo(f"\n 📍 Location: {project_dir.absolute()}")
423
+ click.echo(f"\n 🎯 Next steps:")
424
+ click.echo(f" 1️⃣ cd {name}")
425
+ click.echo(f" 2️⃣ pip install -r requirements.txt")
426
+ click.echo(f" 3️⃣ nextpy dev")
427
+ click.echo(f"\n 🌐 Your app will be available at: http://localhost:5000")
428
+ click.echo(f"\n 📚 Documentation: https://github.com/IBRAHIMFONYUY/nextpy-framework")
429
+ click.echo()
430
+
431
+ except Exception as e:
432
+ click.echo(click.style(f" ❌ Failed to create project: {str(e)}", fg="red"))
433
+ click.echo(click.style(f" 💡 Check the error message above for more details", fg="yellow"))
434
+ # Clean up partial creation
435
+
436
+ # Clean up partial creation
437
+ if project_dir.exists():
438
+ import shutil
439
+ shutil.rmtree(project_dir, ignore_errors=True)
440
+ click.echo(click.style(f" 🧹 Cleaned up partial files", fg="yellow"))
441
+
442
+
443
+ @cli.command()
444
+ def routes():
445
+ """Display all registered routes with detailed information"""
446
+ click.echo(click.style("\n 🛣️ NextPy Routes Overview", fg="cyan", bold=True))
447
+ click.echo(click.style(" =====================\n", fg="cyan"))
448
+
449
+ try:
450
+ from nextpy.core.router import Router
451
+
452
+ router = Router()
453
+ router.scan_pages()
454
+
455
+ page_routes = [r for r in router.routes if not r.is_api]
456
+ api_routes = router.api_routes
457
+
458
+ click.echo(click.style(f" 📄 Page Routes ({len(page_routes)} total)", fg="blue", bold=True))
459
+ if page_routes:
460
+ for i, route in enumerate(page_routes, 1):
461
+ dynamic = " 🔀" if route.is_dynamic else " 📄"
462
+ file_info = f"({route.file_path})"
463
+ click.echo(f" {i:2d}. {dynamic} {route.path:<30} {file_info}")
464
+ else:
465
+ click.echo(f" ℹ️ No page routes found")
466
+
467
+ click.echo()
468
+ click.echo(click.style(f" 🔌 API Routes ({len(api_routes)} total)", fg="green", bold=True))
469
+ if api_routes:
470
+ for i, route in enumerate(api_routes, 1):
471
+ dynamic = " 🔀" if route.is_dynamic else " 🔌"
472
+ file_info = f"({route.file_path})"
473
+ methods = "[GET, POST, PUT, DELETE]" if hasattr(route, 'handler') else "[GET]"
474
+ click.echo(f" {i:2d}. {dynamic} {route.path:<30} {methods:<20} {file_info}")
475
+ else:
476
+ click.echo(f" ℹ️ No API routes found")
477
+
478
+ click.echo()
479
+ click.echo(click.style(f" 📊 Summary:", fg="yellow", bold=True))
480
+ click.echo(f" Total Routes: {len(page_routes + api_routes)}")
481
+ click.echo(f" Dynamic Routes: {len([r for r in page_routes + api_routes if r.is_dynamic])}")
482
+ click.echo(f" Static Routes: {len([r for r in page_routes + api_routes if not r.is_dynamic])}")
483
+ click.echo()
484
+
485
+ except Exception as e:
486
+ click.echo(click.style(f" ❌ Error scanning routes: {str(e)}", fg="red"))
487
+
488
+
489
+ @cli.command()
490
+ @click.option("--out", "-o", default="out", help="Output directory for static files")
491
+ def export(out: str):
492
+ """Export static files with enhanced feedback"""
493
+ click.echo(click.style("\n 📦 NextPy Export", fg="green", bold=True))
494
+ click.echo(click.style(" =============\n", fg="green"))
495
+
496
+ try:
497
+ from nextpy.core.builder import Builder
498
+
499
+ click.echo(f" 📂 Output directory: {out}/")
500
+ click.echo(f" ⚙️ Initializing exporter...")
501
+
502
+ builder = Builder(out_dir=out)
503
+
504
+ click.echo(f" 📤 Exporting static files...")
505
+
506
+ async def run_export():
507
+ manifest = await builder.export_static()
508
+ return manifest
509
+
510
+ manifest = asyncio.run(run_export())
511
+
512
+ files_count = len(manifest.get("files", []))
513
+ total_size = manifest.get("total_size", 0)
514
+
515
+ click.echo()
516
+ click.echo(click.style(f" ✅ Export completed successfully!", fg="green", bold=True))
517
+ click.echo(f" 📁 Files exported: {files_count}")
518
+ click.echo(f" 💾 Total size: {_format_size(total_size)}")
519
+ click.echo(f" 📍 Output: {out}/")
520
+ click.echo()
521
+ click.echo(click.style(f" 🚀 Ready for static hosting!", fg="cyan", bold=True))
522
+ click.echo()
523
+
524
+ except Exception as e:
525
+ click.echo(click.style(f" ❌ Export failed: {str(e)}", fg="red"))
526
+ click.echo(click.style(f" 💡 Make sure you're in a NextPy project directory", fg="yellow"))
527
+
528
+
529
+ @cli.command()
530
+ def version():
531
+ """Show version and system information"""
532
+ click.echo(click.style("\n 📋 NextPy Framework Info", fg="cyan", bold=True))
533
+ click.echo(click.style(" ===================\n", fg="cyan"))
534
+
535
+ click.echo(f" 🏷️ Version: 2.4.1")
536
+ click.echo(f" 🐍 Python: {sys.version.split()[0]}")
537
+ click.echo(f" ⚡ Framework: NextPy")
538
+ click.echo(f" 🎨 Architecture: True JSX")
539
+ click.echo(f" 🖥️ Development Server: uvicorn")
540
+ click.echo(f" 🔄 Hot Reload: Available")
541
+ click.echo(f" 📁 Static Files: Available")
542
+ click.echo(f" 🔌 API Routes: Available")
543
+ click.echo(f" 📄 Page Routes: Available")
544
+ click.echo(f" 🧩 Component Routes: Available")
545
+ click.echo(f" 📚 Component Library: Available")
546
+ click.echo(f" 👨‍💻 Developer: Ibrahim Fonyuy")
547
+ click.echo(f" 📜 License: MIT")
548
+ click.echo(f" 🐙 GitHub: https://github.com/IBRAHIMFONYUY/nextpy-framework")
549
+ click.echo(f" 📖 Documentation: https://nextpy.org/docs")
550
+ click.echo(f" 🆘 Support: https://github.com/IBRAHIMFONYUY/nextpy-framework/issues")
551
+
552
+ click.echo()
553
+
554
+
555
+ @cli.command()
556
+ def info():
557
+ """Show comprehensive framework and system information"""
558
+ click.echo(click.style("\n 🖥️ NextPy System Information", fg="cyan", bold=True))
559
+ click.echo(click.style(" ==========================\n", fg="cyan"))
560
+
561
+ # Framework info
562
+ click.echo(click.style(" 📦 Framework Details:", fg="blue", bold=True))
563
+ click.echo(f" Version: 2.0.0")
564
+ click.echo(f" Architecture: True JSX")
565
+ click.echo(f" Python: {sys.version.split()[0]}")
566
+
567
+ # Feature status
568
+ click.echo(click.style("\n ⚡ Feature Status:", fg="green", bold=True))
569
+ watchdog_status = "✅ Available" if WATCHDOG_AVAILABLE else "❌ Not Available (pip install watchdog)"
570
+ click.echo(f" Hot Reload: {watchdog_status}")
571
+ click.echo(f" Static Files: ✅ Available")
572
+ click.echo(f" API Routes: ✅ Available")
573
+ click.echo(f" Page Routes: ✅ Available")
574
+ click.echo(f" Component Library: ✅ Available")
575
+
576
+ # Project structure check
577
+ click.echo(click.style("\n 📁 Project Structure:", fg="yellow", bold=True))
578
+ required_dirs = ["pages", "components", "templates", "public"]
579
+ for dir_name in required_dirs:
580
+ status = "✅" if Path(dir_name).exists() else "❌"
581
+ click.echo(f" {dir_name}/: {status}")
582
+
583
+ # Available commands
584
+ click.echo(click.style("\n 🛠️ Available Commands:", fg="purple", bold=True))
585
+ commands = [
586
+ ("nextpy dev", "Start development server"),
587
+ ("nextpy build", "Build for production"),
588
+ ("nextpy start", "Start production server"),
589
+ ("nextpy create <name>", "Create new project"),
590
+ ("nextpy generate <type> <name>", "Generate components/pages/APIs"),
591
+ ("nextpy routes", "Show all routes"),
592
+ ("nextpy export", "Export static files"),
593
+ ("nextpy version", "Show version info"),
594
+ ("nextpy info", "Show this information")
595
+ ]
596
+ for cmd, desc in commands:
597
+ click.echo(f" {cmd:<25} - {desc}")
598
+
599
+ click.echo()
600
+
601
+
602
+ @cli.command()
603
+ @click.argument("type", type=click.Choice(["page", "api", "component"]))
604
+ @click.argument("name")
605
+ def generate(type: str, name: str):
606
+ """Generate new page, API endpoint, or component"""
607
+ click.echo(click.style(f"\n Generating {type}: {name}", fg="cyan", bold=True))
608
+ click.echo(click.style(" " + "=" * (20 + len(name) + len(type)) + "\n", fg="cyan"))
609
+
610
+ if type == "page":
611
+ _generate_page(name)
612
+ elif type == "api":
613
+ _generate_api(name)
614
+ elif type == "component":
615
+ _generate_component(name)
616
+
617
+ click.echo(click.style(f"\n {type.title()} '{name}' created successfully!\n", fg="green", bold=True))
618
+
619
+
620
+ @cli.group()
621
+ def plugin():
622
+ """Plugin management commands"""
623
+ pass
624
+
625
+
626
+ @plugin.command()
627
+ def list():
628
+ """List all available plugins"""
629
+ click.echo(click.style("\n 🔌 NextPy Plugins", fg="cyan", bold=True))
630
+ click.echo(click.style(" ================\n", fg="cyan"))
631
+
632
+ try:
633
+ from nextpy.plugins import plugin_manager
634
+
635
+ plugin_info = plugin_manager.get_plugin_info()
636
+
637
+ click.echo(click.style(f" 📊 Overview:", fg="blue", bold=True))
638
+ click.echo(f" Total plugins: {plugin_info['total_plugins']}")
639
+ click.echo(f" Enabled: {plugin_info['enabled_plugins']}")
640
+ click.echo(f" Disabled: {plugin_info['total_plugins'] - plugin_info['enabled_plugins']}")
641
+
642
+ click.echo()
643
+ click.echo(click.style(f" 📋 Plugin Details:", fg="green", bold=True))
644
+
645
+ for plugin in plugin_info['plugins']:
646
+ status = "✅" if plugin['enabled'] else "❌"
647
+ priority = plugin['priority']
648
+ click.echo(f" {status} {plugin['name']:<15} v{plugin['version']:<8} (Priority: {priority})")
649
+
650
+ if plugin['dependencies']:
651
+ click.echo(f" Dependencies: {', '.join(plugin['dependencies'])}")
652
+
653
+ click.echo()
654
+
655
+ except ImportError:
656
+ click.echo(click.style(" ❌ Plugin system not available", fg="red"))
657
+ click.echo(click.style(" 💡 Install with: pip install nextpy[plugins]", fg="yellow"))
658
+ except Exception as e:
659
+ click.echo(click.style(f" ❌ Error: {str(e)}", fg="red"))
660
+
661
+
662
+ @plugin.command()
663
+ @click.argument("name")
664
+ @click.option("--enable/--disable", default=True, help="Enable or disable the plugin")
665
+ def enable(name: str, enable: bool):
666
+ """Enable or disable a plugin"""
667
+ action = "Enabling" if enable else "Disabling"
668
+ click.echo(click.style(f"\n {action} plugin: {name}", fg="cyan", bold=True))
669
+ click.echo(click.style(" " + "=" * (20 + len(name)) + "\n", fg="cyan"))
670
+
671
+ try:
672
+ from nextpy.plugins import plugin_manager
673
+
674
+ if enable:
675
+ plugin_manager.enable_plugin(name)
676
+ click.echo(click.style(f" ✅ Plugin '{name}' enabled successfully", fg="green"))
677
+ else:
678
+ plugin_manager.disable_plugin(name)
679
+ click.echo(click.style(f" ❌ Plugin '{name}' disabled", fg="yellow"))
680
+
681
+ click.echo()
682
+
683
+ except ImportError:
684
+ click.echo(click.style(" ❌ Plugin system not available", fg="red"))
685
+ except Exception as e:
686
+ click.echo(click.style(f" ❌ Error: {str(e)}", fg="red"))
687
+
688
+
689
+ @plugin.command()
690
+ @click.argument("name")
691
+ @click.option("--config", help="Plugin configuration as JSON string")
692
+ def configure(name: str, config: str):
693
+ """Configure a plugin"""
694
+ click.echo(click.style(f"\n ⚙️ Configuring plugin: {name}", fg="cyan", bold=True))
695
+ click.echo(click.style(" " + "=" * (20 + len(name)) + "\n", fg="cyan"))
696
+
697
+ try:
698
+ from nextpy.plugins import plugin_manager
699
+ import json
700
+
701
+ if config:
702
+ try:
703
+ config_dict = json.loads(config)
704
+ except json.JSONDecodeError:
705
+ click.echo(click.style(" ❌ Invalid JSON configuration", fg="red"))
706
+ return
707
+ else:
708
+ config_dict = {}
709
+
710
+ plugin_manager.configure_plugin(name, config_dict)
711
+ click.echo(click.style(f" ✅ Plugin '{name}' configured successfully", fg="green"))
712
+
713
+ if config_dict:
714
+ click.echo(f" Configuration: {json.dumps(config_dict, indent=2)}")
715
+
716
+ click.echo()
717
+
718
+ except ImportError:
719
+ click.echo(click.style(" ❌ Plugin system not available", fg="red"))
720
+ except Exception as e:
721
+ click.echo(click.style(f" ❌ Error: {str(e)}", fg="red"))
722
+
723
+
724
+ @plugin.command()
725
+ @click.argument("file_path", type=click.Path(exists=True))
726
+ def load(file_path: str):
727
+ """Load a plugin from file"""
728
+ click.echo(click.style(f"\n 📦 Loading plugin from: {file_path}", fg="cyan", bold=True))
729
+ click.echo(click.style(" " + "=" * (25 + len(file_path)) + "\n", fg="cyan"))
730
+
731
+ try:
732
+ from nextpy.plugins import plugin_manager
733
+ from pathlib import Path
734
+
735
+ plugin = plugin_manager.load_plugin_from_file(Path(file_path))
736
+ plugin_manager.register_plugin(plugin)
737
+
738
+ click.echo(click.style(f" ✅ Plugin '{plugin.name}' loaded successfully", fg="green"))
739
+ click.echo(f" Version: {plugin.version}")
740
+ click.echo(f" Priority: {plugin.priority.value}")
741
+
742
+ click.echo()
743
+
744
+ except ImportError:
745
+ click.echo(click.style(" ❌ Plugin system not available", fg="red"))
746
+ except Exception as e:
747
+ click.echo(click.style(f" ❌ Error: {str(e)}", fg="red"))
748
+
749
+
750
+ def _generate_page(name: str):
751
+ """Generate a new page"""
752
+ page_path = Path(f"pages/{name}.py")
753
+ page_path.parent.mkdir(parents=True, exist_ok=True)
754
+
755
+ content = f'''"""Generated {name} page"""
756
+
757
+ def {name.title()}(props = None):
758
+ """{name.title()} page component"""
759
+ props = props or {{}}
760
+
761
+ title = props.get("title", "{name.title()} Page")
762
+
763
+ return (
764
+ <div class="max-w-4xl px-4 py-12 mx-auto">
765
+ <h1 class="mb-6 text-4xl font-bold text-gray-900">{{title}}</h1>
766
+ <p class="text-lg text-gray-600">
767
+ This is the {name} page generated by NextPy.
768
+ </p>
769
+ </div>
770
+ )
771
+
772
+ def getServerSideProps(context):
773
+ return {{
774
+ "props": {{
775
+ "title": "{name.title()} Page"
776
+ }}
777
+ }}
778
+
779
+ default = {name.title()}
780
+ '''
781
+
782
+ page_path.write_text(content)
783
+ click.echo(f" Created: {page_path}")
784
+
785
+
786
+ def _generate_api(name: str):
787
+ """Generate a new API endpoint"""
788
+ api_path = Path(f"pages/api/{name}.py")
789
+ api_path.parent.mkdir(parents=True, exist_ok=True)
790
+
791
+ content = f'''"""Generated {name} API endpoint"""
792
+
793
+ async def get(request):
794
+ """GET /api/{name}"""
795
+ return {{
796
+ "message": "Hello from {name} API!",
797
+ "endpoint": "/api/{name}",
798
+ "method": "GET"
799
+ }}
800
+
801
+ async def post(request):
802
+ """POST /api/{name}"""
803
+ body = await request.json()
804
+ return {{
805
+ "message": "POST request received",
806
+ "data": body,
807
+ "endpoint": "/api/{name}",
808
+ "method": "POST"
809
+ }}
810
+ '''
811
+
812
+ api_path.write_text(content)
813
+ click.echo(f" Created: {api_path}")
814
+
815
+
816
+ def _generate_component(name: str):
817
+ """Generate a new component"""
818
+ component_path = Path(f"components/{name}.py")
819
+ component_path.parent.mkdir(parents=True, exist_ok=True)
820
+
821
+ content = f'''"""Generated {name} component"""
822
+
823
+ def {name.title()}(props = None):
824
+ """{name.title()} component"""
825
+ props = props or {{}}
826
+
827
+ children = props.get("children", "")
828
+ className = props.get("className", "")
829
+
830
+ return (
831
+ <div class="{name.lower()}-component " + className>
832
+ {{children}}
833
+ </div>
834
+ )
835
+
836
+ default = {name.title()}
837
+ '''
838
+
839
+ component_path.write_text(content)
840
+ click.echo(f" Created: {component_path}")
841
+
842
+
843
+ def _ensure_project_structure():
844
+ """Ensure the basic project structure exists"""
845
+ dirs = ["pages", "pages/api", "templates", "public", "public/css", "public/js"]
846
+
847
+ for dir_path in dirs:
848
+ Path(dir_path).mkdir(parents=True, exist_ok=True)
849
+
850
+
851
+ def _create_project_structure(project_dir: Path):
852
+ """Create a complete NextPy project structure with all integrations"""
853
+ dirs = [
854
+ "pages",
855
+ "pages/api",
856
+ "pages/blog",
857
+ "pages/api/users",
858
+ "components",
859
+ "components/ui",
860
+ "components/layout",
861
+ "templates",
862
+ "public",
863
+ "public/css",
864
+ "public/js",
865
+ "public/images",
866
+ "styles",
867
+ "models",
868
+ "utils",
869
+ "hooks",
870
+ "middleware",
871
+ "tests",
872
+ "docs",
873
+ ".vscode"
874
+ ]
875
+
876
+ for dir_path in dirs:
877
+ (project_dir / dir_path).mkdir(parents=True, exist_ok=True)
878
+ click.echo(f" Created: {dir_path}/")
879
+
880
+ # Create styles.css with Tailwind directives
881
+ (project_dir / "styles.css").write_text('''/* NextPy Styles */
882
+ @tailwind base;
883
+ @tailwind components;
884
+ @tailwind utilities;
885
+
886
+ /* Custom styles */
887
+ body {
888
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
889
+ }
890
+
891
+ .debug-icon {
892
+ position: fixed;
893
+ bottom: 20px;
894
+ right: 20px;
895
+ background: #3b82f6;
896
+ color: white;
897
+ padding: 8px 12px;
898
+ border-radius: 8px;
899
+ font-size: 12px;
900
+ font-weight: bold;
901
+ z-index: 9999;
902
+ cursor: pointer;
903
+ transition: all 0.3s ease;
904
+ }
905
+
906
+ .debug-icon:hover {
907
+ background: #2563eb;
908
+ transform: scale(1.05);
909
+ }
910
+ ''')
911
+ click.echo(" Created: styles.css")
912
+
913
+ # Create tailwind.config.js with Python support
914
+ (project_dir / "tailwind.config.js").write_text('''module.exports = {
915
+ content: [
916
+ './pages/**/*.{js,ts,jsx,tsx,mdx,py}',
917
+ './components/**/*.{js,ts,jsx,tsx,mdx,py}',
918
+ './templates/**/*.{html,htm}',
919
+ './app/**/*.{js,ts,jsx,tsx,mdx,py}',
920
+ ],
921
+ theme: {
922
+ extend: {
923
+ colors: {
924
+ blue: {
925
+ 50: '#eff6ff',
926
+ 100: '#dbeafe',
927
+ 200: '#bfdbfe',
928
+ 300: '#93c5fd',
929
+ 400: '#60a5fa',
930
+ 500: '#3b82f6',
931
+ 600: '#2563eb',
932
+ 700: '#1d4ed8',
933
+ 800: '#1e40af',
934
+ 900: '#1e3a8a',
935
+ },
936
+ red: {
937
+ 50: '#fef2f2',
938
+ 100: '#fee2e2',
939
+ 200: '#fecaca',
940
+ 300: '#fca5a5',
941
+ 400: '#f87171',
942
+ 500: '#ef4444',
943
+ 600: '#dc2626',
944
+ 700: '#b91c1c',
945
+ 800: '#991b1b',
946
+ 900: '#7f1d1d',
947
+ },
948
+ green: {
949
+ 50: '#f0fdf4',
950
+ 100: '#dcfce7',
951
+ 200: '#bbf7d0',
952
+ 300: '#86efac',
953
+ 400: '#4ade80',
954
+ 500: '#22c55e',
955
+ 600: '#16a34a',
956
+ 700: '#15803d',
957
+ 800: '#166534',
958
+ 900: '#14532d',
959
+ },
960
+ purple: {
961
+ 50: '#faf5ff',
962
+ 100: '#f3e8ff',
963
+ 200: '#e9d5ff',
964
+ 300: '#d8b4fe',
965
+ 400: '#c084fc',
966
+ 500: '#a855f7',
967
+ 600: '#9333ea',
968
+ 700: '#7c3aed',
969
+ 800: '#6b21a8',
970
+ 900: '#581c87',
971
+ }
972
+ }
973
+ },
974
+ },
975
+ plugins: [
976
+ require('@tailwindcss/postcss'),
977
+ ],
978
+ };''')
979
+ click.echo(" Created: tailwind.config.js")
980
+
981
+ # Create postcss.config.js with new Tailwind plugin
982
+ (project_dir / "postcss.config.js").write_text('''module.exports = {
983
+ plugins: {
984
+ '@tailwindcss/postcss': {},
985
+ autoprefixer: {},
986
+ },
987
+ };''')
988
+ click.echo(" Created: postcss.config.js")
989
+
990
+ # Create package.json with Node.js dependencies
991
+ (project_dir / "package.json").write_text('''{
992
+ "name": "nextpy-app",
993
+ "version": "1.0.0",
994
+ "description": "A NextPy application with True JSX and Tailwind CSS",
995
+ "main": "index.js",
996
+ "scripts": {
997
+ "dev": "nextpy dev",
998
+ "build": "nextpy build",
999
+ "start": "nextpy start"
1000
+ },
1001
+ "keywords": ["python", "nextpy", "jsx", "tailwind"],
1002
+ "author": "",
1003
+ "license": "MIT",
1004
+ "devDependencies": {
1005
+ "autoprefixer": "^10.4.22",
1006
+ "postcss": "^8.5.6",
1007
+ "tailwindcss": "^4.1.17"
1008
+ },
1009
+ "dependencies": {
1010
+ "@tailwindcss/postcss": "^4.1.18",
1011
+ "postcss-cli": "^11.0.1"
1012
+ }
1013
+ }''')
1014
+ click.echo(" Created: package.json")
1015
+
1016
+ # Create interactive homepage with advanced features
1017
+ (project_dir / "pages" / "index.py").write_text('''"""Interactive Homepage with True JSX"""
1018
+
1019
+ def Home(props=None):
1020
+ props = props or {}
1021
+ title = props.get("title", "Welcome to NextPy!")
1022
+ message = props.get("message", "Build amazing web apps with Python and True JSX")
1023
+
1024
+ return (
1025
+ <div class="min-h-screen bg-gradient-to-br from-blue-600 via-purple-600 to-pink-600">
1026
+ {/* Navigation */}
1027
+ <nav class="bg-white border-b border-white bg-opacity-10 backdrop-blur-md border-opacity-20">
1028
+ <div class="px-4 mx-auto max-w-7xl sm:px-6 lg:px-8">
1029
+ <div class="flex items-center justify-between h-16">
1030
+ <div class="flex items-center">
1031
+ <h1 class="text-xl font-bold text-white">NextPy</h1>
1032
+ </div>
1033
+ <div class="flex space-x-4">
1034
+ <a href="/about" class="px-3 py-2 text-sm font-medium text-white transition-colors rounded-md hover:text-blue-200">
1035
+ About
1036
+ </a>
1037
+ <a href="/features" class="px-3 py-2 text-sm font-medium text-white transition-colors rounded-md hover:text-blue-200">
1038
+ Features
1039
+ </a>
1040
+ <a href="/docs" class="px-3 py-2 text-sm font-medium text-white transition-colors rounded-md hover:text-blue-200">
1041
+ Docs
1042
+ </a>
1043
+ </div>
1044
+ </div>
1045
+ </div>
1046
+ </nav>
1047
+
1048
+ {/* Hero Section */}
1049
+ <div class="relative overflow-hidden">
1050
+ <div class="mx-auto max-w-7xl">
1051
+ <div class="relative z-10 pb-8 bg-transparent sm:pb-16 md:pb-20 lg:pb-28 xl:pb-32">
1052
+ <main class="mx-auto mt-10 max-w-7xl sm:mt-12 sm:px-6 lg:mt-16 lg:px-8 xl:mt-20">
1053
+ <div class="text-center">
1054
+ <h1 class="text-4xl font-extrabold tracking-tight text-white sm:text-5xl md:text-6xl">
1055
+ <span class="block xl:inline">Build amazing web apps with</span>
1056
+ <span class="block text-blue-200 xl:inline">Python and True JSX</span>
1057
+ </h1>
1058
+ <p class="max-w-lg mx-auto mt-6 text-xl text-blue-100 sm:text-2xl">
1059
+ {message}
1060
+ </p>
1061
+ <div class="flex justify-center mt-10">
1062
+ <a href="/getting-started" class="inline-flex items-center justify-center px-8 py-3 text-base font-medium text-blue-600 transition-all duration-300 transform bg-white border border-transparent rounded-md hover:bg-blue-50 md:py-4 md:text-lg md:px-10 hover:scale-105">
1063
+ Get Started
1064
+ </a>
1065
+ <a href="https://github.com/nextpy/nextpy" class="inline-flex items-center justify-center px-8 py-3 ml-4 text-base font-medium text-white transition-all duration-300 transform bg-blue-500 border border-transparent rounded-md hover:bg-blue-600 md:py-4 md:text-lg md:px-10 hover:scale-105">
1066
+ GitHub
1067
+ </a>
1068
+ </div>
1069
+ </div>
1070
+ </main>
1071
+ </div>
1072
+ </div>
1073
+
1074
+ {/* Background decoration */}
1075
+ <div class="absolute inset-0 -z-10">
1076
+ <div class="absolute top-0 transform -translate-x-1/2 left-1/2 blur-3xl opacity-20">
1077
+ <div class="rounded-full aspect-square w-96 h-96 bg-gradient-to-r from-blue-400 to-purple-600"></div>
1078
+ </div>
1079
+ <div class="absolute top-0 transform translate-x-1/2 right-1/2 blur-3xl opacity-20">
1080
+ <div class="rounded-full aspect-square w-96 h-96 bg-gradient-to-r from-purple-400 to-pink-600"></div>
1081
+ </div>
1082
+ </div>
1083
+ </div>
1084
+
1085
+ {/* Features Section */}
1086
+ <div class="py-12 bg-white">
1087
+ <div class="px-4 mx-auto max-w-7xl sm:px-6 lg:px-8">
1088
+ <div class="lg:text-center">
1089
+ <h2 class="text-base font-semibold tracking-wide text-blue-600 uppercase">
1090
+ Features
1091
+ </h2>
1092
+ <p class="mt-2 text-3xl font-extrabold tracking-tight text-gray-900 sm:text-4xl">
1093
+ Everything you need to build amazing apps
1094
+ </p>
1095
+ </div>
1096
+
1097
+ <div class="mt-10">
1098
+ <div class="space-y-10 md:space-y-0 md:grid md:grid-cols-2 md:gap-x-8 md:gap-y-10 lg:grid-cols-3">
1099
+ {/* Feature 1 */}
1100
+ <div class="relative">
1101
+ <div class="absolute flex items-center justify-center w-12 h-12 text-white bg-blue-500 rounded-md">
1102
+ <svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
1103
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7m0 0v7l9-11h-7z" />
1104
+ </svg>
1105
+ </div>
1106
+ <p class="ml-16 text-lg font-medium leading-6 text-gray-900">
1107
+ True JSX Components
1108
+ </p>
1109
+ <p class="mt-2 ml-16 text-base text-gray-500">
1110
+ Write React-like components directly in Python with full JSX support.
1111
+ </p>
1112
+ <a href="/jsx-demo" class="mt-4 ml-16 text-base font-medium text-blue-600 hover:text-blue-500">
1113
+ Learn more →
1114
+ </a>
1115
+ </div>
1116
+
1117
+ {/* Feature 2 */}
1118
+ <div class="relative">
1119
+ <div class="absolute flex items-center justify-center w-12 h-12 text-white bg-purple-500 rounded-md">
1120
+ <svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
1121
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16m-7 6h7" />
1122
+ </svg>
1123
+ </div>
1124
+ <p class="ml-16 text-lg font-medium leading-6 text-gray-900">
1125
+ Tailwind CSS Integration
1126
+ </p>
1127
+ <p class="mt-2 ml-16 text-base text-gray-500">
1128
+ Built-in Tailwind CSS v4 with PostCSS compilation and utility classes.
1129
+ </p>
1130
+ <a href="/tailwind-demo" class="mt-4 ml-16 text-base font-medium text-blue-600 hover:text-blue-500">
1131
+ Learn more →
1132
+ </a>
1133
+ </div>
1134
+
1135
+ {/* Feature 3 */}
1136
+ <div class="relative">
1137
+ <div class="absolute flex items-center justify-center w-12 h-12 text-white bg-green-500 rounded-md">
1138
+ <svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
1139
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 2 9 9 0 0118 0z" />
1140
+ </svg>
1141
+ </div>
1142
+ <p class="ml-16 text-lg font-medium leading-6 text-gray-900">
1143
+ File-based Routing
1144
+ </p>
1145
+ <p class="mt-2 ml-16 text-base text-gray-500">
1146
+ Automatic route discovery with support for dynamic routes and API endpoints.
1147
+ </p>
1148
+ <a href="/routing-demo" class="mt-4 ml-16 text-base font-medium text-blue-600 hover:text-blue-500">
1149
+ Learn more →
1150
+ </a>
1151
+ </div>
1152
+ </div>
1153
+ </div>
1154
+ </div>
1155
+ </div>
1156
+
1157
+ {/* Interactive Demo Section */}
1158
+ <div class="py-12 bg-gray-50">
1159
+ <div class="px-4 mx-auto max-w-7xl sm:px-6 lg:px-8">
1160
+ <div class="text-center">
1161
+ <h2 class="text-3xl font-extrabold text-gray-900">
1162
+ Try It Yourself
1163
+ </h2>
1164
+ <p class="max-w-2xl mt-4 text-xl text-gray-500">
1165
+ Interactive demos showing NextPy capabilities
1166
+ </p>
1167
+ </div>
1168
+
1169
+ <div class="grid grid-cols-1 gap-8 mt-12 sm:grid-cols-2 lg:grid-cols-3">
1170
+ {/* Interactive Counter */}
1171
+ <div class="p-6 bg-white rounded-lg shadow-lg">
1172
+ <h3 class="text-lg font-medium text-gray-900">Live Counter</h3>
1173
+ <p class="mt-2 text-sm text-gray-500">Interactive state management demo</p>
1174
+ <div class="mt-4">
1175
+ <button class="px-4 py-2 text-white transition-colors bg-blue-500 rounded hover:bg-blue-600">
1176
+ Click me!
1177
+ </button>
1178
+ <span class="ml-4 text-lg font-semibold">Count: 0</span>
1179
+ </div>
1180
+ </div>
1181
+
1182
+ {/* Form Demo */}
1183
+ <div class="p-6 bg-white rounded-lg shadow-lg">
1184
+ <h3 class="text-lg font-medium text-gray-900">Form Handling</h3>
1185
+ <p class="mt-2 text-sm text-gray-500">Server-side form processing</p>
1186
+ <div class="mt-4">
1187
+ <input type="text" placeholder="Type something..." class="w-full px-3 py-2 border border-gray-300 rounded-md" />
1188
+ <button class="w-full px-4 py-2 mt-2 text-white transition-colors bg-green-500 rounded hover:bg-green-600">
1189
+ Submit
1190
+ </button>
1191
+ </div>
1192
+ </div>
1193
+
1194
+ {/* API Demo */}
1195
+ <div class="p-6 bg-white rounded-lg shadow-lg">
1196
+ <h3 class="text-lg font-medium text-gray-900">API Integration</h3>
1197
+ <p class="mt-2 text-sm text-gray-500">RESTful API endpoints</p>
1198
+ <div class="mt-4">
1199
+ <button class="w-full px-4 py-2 text-white transition-colors bg-purple-500 rounded hover:bg-purple-600">
1200
+ Call API
1201
+ </button>
1202
+ <div class="mt-2 text-xs text-gray-600">Response will appear here</div>
1203
+ </div>
1204
+ </div>
1205
+ </div>
1206
+ </div>
1207
+ </div>
1208
+
1209
+ {/* Footer */}
1210
+ <footer class="bg-gray-900">
1211
+ <div class="px-4 py-12 mx-auto max-w-7xl sm:px-6 lg:px-8">
1212
+ <div class="flex flex-col items-center space-y-4">
1213
+ <p class="text-base text-center text-gray-400">
1214
+ Built with ❤️ using NextPy Framework
1215
+ </p>
1216
+ <div class="flex space-x-6">
1217
+ <a href="/about" class="text-gray-400 hover:text-gray-300">About</a>
1218
+ <a href="/docs" class="text-gray-400 hover:text-gray-300">Documentation</a>
1219
+ <a href="https://github.com/nextpy/nextpy" class="text-gray-400 hover:text-gray-300">GitHub</a>
1220
+ </div>
1221
+ </div>
1222
+ </div>
1223
+ </footer>
1224
+ </div>
1225
+ )
1226
+
1227
+ def getServerSideProps(context):
1228
+ return {
1229
+ "props": {
1230
+ "title": "Welcome to NextPy!",
1231
+ "message": "Build amazing web apps with Python and True JSX"
1232
+ }
1233
+ }
1234
+
1235
+ default = Home
1236
+ ''')
1237
+ click.echo(" Created: pages/index.py (interactive homepage)")
1238
+
1239
+ # Create enhanced about page with interactive features
1240
+ (project_dir / "pages" / "about.py").write_text('''"""Enhanced About page with True JSX"""
1241
+
1242
+ def About(props=None):
1243
+ """About page component with interactive features"""
1244
+ props = props or {}
1245
+
1246
+ title = props.get("title", "About NextPy")
1247
+ description = props.get("description", "The Python web framework that brings React-like development to Python")
1248
+
1249
+ return (
1250
+ <div class="min-h-screen bg-gray-50">
1251
+ {/* Hero Section */}
1252
+ <div class="text-white bg-gradient-to-r from-blue-600 to-purple-600">
1253
+ <div class="px-4 py-16 mx-auto max-w-7xl sm:py-24 sm:px-6 lg:px-8">
1254
+ <div class="text-center">
1255
+ <h1 class="text-4xl font-extrabold tracking-tight sm:text-5xl lg:text-6xl">
1256
+ {title}
1257
+ </h1>
1258
+ <p class="max-w-2xl mx-auto mt-6 text-xl text-blue-100">
1259
+ {description}
1260
+ </p>
1261
+ <div class="flex justify-center mt-10 space-x-4">
1262
+ <a href="/features" class="inline-flex items-center justify-center px-8 py-3 text-base font-medium text-blue-600 bg-white border border-transparent rounded-md hover:bg-blue-50 md:py-4 md:text-lg md:px-10">
1263
+ Explore Features
1264
+ </a>
1265
+ <a href="/getting-started" class="inline-flex items-center justify-center px-8 py-3 text-base font-medium text-white bg-blue-500 border border-transparent rounded-md hover:bg-blue-600 md:py-4 md:text-lg md:px-10">
1266
+ Get Started
1267
+ </a>
1268
+ </div>
1269
+ </div>
1270
+ </div>
1271
+ </div>
1272
+
1273
+ {/* Features Grid */}
1274
+ <div class="py-12 bg-white">
1275
+ <div class="px-4 mx-auto max-w-7xl sm:px-6 lg:px-8">
1276
+ <div class="lg:text-center">
1277
+ <h2 class="text-base font-semibold tracking-wide text-blue-600 uppercase">
1278
+ Why Choose NextPy?
1279
+ </h2>
1280
+ <p class="mt-2 text-3xl font-extrabold tracking-tight text-gray-900 sm:text-4xl">
1281
+ Everything you need to build modern web applications
1282
+ </p>
1283
+ </div>
1284
+
1285
+ <div class="mt-10">
1286
+ <div class="gap-8 space-y-10 md:space-y-0 md:grid md:grid-cols-2 lg:grid-cols-3">
1287
+ {/* Feature 1 */}
1288
+ <div class="relative">
1289
+ <div class="absolute flex items-center justify-center w-12 h-12 text-white bg-blue-500 rounded-md">
1290
+ <svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
1291
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7m0 0v7l9-11h-7z" />
1292
+ </svg>
1293
+ </div>
1294
+ <h3 class="ml-16 text-lg font-medium leading-6 text-gray-900">True JSX Support</h3>
1295
+ <p class="mt-2 ml-16 text-base text-gray-500">
1296
+ Write React-like components with JSX syntax directly in Python. No transpilation needed.
1297
+ </p>
1298
+ </div>
1299
+
1300
+ {/* Feature 2 */}
1301
+ <div class="relative">
1302
+ <div class="absolute flex items-center justify-center w-12 h-12 text-white bg-purple-500 rounded-md">
1303
+ <svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
1304
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16m-7 6h7" />
1305
+ </svg>
1306
+ </div>
1307
+ <h3 class="ml-16 text-lg font-medium leading-6 text-gray-900">File-based Routing</h3>
1308
+ <p class="mt-2 ml-16 text-base text-gray-500">
1309
+ Automatic route discovery with support for dynamic routes and API endpoints.
1310
+ </p>
1311
+ </div>
1312
+
1313
+ {/* Feature 3 */}
1314
+ <div class="relative">
1315
+ <div class="absolute flex items-center justify-center w-12 h-12 text-white bg-green-500 rounded-md">
1316
+ <svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
1317
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 2 9 9 0 0118 0z" />
1318
+ </svg>
1319
+ </div>
1320
+ <h3 class="ml-16 text-lg font-medium leading-6 text-gray-900">Tailwind CSS Integration</h3>
1321
+ <p class="mt-2 ml-16 text-base text-gray-500">
1322
+ Built-in Tailwind CSS v4 with PostCSS compilation and utility classes.
1323
+ </p>
1324
+ </div>
1325
+
1326
+ {/* Feature 4 */}
1327
+ <div class="relative">
1328
+ <div class="absolute flex items-center justify-center w-12 h-12 text-white bg-red-500 rounded-md">
1329
+ <svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
1330
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7m0 0v7l9-11h-7z" />
1331
+ </svg>
1332
+ </div>
1333
+ <h3 class="ml-16 text-lg font-medium leading-6 text-gray-900">Server-side Rendering</h3>
1334
+ <p class="mt-2 ml-16 text-base text-gray-500">
1335
+ Fast initial page loads with server-side rendering and data fetching.
1336
+ </p>
1337
+ </div>
1338
+
1339
+ {/* Feature 5 */}
1340
+ <div class="relative">
1341
+ <div class="absolute flex items-center justify-center w-12 h-12 text-white bg-yellow-500 rounded-md">
1342
+ <svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
1343
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 00-1.065 2.572c1.756.426 1.756 2.924 0 3.35-1.756a1.724 1.724 0 00-1.066-2.573c1.756-.426 1.756-2.924 0-3.35 1.756a1.724 1.724 0 00-2.573-1.066c-1.756.426-1.756-2.924 0-3.35 1.756A1.724 1.724 0 006.573 2.572C3.31 7.76 1.574 8.686 4.317 8.686a1.724 1.724 0 00-1.066-2.572c1.756-.426 1.756-2.924 0-3.35 1.756a1.724 1.724 0 00-2.573-1.066c-1.756.426-1.756-2.924 0-3.35 1.756A1.724 1.724 0 001.066 2.572c1.756.426 1.756 2.924 0 3.35-1.756a1.724 1.724 0 002.573 1.066z" />
1344
+ </svg>
1345
+ </div>
1346
+ <h3 class="ml-16 text-lg font-medium leading-6 text-gray-900">Hot Reload Development</h3>
1347
+ <p class="mt-2 ml-16 text-base text-gray-500">
1348
+ Instant hot reload when saving files with Watchdog integration.
1349
+ </p>
1350
+ </div>
1351
+
1352
+ {/* Feature 6 */}
1353
+ <div class="relative">
1354
+ <div class="absolute flex items-center justify-center w-12 h-12 text-white bg-indigo-500 rounded-md">
1355
+ <svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
1356
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
1357
+ </svg>
1358
+ </div>
1359
+ <h3 class="ml-16 text-lg font-medium leading-6 text-gray-900">API Routes</h3>
1360
+ <p class="mt-2 ml-16 text-base text-gray-500">
1361
+ Built-in FastAPI support for creating RESTful API endpoints.
1362
+ </p>
1363
+ </div>
1364
+ </div>
1365
+ </div>
1366
+ </div>
1367
+ </div>
1368
+
1369
+ {/* Interactive Demo Section */}
1370
+ <div class="py-12 bg-gray-50">
1371
+ <div class="px-4 mx-auto max-w-7xl sm:px-6 lg:px-8">
1372
+ <div class="text-center">
1373
+ <h2 class="text-3xl font-extrabold text-gray-900">
1374
+ See It In Action
1375
+ </h2>
1376
+ <p class="max-w-2xl mt-4 text-xl text-gray-500">
1377
+ Try these interactive demos to experience NextPy capabilities
1378
+ </p>
1379
+ </div>
1380
+
1381
+ <div class="grid grid-cols-1 gap-8 mt-12 sm:grid-cols-2 lg:grid-cols-3">
1382
+ {/* JSX Demo */}
1383
+ <div class="p-6 transition-shadow bg-white rounded-lg shadow-lg hover:shadow-xl">
1384
+ <div class="text-center">
1385
+ <div class="flex items-center justify-center w-12 h-12 mx-auto mb-4 text-white bg-blue-500 rounded-md">
1386
+ <svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
1387
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7m0 0v7l9-11h-7z" />
1388
+ </svg>
1389
+ </div>
1390
+ <h3 class="mb-2 text-lg font-medium text-gray-900">JSX Components</h3>
1391
+ <p class="mb-4 text-sm text-gray-500">Interactive component demo</p>
1392
+ <button onclick="alert('Hello from JSX!')" class="w-full px-4 py-2 text-white transition-colors bg-blue-500 rounded hover:bg-blue-600">
1393
+ Try JSX Demo
1394
+ </button>
1395
+ </div>
1396
+
1397
+ {/* Tailwind Demo */}
1398
+ <div class="p-6 transition-shadow bg-white rounded-lg shadow-lg hover:shadow-xl">
1399
+ <div class="text-center">
1400
+ <div class="flex items-center justify-center w-12 h-12 mx-auto mb-4 text-white bg-purple-500 rounded-md">
1401
+ <svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
1402
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16m-7 6h7" />
1403
+ </svg>
1404
+ </div>
1405
+ <h3 class="mb-2 text-lg font-medium text-gray-900">Tailwind CSS</h3>
1406
+ <p class="mb-4 text-sm text-gray-500">Beautiful styling with utility classes</p>
1407
+ <button class="w-full px-4 py-2 text-white transition-colors bg-purple-500 rounded hover:bg-purple-600">
1408
+ Try Tailwind Demo
1409
+ </button>
1410
+ </div>
1411
+
1412
+ {/* API Demo */}
1413
+ <div class="p-6 transition-shadow bg-white rounded-lg shadow-lg hover:shadow-xl">
1414
+ <div class="text-center">
1415
+ <div class="flex items-center justify-center w-12 h-12 mx-auto mb-4 text-white bg-green-500 rounded-md">
1416
+ <svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
1417
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 2 9 9 0 0118 0z" />
1418
+ </svg>
1419
+ </div>
1420
+ <h3 class="mb-2 text-lg font-medium text-gray-900">API Integration</h3>
1421
+ <p class="mb-4 text-sm text-gray-500">RESTful API endpoints</p>
1422
+ <button class="w-full px-4 py-2 text-white transition-colors bg-green-500 rounded hover:bg-green-600">
1423
+ Try API Demo
1424
+ </button>
1425
+ </div>
1426
+ </div>
1427
+ </div>
1428
+ </div>
1429
+ </div>
1430
+
1431
+ {/* Stats Section */}
1432
+ <div class="bg-blue-600">
1433
+ <div class="px-4 py-12 mx-auto max-w-7xl sm:px-6 lg:px-8">
1434
+ <div class="grid grid-cols-2 gap-8 lg:grid-cols-4">
1435
+ <div class="text-center">
1436
+ <div class="text-3xl font-extrabold text-white">10x</div>
1437
+ <div class="mt-2 text-lg text-blue-200">Faster Development</div>
1438
+ </div>
1439
+ <div class="text-center">
1440
+ <div class="text-3xl font-extrabold text-white">100%</div>
1441
+ <div class="mt-2 text-lg text-blue-200">Python Compatible</div>
1442
+ </div>
1443
+ <div class="text-center">
1444
+ <div class="text-3xl font-extrabold text-white">JSX</div>
1445
+ <div class="mt-2 text-lg text-blue-200">Modern Syntax</div>
1446
+ </div>
1447
+ <div class="text-center">
1448
+ <div class="text-3xl font-extrabold text-white">∞</div>
1449
+ <div class="mt-2 text-lg text-blue-200">Infinite Possibilities</div>
1450
+ </div>
1451
+ </div>
1452
+ </div>
1453
+ </div>
1454
+
1455
+ {/* Footer */}
1456
+ <footer class="bg-gray-900">
1457
+ <div class="px-4 py-8 mx-auto max-w-7xl sm:px-6 lg:px-8">
1458
+ <div class="flex flex-col items-center space-y-4">
1459
+ <p class="text-base text-center text-gray-400">
1460
+ Built with ❤️ using NextPy Framework
1461
+ </p>
1462
+ <div class="flex space-x-6">
1463
+ <a href="/" class="text-gray-400 hover:text-gray-300">Home</a>
1464
+ <a href="/features" class="text-gray-400 hover:text-gray-300">Features</a>
1465
+ <a href="/docs" class="text-gray-400 hover:text-gray-300">Documentation</a>
1466
+ <a href="https://github.com/nextpy/nextpy" class="text-gray-400 hover:text-gray-300">GitHub</a>
1467
+ </div>
1468
+ </div>
1469
+ </div>
1470
+ </footer>
1471
+ </div>
1472
+ )
1473
+
1474
+ def getServerSideProps(context):
1475
+ return {
1476
+ "props": {
1477
+ "title": "About NextPy",
1478
+ "description": "The Python web framework that brings React-like development to Python"
1479
+ }
1480
+ }
1481
+
1482
+ default = About
1483
+ ''')
1484
+ click.echo(" Created: pages/about.py (enhanced interactive page)")
1485
+
1486
+ # Create interactive demo pages
1487
+ (project_dir / "pages" / "interactive.py").write_text('''"""Interactive Demo Page"""
1488
+
1489
+ def InteractiveDemo(props=None):
1490
+ """Interactive demo showcasing NextPy capabilities"""
1491
+ return (
1492
+ <div class="min-h-screen py-12 bg-gradient-to-br from-indigo-50 to-purple-100">
1493
+ <div class="px-4 mx-auto max-w-7xl sm:px-6 lg:px-8">
1494
+ <h1 class="mb-12 text-4xl font-extrabold text-center text-gray-900">
1495
+ Interactive NextPy Demos
1496
+ </h1>
1497
+
1498
+ <div class="grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-3">
1499
+ {/* Counter Demo */}
1500
+ <div class="p-6 bg-white shadow-lg rounded-xl">
1501
+ <h2 class="mb-4 text-2xl font-bold text-gray-900">Live Counter</h2>
1502
+ <div class="text-center">
1503
+ <div class="mb-4 text-6xl font-bold text-blue-600" id="counter">0</div>
1504
+ <div class="space-x-4">
1505
+ <button onclick="updateCounter(-1)" class="px-6 py-3 text-white transition-colors bg-red-500 rounded-lg hover:bg-red-600">
1506
+ -
1507
+ </button>
1508
+ <button onclick="updateCounter(1)" class="px-6 py-3 text-white transition-colors bg-green-500 rounded-lg hover:bg-green-600">
1509
+ +
1510
+ </button>
1511
+ <button onclick="resetCounter()" class="px-6 py-3 text-white transition-colors bg-gray-500 rounded-lg hover:bg-gray-600">
1512
+ Reset
1513
+ </button>
1514
+ </div>
1515
+ </div>
1516
+ </div>
1517
+
1518
+ {/* Todo List Demo */}
1519
+ <div class="p-6 bg-white shadow-lg rounded-xl">
1520
+ <h2 class="mb-4 text-2xl font-bold text-gray-900">Todo List</h2>
1521
+ <div class="space-y-4">
1522
+ <div class="flex space-x-2">
1523
+ <input type="text" id="todoInput" placeholder="Add a new todo..." class="flex-1 px-4 py-2 border border-gray-300 rounded-lg" />
1524
+ <button onclick="addTodo()" class="px-6 py-2 text-white transition-colors bg-blue-500 rounded-lg hover:bg-blue-600">
1525
+ Add
1526
+ </button>
1527
+ </div>
1528
+ <ul id="todoList" class="space-y-2">
1529
+ {/* Todos will be added here dynamically */}
1530
+ </ul>
1531
+ </div>
1532
+ </div>
1533
+
1534
+ {/* Color Picker Demo */}
1535
+ <div class="p-6 bg-white shadow-lg rounded-xl">
1536
+ <h2 class="mb-4 text-2xl font-bold text-gray-900">Color Picker</h2>
1537
+ <div class="space-y-4">
1538
+ <input type="color" id="colorPicker" class="w-full h-20 rounded-lg cursor-pointer" />
1539
+ <div id="colorDisplay" class="p-4 font-mono text-lg text-center bg-gray-100 rounded-lg">
1540
+ Selected: #3B82F6
1541
+ </div>
1542
+ </div>
1543
+ </div>
1544
+
1545
+ {/* Form Validation Demo */}
1546
+ <div class="p-6 bg-white shadow-lg rounded-xl">
1547
+ <h2 class="mb-4 text-2xl font-bold text-gray-900">Form Validation</h2>
1548
+ <form onsubmit="validateForm(event)" class="space-y-4">
1549
+ <div>
1550
+ <label class="block mb-2 text-sm font-medium text-gray-700">Email</label>
1551
+ <input type="email" id="email" required class="w-full px-4 py-2 border border-gray-300 rounded-lg" placeholder="you@example.com" />
1552
+ </div>
1553
+ <div>
1554
+ <label class="block mb-2 text-sm font-medium text-gray-700">Password</label>
1555
+ <input type="password" id="password" required minlength="6" class="w-full px-4 py-2 border border-gray-300 rounded-lg" placeholder="•••••••••" />
1556
+ </div>
1557
+ <button type="submit" class="w-full px-6 py-3 text-white transition-colors bg-blue-500 rounded-lg hover:bg-blue-600">
1558
+ Validate & Submit
1559
+ </button>
1560
+ </form>
1561
+ <div id="validationResult" class="hidden p-4 mt-4 rounded-lg">
1562
+ {/* Validation results will appear here */}
1563
+ </div>
1564
+ </div>
1565
+ </div>
1566
+ </div>
1567
+ </div>
1568
+ )
1569
+
1570
+ def getServerSideProps(context):
1571
+ return {"props": {}}
1572
+
1573
+ default = InteractiveDemo
1574
+ ''')
1575
+ click.echo(" Created: pages/interactive.py (interactive demos)")
1576
+
1577
+ # Create features page
1578
+ (project_dir / "pages" / "features.py").write_text('''"""Features Page"""
1579
+
1580
+ def Features(props=None):
1581
+ """Comprehensive features showcase"""
1582
+ return (
1583
+ <div class="min-h-screen bg-gray-50">
1584
+ <div class="px-4 py-16 mx-auto max-w-7xl sm:px-6 lg:px-8">
1585
+ <div class="mb-16 text-center">
1586
+ <h1 class="text-4xl font-extrabold text-gray-900">
1587
+ NextPy Features
1588
+ </h1>
1589
+ <p class="mt-4 text-xl text-gray-600">
1590
+ Everything you need to build modern web applications
1591
+ </p>
1592
+ </div>
1593
+
1594
+ <div class="grid grid-cols-1 gap-12 md:grid-cols-2">
1595
+ <div class="space-y-12">
1596
+ {/* Core Features */}
1597
+ <div>
1598
+ <h2 class="mb-6 text-2xl font-bold text-gray-900">Core Features</h2>
1599
+ <div class="space-y-6">
1600
+ <div class="flex items-start space-x-4">
1601
+ <div class="flex-shrink-0 w-6 h-6 text-green-500">
1602
+ <svg fill="none" viewBox="0 0 24 24" stroke="currentColor">
1603
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
1604
+ </svg>
1605
+ </div>
1606
+ <div>
1607
+ <h3 class="text-lg font-medium text-gray-900">True JSX Components</h3>
1608
+ <p class="mt-2 text-gray-600">Write React-like components with JSX syntax directly in Python</p>
1609
+ </div>
1610
+ </div>
1611
+
1612
+ <div class="flex items-start space-x-4">
1613
+ <div class="flex-shrink-0 w-6 h-6 text-blue-500">
1614
+ <svg fill="none" viewBox="0 0 24 24" stroke="currentColor">
1615
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16m-7 6h7" />
1616
+ </svg>
1617
+ </div>
1618
+ <div>
1619
+ <h3 class="text-lg font-medium text-gray-900">File-based Routing</h3>
1620
+ <p class="mt-2 text-gray-600">Automatic route discovery with dynamic routes support</p>
1621
+ </div>
1622
+ </div>
1623
+
1624
+ <div class="flex items-start space-x-4">
1625
+ <div class="flex-shrink-0 w-6 h-6 text-purple-500">
1626
+ <svg fill="none" viewBox="0 0 24 24" stroke="currentColor">
1627
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 2 9 9 0 0118 0z" />
1628
+ </svg>
1629
+ </div>
1630
+ <div>
1631
+ <h3 class="text-lg font-medium text-gray-900">Tailwind CSS Integration</h3>
1632
+ <p class="mt-2 text-gray-600">Built-in Tailwind CSS v4 with PostCSS compilation</p>
1633
+ </div>
1634
+ </div>
1635
+ </div>
1636
+ </div>
1637
+
1638
+ {/* Development Features */}
1639
+ <div>
1640
+ <h2 class="mb-6 text-2xl font-bold text-gray-900">Development Experience</h2>
1641
+ <div class="space-y-6">
1642
+ <div class="flex items-start space-x-4">
1643
+ <div class="flex-shrink-0 w-6 h-6 text-red-500">
1644
+ <svg fill="none" viewBox="0 0 24 24" stroke="currentColor">
1645
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v16h16V4H4z" />
1646
+ </svg>
1647
+ </div>
1648
+ <div>
1649
+ <h3 class="text-lg font-medium text-gray-900">Hot Reload</h3>
1650
+ <p class="mt-2 text-gray-600">Instant hot reload when saving files with Watchdog</p>
1651
+ </div>
1652
+ </div>
1653
+
1654
+ <div class="flex items-start space-x-4">
1655
+ <div class="flex-shrink-0 w-6 h-6 text-yellow-500">
1656
+ <svg fill="none" viewBox="0 0 24 24" stroke="currentColor">
1657
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.75 5H6.25v13l4.5 4.5z" />
1658
+ </svg>
1659
+ </div>
1660
+ <div>
1661
+ <h3 class="text-lg font-medium text-gray-900">Debug Mode</h3>
1662
+ <p class="mt-2 text-gray-600">Comprehensive debugging with detailed error pages</p>
1663
+ </div>
1664
+ </div>
1665
+
1666
+ <div class="flex items-start space-x-4">
1667
+ <div class="flex-shrink-0 w-6 h-6 text-indigo-500">
1668
+ <svg fill="none" viewBox="0 0 24 24" stroke="currentColor">
1669
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
1670
+ </svg>
1671
+ </div>
1672
+ <div>
1673
+ <h3 class="text-lg font-medium text-gray-900">VS Code Integration</h3>
1674
+ <p class="mt-2 text-gray-600">Full VS Code support with extensions and IntelliSense</p>
1675
+ </div>
1676
+ </div>
1677
+ </div>
1678
+ </div>
1679
+ </div>
1680
+ </div>
1681
+ </div>
1682
+ )
1683
+
1684
+ def getServerSideProps(context):
1685
+ return {"props": {}}
1686
+
1687
+ default = Features
1688
+ ''')
1689
+ click.echo(" Created: pages/features.py (features showcase)")
1690
+
1691
+ # Create getting started guide
1692
+ (project_dir / "pages" / "getting-started.py").write_text('''"""Getting Started Guide"""
1693
+
1694
+ def GettingStarted(props=None):
1695
+ """Comprehensive getting started guide"""
1696
+ return (
1697
+ <div class="min-h-screen bg-white">
1698
+ <div class="max-w-4xl px-4 py-16 mx-auto sm:px-6 lg:px-8">
1699
+ <div class="mb-16 text-center">
1700
+ <h1 class="text-4xl font-extrabold text-gray-900">
1701
+ Getting Started with NextPy
1702
+ </h1>
1703
+ <p class="mt-4 text-xl text-gray-600">
1704
+ Your journey to building amazing web apps starts here
1705
+ </p>
1706
+ </div>
1707
+
1708
+ <div class="space-y-16">
1709
+ {/* Step 1 */}
1710
+ <div class="p-8 rounded-lg bg-blue-50">
1711
+ <div class="flex items-center mb-4">
1712
+ <div class="flex items-center justify-center flex-shrink-0 w-8 h-8 font-bold text-white bg-blue-500 rounded-full">
1713
+ 1
1714
+ </div>
1715
+ <h2 class="ml-4 text-2xl font-bold text-gray-900">Installation</h2>
1716
+ </div>
1717
+ <div class="ml-12 space-y-4">
1718
+ <div class="p-4 bg-white border-l-4 border-blue-500 rounded">
1719
+ <h3 class="mb-2 font-semibold text-gray-900">Install NextPy</h3>
1720
+ <code class="block p-2 text-sm bg-gray-100 rounded">pip install nextpy-framework</code>
1721
+ </div>
1722
+ <div class="p-4 bg-white border-l-4 border-blue-500 rounded">
1723
+ <h3 class="mb-2 font-semibold text-gray-900">Create New Project</h3>
1724
+ <code class="block p-2 text-sm bg-gray-100 rounded">nextpy create my-app</code>
1725
+ </div>
1726
+ </div>
1727
+ </div>
1728
+
1729
+ {/* Step 2 */}
1730
+ <div class="p-8 rounded-lg bg-green-50">
1731
+ <div class="flex items-center mb-4">
1732
+ <div class="flex items-center justify-center flex-shrink-0 w-8 h-8 font-bold text-white bg-green-500 rounded-full">
1733
+ 2
1734
+ </div>
1735
+ <h2 class="ml-4 text-2xl font-bold text-gray-900">Project Structure</h2>
1736
+ </div>
1737
+ <div class="ml-12">
1738
+ <div class="p-4 bg-white border-l-4 border-green-500 rounded">
1739
+ <h3 class="mb-2 font-semibold text-gray-900">Navigate to Your Project</h3>
1740
+ <code class="block p-2 text-sm bg-gray-100 rounded">cd my-app</code>
1741
+ </div>
1742
+ <div class="p-4 mt-4 bg-white border-l-4 border-green-500 rounded">
1743
+ <h3 class="mb-2 font-semibold text-gray-900">Project Structure</h3>
1744
+ <pre class="p-4 overflow-x-auto text-sm bg-gray-100 rounded">
1745
+ {`my-app/
1746
+ ├── pages/ # Your pages and API routes
1747
+ ├── components/ # Reusable components
1748
+ ├── templates/ # HTML templates
1749
+ ├── public/ # Static assets
1750
+ ├── styles.css # Tailwind CSS
1751
+ ├── main.py # Application entry point
1752
+ └── requirements.txt # Python dependencies`}</pre>
1753
+ </div>
1754
+ </div>
1755
+ </div>
1756
+
1757
+ {/* Step 3 */}
1758
+ <div class="p-8 rounded-lg bg-purple-50">
1759
+ <div class="flex items-center mb-4">
1760
+ <div class="flex items-center justify-center flex-shrink-0 w-8 h-8 font-bold text-white bg-purple-500 rounded-full">
1761
+ 3
1762
+ </div>
1763
+ <h2 class="ml-4 text-2xl font-bold text-gray-900">Development</h2>
1764
+ </div>
1765
+ <div class="ml-12 space-y-4">
1766
+ <div class="p-4 bg-white border-l-4 border-purple-500 rounded">
1767
+ <h3 class="mb-2 font-semibold text-gray-900">Start Development Server</h3>
1768
+ <code class="block p-2 text-sm bg-gray-100 rounded">nextpy dev</code>
1769
+ </div>
1770
+ <div class="p-4 bg-white border-l-4 border-purple-500 rounded">
1771
+ <h3 class="mb-2 font-semibold text-gray-900">Open Your Browser</h3>
1772
+ <code class="block p-2 text-sm bg-gray-100 rounded">http://localhost:8000</code>
1773
+ </div>
1774
+ </div>
1775
+ </div>
1776
+
1777
+ {/* Step 4 */}
1778
+ <div class="p-8 rounded-lg bg-yellow-50">
1779
+ <div class="flex items-center mb-4">
1780
+ <div class="flex items-center justify-center flex-shrink-0 w-8 h-8 font-bold text-white bg-yellow-500 rounded-full">
1781
+ 4
1782
+ </div>
1783
+ <h2 class="ml-4 text-2xl font-bold text-gray-900">Build Your First Component</h2>
1784
+ </div>
1785
+ <div class="ml-12">
1786
+ <div class="p-4 bg-white border-l-4 border-yellow-500 rounded">
1787
+ <h3 class="mb-2 font-semibold text-gray-900">Create a Component</h3>
1788
+ <p class="mb-2 text-gray-600">Edit pages/index.py to create your first JSX component:</p>
1789
+ <pre class="p-4 overflow-x-auto text-sm text-green-400 bg-gray-900 rounded">
1790
+ {`def Home(props=None):
1791
+ return (
1792
+ <div class="flex items-center justify-center min-h-screen bg-blue-500">
1793
+ <h1 class="text-3xl font-bold text-white">
1794
+ Hello, NextPy!
1795
+ </h1>
1796
+ </div>
1797
+ )
1798
+
1799
+ default = Home`}</pre>
1800
+ </div>
1801
+ </div>
1802
+ </div>
1803
+ </div>
1804
+ </div>
1805
+ )
1806
+
1807
+ def getServerSideProps(context):
1808
+ return {"props": {}}
1809
+
1810
+ default = GettingStarted
1811
+ ''')
1812
+ click.echo(" Created: pages/getting-started.py (comprehensive guide)")
1813
+ (project_dir / "components" / "ui" / "Button.py").write_text('''"""Button component"""
1814
+
1815
+ def Button(props = None):
1816
+ """Reusable Button component"""
1817
+ props = props or {}
1818
+
1819
+ variant = props.get("variant", "default")
1820
+ children = props.get("children", "Button")
1821
+ className = props.get("className", "")
1822
+
1823
+ if variant == "primary":
1824
+ variant_class = "bg-blue-600 text-white hover:bg-blue-700 transform hover:scale-105 transition-all duration-200"
1825
+ elif variant == "secondary":
1826
+ variant_class = "bg-gray-200 text-gray-900 hover:bg-gray-300 transform hover:scale-105 transition-all duration-200"
1827
+ elif variant == "success":
1828
+ variant_class = "bg-green-600 text-white hover:bg-green-700 transform hover:scale-105 transition-all duration-200"
1829
+ elif variant == "danger":
1830
+ variant_class = "bg-red-600 text-white hover:bg-red-700 transform hover:scale-105 transition-all duration-200"
1831
+ else:
1832
+ variant_class = "bg-gray-600 text-white hover:bg-gray-700 transform hover:scale-105 transition-all duration-200"
1833
+
1834
+ class_attr = f"px-6 py-3 rounded-lg font-medium transition-all duration-200 transform hover:scale-105 {variant_class} {className}"
1835
+
1836
+ return (
1837
+ <button class={class_attr}
1838
+ id={props.get("id")}
1839
+ disabled={props.get("disabled", False)}
1840
+ onclick={props.get("onClick", "")}>
1841
+ {children}
1842
+ </button>
1843
+ )
1844
+
1845
+ default = Button
1846
+ ''')
1847
+ click.echo(" Created: components/ui/Button.py (enhanced interactive)")
1848
+
1849
+
1850
+ # Create a Layout component
1851
+ (project_dir / "components" / "layout" / "Layout.py").write_text('''"""Layout component"""
1852
+
1853
+ def Layout(props = None):
1854
+ """Layout component wrapper"""
1855
+ props = props or {}
1856
+
1857
+ title = props.get("title", "NextPy App")
1858
+ children = props.get("children", "")
1859
+
1860
+ return (
1861
+ <div class="flex flex-col min-h-screen">
1862
+ <header class="bg-white shadow-sm">
1863
+ <div class="px-4 py-4 mx-auto max-w-7xl">
1864
+ <div class="flex items-center justify-between">
1865
+ <h1 class="text-2xl font-bold text-gray-900">{title}</h1>
1866
+ <nav class="flex space-x-4">
1867
+ <a href="/" class="text-gray-600 hover:text-gray-900">Home</a>
1868
+ <a href="/about" class="text-gray-600 hover:text-gray-900">About</a>
1869
+ </nav>
1870
+ </div>
1871
+ </div>
1872
+ </header>
1873
+ <main class="flex-1">
1874
+ {children}
1875
+ </main>
1876
+ <footer class="mt-auto bg-gray-100">
1877
+ <div class="px-4 py-6 mx-auto text-center text-gray-600 max-w-7xl">
1878
+ <p>&copy; 2025 NextPy Framework. All rights reserved.</p>
1879
+ </div>
1880
+ </footer>
1881
+ </div>
1882
+ )
1883
+
1884
+ default = Layout
1885
+ ''')
1886
+ click.echo(" Created: components/layout/Layout.py")
1887
+
1888
+ # Create VS Code configuration for JSX support
1889
+ (project_dir / ".vscode").mkdir(exist_ok=True)
1890
+ (project_dir / ".vscode" / "settings.json").write_text('''{
1891
+ "files.associations": {
1892
+ "*.py": "python",
1893
+ "*.py.jsx": "python",
1894
+ "*.jsx": "javascriptreact"
1895
+ },
1896
+ "emmet.includeLanguages": {
1897
+ "python": "html",
1898
+ "javascriptreact": "html",
1899
+ "typescriptreact": "html"
1900
+ },
1901
+ "emmet.triggerExpansionOnTab": true,
1902
+ "typescript.preferences.includePackageJsonAutoImports": "on",
1903
+ "editor.quickSuggestions": {
1904
+ "strings": true
1905
+ },
1906
+ "editor.suggestSelection": "first",
1907
+ "editor.wordBasedSuggestions": true,
1908
+ "editor.snippetSuggestions": "top",
1909
+ "editor.parameterHints": {
1910
+ "enabled": true
1911
+ },
1912
+ "editor.snippetSuggestions": "top",
1913
+ "html.autoClosingTags": true,
1914
+ "css.autoClosingTags": true,
1915
+ "javascript.autoClosingTags": true,
1916
+ "typescript.autoClosingTags": true,
1917
+ "editor.autoClosingBrackets": "always",
1918
+ "editor.autoClosingQuotes": "always",
1919
+ "editor.formatOnSave": true,
1920
+ "editor.codeActionsOnSave": {
1921
+ "source.fixAll.eslint": true,
1922
+ "source.organizeImports": true
1923
+ },
1924
+ "emmet.preferences": {
1925
+ "css.property.endWithSemicolon": true,
1926
+ "css.value.unit": "rem"
1927
+ },
1928
+ "files.exclude": {
1929
+ "**/__pycache__": true,
1930
+ "**/*.pyc": true,
1931
+ "**/node_modules": true,
1932
+ "**/out": true,
1933
+ "**/.next": true,
1934
+ "**/.pytest_cache": true,
1935
+ "**/.mypy_cache": true
1936
+ },
1937
+ "search.exclude": {
1938
+ "**/node_modules": true,
1939
+ "**/out": true,
1940
+ "**/.next": true,
1941
+ "**/__pycache__": true,
1942
+ "**/.pytest_cache": true,
1943
+ "**/.mypy_cache": true
1944
+ },
1945
+ "python.linting.enabled": true,
1946
+ "python.linting.pylintEnabled": false,
1947
+ "python.linting.flake8Enabled": false,
1948
+ "python.linting.pylintArgs": [
1949
+ "--disable=C0114,C0115,C0116,E1132,E1131,E1130"
1950
+ ],
1951
+ "python.formatting.provider": "black",
1952
+ "[python]": {
1953
+ "editor.defaultFormatter": "ms-python.black-formatter",
1954
+ "editor.formatOnSave": true,
1955
+ "editor.rulers": [88],
1956
+ "editor.tabSize": 4,
1957
+ "editor.insertSpaces": true
1958
+ }
1959
+ }''')
1960
+ click.echo(" Created: .vscode/settings.json")
1961
+
1962
+ (project_dir / ".vscode" / "extensions.json").write_text('''{
1963
+ "recommendations": [
1964
+ "ms-python.python",
1965
+ "ms-python.vscode-pylance",
1966
+ "bradlc.vscode-tailwindcss",
1967
+ "esbenp.prettier-vscode",
1968
+ "ms-vscode.vscode-json",
1969
+ "formulahendry.auto-rename-tag",
1970
+ "christian-kohler.path-intellisense",
1971
+ "ms-vscode.vscode-html-css-class-completion",
1972
+ "ms-vscode.vscode-emmet",
1973
+ "ms-vscode.vscode-eslint",
1974
+ "dbaeumer.vscode-eslint",
1975
+ "ms-vscode.vscode-typescript-next",
1976
+ "ritwickdey.liveserver",
1977
+ "ms-vscode.vscode-jest",
1978
+ "esbenp.prettier-vscode",
1979
+ "streetsidesoftware.code-spell-checker",
1980
+ "gruntfuggly.todo-tree",
1981
+ "ms-vscode.vscode-git-graph",
1982
+ "eamodio.gitlens",
1983
+ "ms-vscode.vscode-docker",
1984
+ "ms-vscode.remote-explorer",
1985
+ "ms-vscode-remote.remote-containers",
1986
+ "ms-vscode.vscode-remote-wsl",
1987
+ "redhat.vscode-yaml",
1988
+ "ms-vscode.vscode-markdown",
1989
+ "yzhang.markdown-all-in-one",
1990
+ "shd101wyy.markdown-preview-enhanced",
1991
+ "ms-vscode.vscode-python",
1992
+ "kevinrose.vsc-python-indent",
1993
+ "ms-python.black-formatter",
1994
+ "ms-python.isort",
1995
+ "ms-python.flake8",
1996
+ "ms-python.mypy-type-checker"
1997
+ ]
1998
+ }''')
1999
+ click.echo(" Created: .vscode/extensions.json")
2000
+
2001
+ # Create comprehensive API examples
2002
+ (project_dir / "pages" / "api" / "hello.py").write_text('''"""API example - Hello endpoint"""
2003
+
2004
+ from fastapi import Request
2005
+
2006
+ async def get(request: Request):
2007
+ """GET /api/hello"""
2008
+ return {"message": "Hello from NextPy API!", "status": "success"}
2009
+
2010
+ async def post(request: Request):
2011
+ """POST /api/hello"""
2012
+ data = await request.json()
2013
+ return {"message": "POST request received", "data": data, "status": "success"}
2014
+ ''')
2015
+ click.echo(" Created: pages/api/hello.py")
2016
+
2017
+ (project_dir / "pages" / "api" / "users" / "index.py").write_text('''"""API example - Users index"""
2018
+
2019
+ from fastapi import Request
2020
+
2021
+ async def get(request: Request):
2022
+ """GET /api/users - List all users"""
2023
+ users = [
2024
+ {"id": 1, "name": "John Doe", "email": "john@example.com"},
2025
+ {"id": 2, "name": "Jane Smith", "email": "jane@example.com"},
2026
+ ]
2027
+ return {"users": users, "total": len(users)}
2028
+
2029
+ async def post(request: Request):
2030
+ """POST /api/users - Create new user"""
2031
+ data = await request.json()
2032
+ # In a real app, you'd save to database
2033
+ new_user = {
2034
+ "id": 3,
2035
+ "name": data.get("name"),
2036
+ "email": data.get("email")
2037
+ }
2038
+ return {"user": new_user, "message": "User created successfully"}
2039
+ ''')
2040
+ click.echo(" Created: pages/api/users/index.py")
2041
+
2042
+ (project_dir / "pages" / "api" / "users" / "[id].py").write_text('''"""API example - Dynamic user route"""
2043
+
2044
+ from fastapi import Request
2045
+
2046
+ async def get(request: Request, id: int):
2047
+ """GET /api/users/{id} - Get user by ID"""
2048
+ users = {
2049
+ 1: {"id": 1, "name": "John Doe", "email": "john@example.com"},
2050
+ 2: {"id": 2, "name": "Jane Smith", "email": "jane@example.com"},
2051
+ }
2052
+
2053
+ if id in users:
2054
+ return {"user": users[id]}
2055
+ else:
2056
+ return {"error": "User not found"}, 404
2057
+
2058
+ async def put(request: Request, id: int):
2059
+ """PUT /api/users/{id} - Update user"""
2060
+ data = await request.json()
2061
+ return {"message": f"User {id} updated", "data": data}
2062
+
2063
+ async def delete(request: Request, id: int):
2064
+ """DELETE /api/users/{id} - Delete user"""
2065
+ return {"message": f"User {id} deleted successfully"}
2066
+ ''')
2067
+ click.echo(" Created: pages/api/users/[id].py")
2068
+
2069
+ # Create database models
2070
+ (project_dir / "models" / "User.py").write_text('''"""User model example"""
2071
+
2072
+ from sqlalchemy import Column, Integer, String, DateTime, Boolean
2073
+ from sqlalchemy.ext.declarative import declarative_base
2074
+ from datetime import datetime
2075
+
2076
+ Base = declarative_base()
2077
+
2078
+ class User(Base):
2079
+ __tablename__ = "users"
2080
+
2081
+ id = Column(Integer, primary_key=True, index=True)
2082
+ name = Column(String(100), nullable=False)
2083
+ email = Column(String(100), unique=True, nullable=False)
2084
+ created_at = Column(DateTime, default=datetime.utcnow)
2085
+ is_active = Column(Boolean, default=True)
2086
+
2087
+ def __repr__(self):
2088
+ return f"<User(id={self.id}, name='{self.name}', email='{self.email}')>"
2089
+ ''')
2090
+ click.echo(" Created: models/User.py")
2091
+
2092
+ # Create utility functions
2093
+ (project_dir / "utils" / "helpers.py").write_text('''"""Utility helper functions"""
2094
+
2095
+ import hashlib
2096
+ import secrets
2097
+ from datetime import datetime
2098
+
2099
+ def generate_secret_key(length: int = 32) -> str:
2100
+ """Generate a secure secret key"""
2101
+ return secrets.token_urlsafe(length)
2102
+
2103
+ def hash_password(password: str) -> str:
2104
+ """Hash a password using SHA-256"""
2105
+ return hashlib.sha256(password.encode()).hexdigest()
2106
+
2107
+ def format_date(date: datetime) -> str:
2108
+ """Format datetime for display"""
2109
+ return date.strftime("%B %d, %Y at %I:%M %p")
2110
+
2111
+ def slugify(text: str) -> str:
2112
+ """Convert text to URL-friendly slug"""
2113
+ return text.lower().replace(" ", "-").replace("_", "-")
2114
+ ''')
2115
+ click.echo(" Created: utils/helpers.py")
2116
+
2117
+ # Create custom hooks
2118
+ (project_dir / "hooks" / "use_auth.py").write_text('''"""Authentication hook example"""
2119
+
2120
+ def use_auth(request):
2121
+ """Example authentication hook"""
2122
+ # In a real app, you'd check tokens, sessions, etc.
2123
+ auth_header = request.headers.get("authorization")
2124
+
2125
+ if auth_header and auth_header.startswith("Bearer "):
2126
+ token = auth_header.split(" ")[1]
2127
+ # Validate token here
2128
+ return {"user": {"id": 1, "name": "Authenticated User"}, "token": token}
2129
+
2130
+ return {"user": None, "error": "No authentication provided"}
2131
+ ''')
2132
+ click.echo(" Created: hooks/use_auth.py")
2133
+
2134
+ # Create middleware example
2135
+ (project_dir / "middleware" / "cors.py").write_text('''"""CORS middleware example"""
2136
+
2137
+ from fastapi import Request, Response
2138
+ from fastapi.middleware.cors import CORSMiddleware
2139
+
2140
+ def add_cors_middleware(app):
2141
+ """Add CORS middleware to the app"""
2142
+ app.add_middleware(
2143
+ CORSMiddleware,
2144
+ allow_origins=["*"], # Configure appropriately for production
2145
+ allow_credentials=True,
2146
+ allow_methods=["*"],
2147
+ allow_headers=["*"],
2148
+ )
2149
+ return app
2150
+ ''')
2151
+ click.echo(" Created: middleware/cors.py")
2152
+
2153
+ # Create test files
2154
+ (project_dir / "tests" / "test_api.py").write_text('''"""API tests example"""
2155
+
2156
+ import pytest
2157
+ from fastapi.testclient import TestClient
2158
+ from main import app
2159
+
2160
+ client = TestClient(app)
2161
+
2162
+ def test_hello_api():
2163
+ """Test the hello API endpoint"""
2164
+ response = client.get("/api/hello")
2165
+ assert response.status_code == 200
2166
+ data = response.json()
2167
+ assert data["message"] == "Hello from NextPy API!"
2168
+ assert data["status"] == "success"
2169
+
2170
+ def test_users_api():
2171
+ """Test the users API endpoint"""
2172
+ response = client.get("/api/users")
2173
+ assert response.status_code == 200
2174
+ data = response.json()
2175
+ assert "users" in data
2176
+ assert "total" in data
2177
+ assert len(data["users"]) == data["total"]
2178
+ ''')
2179
+ click.echo(" Created: tests/test_api.py")
2180
+
2181
+ # Create documentation
2182
+ (project_dir / "docs" / "README.md").write_text('''# Project Documentation
2183
+
2184
+ ## Overview
2185
+ This is a NextPy application with True JSX, Tailwind CSS, and comprehensive API support.
2186
+
2187
+ ## Features
2188
+ - ✅ True JSX components in Python
2189
+ - ✅ Tailwind CSS integration
2190
+ - ✅ File-based routing
2191
+ - ✅ API routes with FastAPI
2192
+ - ✅ Database models with SQLAlchemy
2193
+ - ✅ Authentication hooks
2194
+ - ✅ CORS middleware
2195
+ - ✅ Comprehensive testing
2196
+
2197
+ ## Project Structure
2198
+ ```
2199
+ ├── pages/ # File-based routing
2200
+ │ ├── api/ # API routes
2201
+ │ └── *.py # Page components
2202
+ ├── components/ # Reusable components
2203
+ ├── templates/ # HTML templates
2204
+ ├── models/ # Database models
2205
+ ├── utils/ # Utility functions
2206
+ ├── hooks/ # Custom hooks
2207
+ ├── middleware/ # Custom middleware
2208
+ ├── tests/ # Test files
2209
+ ├── public/ # Static assets
2210
+ ├── styles/ # CSS files
2211
+ └── docs/ # Documentation
2212
+ ```
2213
+
2214
+ ## Getting Started
2215
+ 1. Install dependencies: `pip install -r requirements.txt`
2216
+ 2. Install Node.js deps: `npm install`
2217
+ 3. Run development server: `nextpy dev`
2218
+ 4. Open http://localhost:8000
2219
+
2220
+ ## API Endpoints
2221
+ - `GET /api/hello` - Hello message
2222
+ - `GET /api/users` - List users
2223
+ - `POST /api/users` - Create user
2224
+ - `GET /api/users/{id}` - Get user by ID
2225
+ - `PUT /api/users/{id}` - Update user
2226
+ - `DELETE /api/users/{id}` - Delete user
2227
+ ''')
2228
+ click.echo(" Created: docs/README.md")
2229
+
2230
+ (project_dir / "requirements.txt").write_text('''fastapi>=0.100.0
2231
+ uvicorn>=0.23.0
2232
+ jinja2>=3.1.0
2233
+ pydantic>=2.0.0
2234
+ pydantic-settings>=2.0.0
2235
+ click>=8.1.0
2236
+ watchdog>=3.0.0
2237
+ python-multipart>=0.0.6
2238
+ pillow>=10.0.0
2239
+ aiofiles>=23.0.0
2240
+ httpx>=0.24.0
2241
+ sqlalchemy>=2.0.0
2242
+ python-dotenv>=1.0.0
2243
+ pyjwt>=2.8.0
2244
+ markdown>=3.0.0 # Added markdown for documentation rendering
2245
+ ''')
2246
+ click.echo(" Created: requirements.txt")
2247
+
2248
+ # Create main.py with Tailwind compilation (for pip-installed NextPy)
2249
+ (project_dir / "main.py").write_text('''"""NextPy ASGI Application Entry Point"""
2250
+
2251
+ import os
2252
+ import sys
2253
+ import subprocess
2254
+ from pathlib import Path
2255
+
2256
+ print(f"DEBUG: Current working directory: {Path.cwd()}")
2257
+ print(f"DEBUG: sys.path before modification: {sys.path}")
2258
+
2259
+ # Compile Tailwind CSS using PostCSS
2260
+ try:
2261
+ print("Compiling Tailwind CSS...")
2262
+ # Use PostCSS with new Tailwind plugin
2263
+ result = subprocess.run(
2264
+ ["./node_modules/.bin/postcss", "styles.css", "-o", "public/tailwind.css"],
2265
+ capture_output=True,
2266
+ text=True,
2267
+ check=True
2268
+ )
2269
+ print("Tailwind CSS compiled successfully.")
2270
+ if result.stdout:
2271
+ print(f"CSS Output: {result.stdout[:200]}...")
2272
+ except subprocess.CalledProcessError as e:
2273
+ print(f"Error compiling Tailwind CSS: {e}")
2274
+ if e.stderr:
2275
+ print(f"CSS Error: {e.stderr}")
2276
+ except FileNotFoundError:
2277
+ print("Error: PostCSS not found. Make sure Node.js and Tailwind CSS are installed.")
2278
+ print("Install with: npm install postcss-cli @tailwindcss/postcss")
2279
+
2280
+ # Import NextPy modules (works when installed via pip)
2281
+ from nextpy.server.app import create_app
2282
+ from nextpy.db import init_db
2283
+ from nextpy.config import settings
2284
+
2285
+ # Initialize database
2286
+ try:
2287
+ init_db(settings["database_url"])
2288
+ print("Database initialized successfully.")
2289
+ except Exception as e:
2290
+ print(f"Warning: Database initialization failed: {e}")
2291
+
2292
+ # Create NextPy app with file-based routing
2293
+ app = create_app(
2294
+ pages_dir="pages",
2295
+ templates_dir="templates",
2296
+ public_dir="public",
2297
+ out_dir="out",
2298
+ debug=settings["debug"],
2299
+ )
2300
+
2301
+ # Note: Routes are automatically loaded from pages/ directory
2302
+ # - / -> pages/index.py
2303
+ # - /about -> pages/about.py
2304
+ # - /api/* -> pages/api/*.py
2305
+
2306
+ if __name__ == "__main__":
2307
+ import uvicorn
2308
+ uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
2309
+ ''')
2310
+ click.echo(" Created: main.py (pip-compatible)")
2311
+
2312
+ # Create .env file for development
2313
+ (project_dir / ".env").write_text('''# NextPy Development Environment
2314
+ DEVELOPMENT=true
2315
+ DEBUG=true
2316
+ NEXTPY_DEBUG=true
2317
+
2318
+ # Server Configuration
2319
+ HOST=0.0.0.0
2320
+ PORT=8000
2321
+
2322
+ # Database (if needed)
2323
+ DATABASE_URL=sqlite:///./app.db
2324
+
2325
+ # Secret Key
2326
+ SECRET_KEY=your-secret-key-here
2327
+
2328
+ # NextPy Settings
2329
+ NEXTPY_DEBUG_ICON=true
2330
+ NEXTPY_HOT_RELOAD=true
2331
+ NEXTPY_LOG_LEVEL=info
2332
+ ''')
2333
+ click.echo(" Created: .env")
2334
+
2335
+ # Install Node.js dependencies
2336
+ try:
2337
+ import subprocess
2338
+ import sys
2339
+
2340
+ click.echo(click.style(" 📦 Installing Node.js dependencies...", fg="blue"))
2341
+ result = subprocess.run(
2342
+ ["npm", "install"],
2343
+ cwd=project_dir,
2344
+ capture_output=True,
2345
+ text=True
2346
+ )
2347
+
2348
+ if result.returncode == 0:
2349
+ click.echo(click.style(" ✅ Node.js dependencies installed", fg="green"))
2350
+ else:
2351
+ click.echo(click.style(" ⚠️ npm install failed", fg="yellow"))
2352
+ click.echo(" 💡 Run manually: npm install")
2353
+
2354
+ except FileNotFoundError:
2355
+ click.echo(click.style(" ⚠️ npm not found", fg="yellow"))
2356
+ click.echo(" 💡 Install Node.js: https://nodejs.org/")
2357
+ except Exception as e:
2358
+ click.echo(click.style(f" ⚠️ Could not install Node.js deps: {e}", fg="yellow"))
2359
+
2360
+ # Install Python dependencies
2361
+ try:
2362
+ click.echo(click.style(" 🐍 Installing Python dependencies...", fg="blue"))
2363
+ result = subprocess.run(
2364
+ [sys.executable, "-m", "pip", "install", "-r", "requirements.txt"],
2365
+ cwd=project_dir,
2366
+ capture_output=True,
2367
+ text=True
2368
+ )
2369
+
2370
+ if result.returncode == 0:
2371
+ click.echo(click.style(" ✅ Python dependencies installed", fg="green"))
2372
+ else:
2373
+ click.echo(click.style(" ⚠️ pip install failed", fg="yellow"))
2374
+ click.echo(" 💡 Run manually: pip install -r requirements.txt")
2375
+
2376
+ except Exception as e:
2377
+ click.echo(click.style(f" ⚠️ Could not install Python deps: {e}", fg="yellow"))
2378
+ try:
2379
+ import sys
2380
+ import subprocess
2381
+ from pathlib import Path
2382
+
2383
+ # Check if VS Code is available
2384
+ result = subprocess.run([sys.executable, "-c", "import vscode"],
2385
+ capture_output=True, text=True)
2386
+
2387
+ if result.returncode == 0:
2388
+ extension_id = "nextpy.nextpy-vscode"
2389
+
2390
+ # Check if extension is already installed
2391
+ check_cmd = ["code", "--list-extensions", "--show-versions", extension_id]
2392
+ check_result = subprocess.run(check_cmd, capture_output=True, text=True)
2393
+
2394
+ if extension_id not in check_result.stdout:
2395
+ click.echo(click.style(" 🔌 Installing NextPy VS Code extension...", fg="blue"))
2396
+
2397
+ # Try to install from marketplace
2398
+ install_cmd = ["code", "--install-extension", extension_id]
2399
+ install_result = subprocess.run(install_cmd, capture_output=True, text=True)
2400
+
2401
+ if install_result.returncode == 0:
2402
+ click.echo(click.style(" ✅ NextPy VS Code extension installed!", fg="green"))
2403
+ click.echo(click.style(" 📝 Restart VS Code to activate", fg="yellow"))
2404
+ else:
2405
+ click.echo(click.style(" ⚠️ Extension installation failed", fg="yellow"))
2406
+ click.echo(" 💡 Install manually: code --install-extension nextpy.nextpy-vscode")
2407
+ else:
2408
+ click.echo(click.style(" ✅ NextPy VS Code extension already installed", fg="green"))
2409
+ else:
2410
+ click.echo(click.style(" ⚠️ VS Code not available", fg="yellow"))
2411
+ except Exception as e:
2412
+ click.echo(click.style(f" ⚠️ Could not install VS Code extension: {e}", fg="yellow"))
2413
+
2414
+
2415
+
2416
+
2417
+ if __name__ == "__main__":
2418
+ cli()