create-aziz-app 2.0.6 → 2.0.8

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.
@@ -312,6 +312,9 @@ function Footer() {
312
312
 
313
313
  // Update all pages
314
314
  updatePagesForBootstrap(targetDir);
315
+
316
+ // Update 404 page
317
+ update404ForBootstrap(targetDir);
315
318
  }
316
319
 
317
320
  // Update components for Tailwind
@@ -380,6 +383,9 @@ function Footer() {
380
383
 
381
384
  // Update all pages
382
385
  updatePagesForTailwind(targetDir);
386
+
387
+ // Update 404 page
388
+ update404ForTailwind(targetDir);
383
389
  }
384
390
 
385
391
  // Update all pages for Bootstrap
@@ -688,6 +694,79 @@ function TestPage() {
688
694
  `;
689
695
  }
690
696
 
697
+ // Update 404 page for Bootstrap
698
+ function update404ForBootstrap(targetDir) {
699
+ const routerPath = path.join(targetDir, 'modules', 'router.js');
700
+ if (!fs.existsSync(routerPath)) return;
701
+
702
+ let content = fs.readFileSync(routerPath, 'utf8');
703
+
704
+ const old404 = `// 404 page — shown when no route matches
705
+ function NotFoundPage() {
706
+ return (
707
+ <div class="not-found">
708
+ <h1>404</h1>
709
+ <p>Page not found.</p>
710
+ <button class="btn btn-primary" onClick={() => navigate("home")}>Go Home</button>
711
+ </div>
712
+ );
713
+ }`;
714
+
715
+ const new404 = `// 404 page — shown when no route matches
716
+ function NotFoundPage() {
717
+ return (
718
+ <div class="container text-center py-5">
719
+ <h1 class="display-1 fw-bold text-primary mb-4">404</h1>
720
+ <p class="lead mb-4">Oops! The page you're looking for doesn't exist.</p>
721
+ <button class="btn btn-primary btn-lg" onClick={() => navigate("home")}>
722
+ <i class="bi bi-house-door me-2"></i>Go Home
723
+ </button>
724
+ </div>
725
+ );
726
+ }`;
727
+
728
+ content = content.replace(old404, new404);
729
+ fs.writeFileSync(routerPath, content);
730
+ }
731
+
732
+ // Update 404 page for Tailwind
733
+ function update404ForTailwind(targetDir) {
734
+ const routerPath = path.join(targetDir, 'modules', 'router.js');
735
+ if (!fs.existsSync(routerPath)) return;
736
+
737
+ let content = fs.readFileSync(routerPath, 'utf8');
738
+
739
+ const old404 = `// 404 page — shown when no route matches
740
+ function NotFoundPage() {
741
+ return (
742
+ <div class="not-found">
743
+ <h1>404</h1>
744
+ <p>Page not found.</p>
745
+ <button class="btn btn-primary" onClick={() => navigate("home")}>Go Home</button>
746
+ </div>
747
+ );
748
+ }`;
749
+
750
+ const new404 = `// 404 page — shown when no route matches
751
+ function NotFoundPage() {
752
+ return (
753
+ <div class="flex flex-col items-center justify-center min-h-[60vh] text-center px-4">
754
+ <h1 class="text-9xl font-bold text-primary mb-4">404</h1>
755
+ <p class="text-2xl text-gray-600 mb-8">Oops! The page you're looking for doesn't exist.</p>
756
+ <button
757
+ class="px-8 py-3 bg-primary text-dark font-bold rounded-lg hover:opacity-80 transition transform hover:scale-105"
758
+ onClick={() => navigate("home")}
759
+ >
760
+ ← Go Home
761
+ </button>
762
+ </div>
763
+ );
764
+ }`;
765
+
766
+ content = content.replace(old404, new404);
767
+ fs.writeFileSync(routerPath, content);
768
+ }
769
+
691
770
  // Run main function
692
771
  main().catch(err => {
693
772
  console.error('❌ Error:', err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-aziz-app",
3
- "version": "2.0.6",
3
+ "version": "2.0.8",
4
4
  "description": "A React-like framework with state management, routing, and API routes - Create apps with auto-import, live reload, and Next.js-like features",
5
5
  "keywords": [
6
6
  "react",
@@ -0,0 +1,15 @@
1
+ {
2
+ "tailwindCSS.experimental.classRegex": [
3
+ ["class\\s*=\\s*['\"`]([^'\"`]*)['\"`]", "([^\\s]+)"]
4
+ ],
5
+ "tailwindCSS.includeLanguages": {
6
+ "javascript": "javascript",
7
+ "javascriptreact": "javascript"
8
+ },
9
+ "files.associations": {
10
+ "*.jsx": "javascriptreact"
11
+ },
12
+ "editor.quickSuggestions": {
13
+ "strings": true
14
+ }
15
+ }
@@ -72,8 +72,7 @@ Just create files in the appropriate folders - they're automatically imported!
72
72
  // pages/Profile.jsx
73
73
  /** @jsx h */
74
74
 
75
- registerPage("profile", ProfilePage);
76
-
75
+ // Auto-registers as "#/profile" - no registerPage() needed!
77
76
  function ProfilePage() {
78
77
  const [name, setName] = useState('');
79
78
 
@@ -93,8 +92,19 @@ function ProfilePage() {
93
92
 
94
93
  **Navigate to:** `http://localhost:3000/#/profile`
95
94
 
95
+ ✨ **That's it!** No `registerPage()` call needed. Just name your function `YourNamePage()` and it auto-registers!
96
+
96
97
  ## ✨ Features
97
98
 
99
+ ### Auto-Registration ⚡
100
+ ```javascript
101
+ // Just name your function with "Page" suffix
102
+ function AboutPage() {
103
+ return <div><h1>About</h1></div>;
104
+ }
105
+ // Automatically becomes route "#/about"
106
+ ```
107
+
98
108
  ### State Management
99
109
  ```javascript
100
110
  const [count, setCount] = useState(0);
@@ -103,8 +113,11 @@ const [name, setName] = useState('');
103
113
 
104
114
  ### Routing
105
115
  ```javascript
106
- // Register a page
107
- registerPage("about", AboutPage);
116
+ // Auto-registers based on function name
117
+ function HomePage() { } // → #/home
118
+
119
+ // Or manually register for custom names
120
+ registerPage("user-profile", UserProfilePage);
108
121
 
109
122
  // Navigate
110
123
  navigate("about");
@@ -138,8 +151,10 @@ The CLI will automatically set up your chosen framework!
138
151
  ## 📚 Documentation
139
152
 
140
153
  - **[Getting Started](docs/GETTING-STARTED.md)** - Complete setup guide
154
+ - **[Auto-Registration](docs/AUTO-REGISTRATION.md)** - How pages auto-register
141
155
  - **[API Guide](docs/API-GUIDE.md)** - How to create API routes
142
156
  - **[Auto-Import](docs/AUTO-IMPORT.md)** - Auto-import system details
157
+ - **[Dev Server](docs/DEV-SERVER.md)** - Live reload and development
143
158
  - **[Quick Start](docs/QUICK-START.md)** - Quick examples
144
159
 
145
160
  ## 🛠️ Commands
@@ -153,8 +168,9 @@ npm run build # Generate imports manually
153
168
 
154
169
  ### Pages
155
170
  - Files in `pages/` become routes
156
- - Use `registerPage("route", Component)`
157
- - Access via `#/route`
171
+ - Name functions with "Page" suffix (e.g., `AboutPage`)
172
+ - Auto-registers as route (e.g., `#/about`)
173
+ - Optional: Use `registerPage("custom-name", Component)` for custom routes
158
174
 
159
175
  ### Components
160
176
  - Reusable UI components
package/template/app.html CHANGED
@@ -40,6 +40,7 @@
40
40
  <script type="text/babel" src="pages/About.jsx"></script>
41
41
  <script type="text/babel" src="pages/ApiDemo.jsx"></script>
42
42
  <script type="text/babel" src="pages/Contact.jsx"></script>
43
+ <script type="text/babel" src="pages/Example.jsx"></script>
43
44
  <script type="text/babel" src="pages/Home.jsx"></script>
44
45
  <script type="text/babel" src="pages/test.jsx"></script>
45
46
 
@@ -0,0 +1,248 @@
1
+ # Auto-Registration Guide
2
+
3
+ ## Overview
4
+
5
+ Pages automatically register themselves based on their function name. No need to call `registerPage()` manually!
6
+
7
+ ## How It Works
8
+
9
+ ### ✨ Automatic Registration
10
+
11
+ Just name your page function with "Page" at the end:
12
+
13
+ ```jsx
14
+ /** @jsx h */
15
+
16
+ // That's it! Automatically becomes route "#/example"
17
+ function ExamplePage() {
18
+ return (
19
+ <div>
20
+ <h1>Example Page</h1>
21
+ <p>This page auto-registered!</p>
22
+ </div>
23
+ );
24
+ }
25
+ ```
26
+
27
+ ### Function Name → Route Mapping
28
+
29
+ The system converts your function name to a route:
30
+
31
+ | Function Name | Auto Route | URL |
32
+ |--------------|------------|-----|
33
+ | `HomePage()` | `home` | `#/home` |
34
+ | `AboutPage()` | `about` | `#/about` |
35
+ | `ContactPage()` | `contact` | `#/contact` |
36
+ | `UserProfilePage()` | `userprofile` | `#/userprofile` |
37
+ | `MyAwesomePage()` | `myawesome` | `#/myawesome` |
38
+
39
+ **Rule:** Remove "Page" suffix and convert to lowercase.
40
+
41
+ ## Manual Registration (Optional)
42
+
43
+ You can still manually register for custom route names:
44
+
45
+ ```jsx
46
+ /** @jsx h */
47
+
48
+ // Custom route name with dashes or different naming
49
+ registerPage("user-profile", UserProfilePage);
50
+
51
+ function UserProfilePage() {
52
+ return <div><h1>User Profile</h1></div>;
53
+ }
54
+ ```
55
+
56
+ ### When to Use Manual Registration
57
+
58
+ - **Custom route names** with dashes: `"api-demo"`, `"user-settings"`
59
+ - **Multiple routes** pointing to same component
60
+ - **Override auto-registration** with different name
61
+
62
+ ## Examples
63
+
64
+ ### Example 1: Simple Auto Page
65
+
66
+ ```jsx
67
+ /** @jsx h */
68
+
69
+ function ProductsPage() {
70
+ return (
71
+ <div>
72
+ <h1>Products</h1>
73
+ <p>Browse our products</p>
74
+ </div>
75
+ );
76
+ }
77
+ ```
78
+
79
+ Access at: `#/products`
80
+
81
+ ### Example 2: With State
82
+
83
+ ```jsx
84
+ /** @jsx h */
85
+
86
+ function DashboardPage() {
87
+ const [count, setCount] = useState(0);
88
+
89
+ return (
90
+ <div>
91
+ <h1>Dashboard</h1>
92
+ <p>Count: {count}</p>
93
+ <button onClick={() => setCount(count + 1)}>
94
+ Increment
95
+ </button>
96
+ </div>
97
+ );
98
+ }
99
+ ```
100
+
101
+ Access at: `#/dashboard`
102
+
103
+ ### Example 3: Manual Registration
104
+
105
+ ```jsx
106
+ /** @jsx h */
107
+
108
+ // Want route "#/user-settings" instead of "#/usersettings"
109
+ registerPage("user-settings", UserSettingsPage);
110
+
111
+ function UserSettingsPage() {
112
+ return (
113
+ <div>
114
+ <h1>User Settings</h1>
115
+ </div>
116
+ );
117
+ }
118
+ ```
119
+
120
+ Access at: `#/user-settings`
121
+
122
+ ## Creating a New Page
123
+
124
+ ### Step 1: Create the file
125
+
126
+ Create `pages/MyNewPage.jsx`:
127
+
128
+ ```jsx
129
+ /** @jsx h */
130
+
131
+ function MyNewPage() {
132
+ return (
133
+ <div>
134
+ <h1>My New Page</h1>
135
+ <div class="card">
136
+ <p>This is my new page!</p>
137
+ </div>
138
+ </div>
139
+ );
140
+ }
141
+ ```
142
+
143
+ ### Step 2: That's it!
144
+
145
+ The dev server will:
146
+ 1. Detect the new file
147
+ 2. Auto-generate the import in `app.html`
148
+ 3. Auto-register the route as `#/mynew`
149
+ 4. Reload the browser
150
+
151
+ Navigate to `#/mynew` and your page appears!
152
+
153
+ ## Debugging
154
+
155
+ ### Check Auto-Registration
156
+
157
+ Open browser console when the app starts. You'll see:
158
+
159
+ ```
160
+ 📄 Auto-registered: home → HomePage()
161
+ 📄 Auto-registered: about → AboutPage()
162
+ 📄 Auto-registered: contact → ContactPage()
163
+ 📄 Auto-registered: mynew → MyNewPage()
164
+ ```
165
+
166
+ ### Page Not Found?
167
+
168
+ 1. **Check function name** - Must end with "Page"
169
+ 2. **Check file is loaded** - Look in `app.html` for the script tag
170
+ 3. **Check console** - Look for auto-registration message
171
+ 4. **Try manual registration** - Use `registerPage("route", ComponentPage)`
172
+
173
+ ## Best Practices
174
+
175
+ ### ✅ Do This
176
+
177
+ ```jsx
178
+ // Clear, descriptive names
179
+ function HomePage() { }
180
+ function AboutPage() { }
181
+ function ContactPage() { }
182
+ function UserProfilePage() { }
183
+ ```
184
+
185
+ ### ❌ Avoid This
186
+
187
+ ```jsx
188
+ // Won't auto-register (missing "Page" suffix)
189
+ function Home() { }
190
+ function About() { }
191
+
192
+ // Will create weird routes
193
+ function Page() { } // route: "" (empty)
194
+ function MyPage() { } // route: "my" (too generic)
195
+ ```
196
+
197
+ ## Migration Guide
198
+
199
+ ### Old Way (Still Works)
200
+
201
+ ```jsx
202
+ /** @jsx h */
203
+
204
+ registerPage("about", AboutPage);
205
+
206
+ function AboutPage() {
207
+ return <div><h1>About</h1></div>;
208
+ }
209
+ ```
210
+
211
+ ### New Way (Simpler)
212
+
213
+ ```jsx
214
+ /** @jsx h */
215
+
216
+ function AboutPage() {
217
+ return <div><h1>About</h1></div>;
218
+ }
219
+ ```
220
+
221
+ Both work! The new way is just less code.
222
+
223
+ ## Advanced: Multiple Routes
224
+
225
+ Point multiple routes to the same component:
226
+
227
+ ```jsx
228
+ /** @jsx h */
229
+
230
+ // Auto-registers as "dashboard"
231
+ function DashboardPage() {
232
+ return <div><h1>Dashboard</h1></div>;
233
+ }
234
+
235
+ // Also register as "home" and "index"
236
+ registerPage("home", DashboardPage);
237
+ registerPage("index", DashboardPage);
238
+ ```
239
+
240
+ Now `#/dashboard`, `#/home`, and `#/index` all show the same page!
241
+
242
+ ## Summary
243
+
244
+ - **Function name ends with "Page"** → Auto-registers
245
+ - **Route name** = function name without "Page", lowercase
246
+ - **Manual registration** still available for custom names
247
+ - **Dev server** handles everything automatically
248
+ - **Zero configuration** needed for basic pages
@@ -0,0 +1,98 @@
1
+ # Dev Server Guide
2
+
3
+ ## Overview
4
+
5
+ The dev server provides a smooth development experience with live reload, automatic import generation, and file watching.
6
+
7
+ ## Features
8
+
9
+ ### 🔄 Live Reload
10
+ - **Automatic browser refresh** when you save files
11
+ - **Visual indicator** in bottom-right corner shows connection status
12
+ - **Smart caching** - forces cache clear on reload to always show latest changes
13
+ - **CSS hot reload** - instant updates without regenerating imports
14
+
15
+ ### 👀 File Watching
16
+ The dev server watches these directories:
17
+ - `api/` - API route files
18
+ - `components/` - React-like components
19
+ - `pages/` - Page components
20
+ - `modules/` - Core system modules
21
+ - `styles/` - CSS files
22
+
23
+ ### 🚀 Auto Import Generation
24
+ - Automatically generates script tags in `app.html`
25
+ - Runs on startup and whenever JS/JSX files change
26
+ - No need to manually add imports
27
+
28
+ ### 🎯 Smart Port Selection
29
+ - Defaults to port 3000
30
+ - Automatically finds next available port if 3000 is busy
31
+ - Shows the actual URL in console
32
+
33
+ ## Usage
34
+
35
+ ```bash
36
+ npm run dev
37
+ ```
38
+
39
+ Then open the URL shown in console (usually `http://localhost:3000/`)
40
+
41
+ ## Visual Indicators
42
+
43
+ The live reload indicator in the bottom-right corner shows:
44
+ - 🔌 **Connecting...** - Initial connection
45
+ - ✅ **Live Reload Active** - Connected and watching (fades after 2s)
46
+ - 🔄 **Reloading...** - Changes detected, refreshing page
47
+ - ⚠️ **Reconnecting...** - Connection lost, attempting to reconnect
48
+
49
+ Hover over the indicator to make it fully visible.
50
+
51
+ ## How It Works
52
+
53
+ 1. **Server starts** and generates initial imports
54
+ 2. **Browser connects** via Server-Sent Events (SSE)
55
+ 3. **File watcher** detects changes in watched directories
56
+ 4. **Debouncing** prevents multiple rapid reloads (300ms for CSS, 500ms for JS)
57
+ 5. **Import regeneration** (for JS/JSX changes only)
58
+ 6. **Browser notification** via SSE
59
+ 7. **Page reload** with cache busting
60
+
61
+ ## Cache Control
62
+
63
+ The dev server sends these headers to prevent caching:
64
+ ```
65
+ Cache-Control: no-cache, no-store, must-revalidate
66
+ Pragma: no-cache
67
+ Expires: 0
68
+ ```
69
+
70
+ This ensures you always see the latest changes without needing Ctrl+Shift+R.
71
+
72
+ ## Troubleshooting
73
+
74
+ ### Changes not showing?
75
+ 1. Check the visual indicator - is it green?
76
+ 2. Open browser console - look for "✅ Live reload connected"
77
+ 3. Check terminal - does it show "🔄 Notifying X browser(s) to reload..."?
78
+ 4. Try hard refresh once: Ctrl+Shift+R (Windows) or Cmd+Shift+R (Mac)
79
+
80
+ ### Port already in use?
81
+ The server will automatically try the next port (3001, 3002, etc.)
82
+
83
+ ### Live reload disconnected?
84
+ The page will automatically try to reconnect after 2 seconds.
85
+
86
+ ## Performance
87
+
88
+ - **Debouncing** prevents reload spam when saving multiple files
89
+ - **CSS-only changes** skip import regeneration for faster updates
90
+ - **SSE connection** is lightweight and efficient
91
+ - **Multiple browsers** can connect simultaneously
92
+
93
+ ## Development Tips
94
+
95
+ 1. Keep the terminal visible to see file change notifications
96
+ 2. The visual indicator helps confirm live reload is working
97
+ 3. CSS changes are faster than JS changes (no import regeneration)
98
+ 4. Multiple browser tabs will all reload simultaneously
@@ -56,17 +56,35 @@ function h(tag, props, ...children) {
56
56
  // "#/contact" → Contact page
57
57
  //
58
58
  // To add a new page:
59
- // 1. Create pages/YourPage.jsx
60
- // 2. Load it in app.html with <script type="text/babel">
61
- // 3. Register it below in the `routes` object
59
+ // 1. Create pages/YourPage.jsx with a function like AboutPage()
60
+ // 2. The page will automatically be registered!
61
+ // 3. Optional: Call registerPage("about", AboutPage) for custom route names
62
62
  // ============================================================
63
63
  const routes = {};
64
64
 
65
65
  // registerPage("about", AboutPage) — called by each page file
66
+ // Now optional! Pages auto-register based on function name
66
67
  function registerPage(path, component) {
67
68
  routes[path] = component;
68
69
  }
69
70
 
71
+ // Auto-register pages from global scope
72
+ // Looks for functions ending with "Page" (e.g., AboutPage, HomePage)
73
+ function autoRegisterPages() {
74
+ for (let key in window) {
75
+ if (key.endsWith('Page') && typeof window[key] === 'function') {
76
+ // Convert "AboutPage" → "about", "HomePage" → "home"
77
+ const routeName = key.replace('Page', '').toLowerCase();
78
+
79
+ // Only register if not already registered
80
+ if (!routes[routeName]) {
81
+ routes[routeName] = window[key];
82
+ console.log(`📄 Auto-registered: ${routeName} → ${key}()`);
83
+ }
84
+ }
85
+ }
86
+ }
87
+
70
88
  function getCurrentPath() {
71
89
  // Remove the "#/" prefix → "about", "contact", or "" for home
72
90
  return window.location.hash.replace("#/", "") || "home";
@@ -162,8 +180,10 @@ function NotFoundPage() {
162
180
  return (
163
181
  <div class="not-found">
164
182
  <h1>404</h1>
165
- <p>Page not found.</p>
166
- <button class="btn btn-primary" onClick={() => navigate("home")}>Go Home</button>
183
+ <p>Oops! The page you're looking for doesn't exist.</p>
184
+ <button class="btn btn-primary" onClick={() => navigate("home")}>
185
+ Go Home
186
+ </button>
167
187
  </div>
168
188
  );
169
189
  }
@@ -174,6 +194,9 @@ function NotFoundPage() {
174
194
  // Called once after all scripts are loaded
175
195
  // ============================================================
176
196
  function startApp() {
197
+ // Auto-register any pages that weren't manually registered
198
+ autoRegisterPages();
199
+
177
200
  renderShell();
178
201
  renderPage();
179
202
 
@@ -1,8 +1,9 @@
1
1
  /** @jsx h */
2
2
 
3
- // Register this page at route "about" → "#/about"
4
- registerPage("about", AboutPage);
3
+ // Optional: You can still manually register if you want a custom route name
4
+ // registerPage("about", AboutPage);
5
5
 
6
+ // Or just define the function and it auto-registers as "about"
6
7
  function AboutPage() {
7
8
  return (
8
9
  <div>
@@ -28,6 +29,20 @@ function AboutPage() {
28
29
  Go to Contact →
29
30
  </button>
30
31
  </div>
32
+
33
+ <div class="card">
34
+ <h2>✨ Auto-Registration</h2>
35
+ <p>
36
+ This page works without calling <code>registerPage()</code>!
37
+ Just name your function <code>YourNamePage()</code> and it automatically
38
+ becomes a route.
39
+ </p>
40
+ <ul style={{ lineHeight: '1.8', marginLeft: '20px' }}>
41
+ <li><code>HomePage()</code> → <code>#/home</code></li>
42
+ <li><code>AboutPage()</code> → <code>#/about</code></li>
43
+ <li><code>ContactPage()</code> → <code>#/contact</code></li>
44
+ </ul>
45
+ </div>
31
46
  </div>
32
47
  );
33
48
  }
@@ -1,6 +1,6 @@
1
1
  /** @jsx h */
2
2
 
3
- // Register this page at route "api-demo" → "#/api-demo"
3
+ // Auto-registered as "api-demo" route (or manually register for custom name)
4
4
  registerPage("api-demo", ApiDemoPage);
5
5
 
6
6
  function ApiDemoPage() {
@@ -1,8 +1,6 @@
1
1
  /** @jsx h */
2
2
 
3
- // Register this page at route "contact" → "#/contact"
4
- registerPage("contact", ContactPage);
5
-
3
+ // Auto-registered as "contact" route
6
4
  function ContactPage() {
7
5
  return (
8
6
  <div>
@@ -0,0 +1,59 @@
1
+ /** @jsx h */
2
+
3
+ // That's it! No registerPage() needed.
4
+ // This automatically becomes route "#/example"
5
+ function ExamplePage() {
6
+ const [message, setMessage] = useState('Hello from Example Page! 🎉');
7
+
8
+ return (
9
+ <div>
10
+ <h1>Example Page</h1>
11
+
12
+ <div class="card">
13
+ <h2>✨ Auto-Registration Demo</h2>
14
+ <p>
15
+ This page was created without calling <code>registerPage()</code>.
16
+ Just define a function ending with "Page" and it works!
17
+ </p>
18
+
19
+ <div style={{
20
+ background: '#f0f0f0',
21
+ padding: '16px',
22
+ borderRadius: '8px',
23
+ fontFamily: 'monospace',
24
+ fontSize: '14px'
25
+ }}>
26
+ <strong>Function name:</strong> ExamplePage()<br/>
27
+ <strong>Auto route:</strong> #/example<br/>
28
+ <strong>URL:</strong> {window.location.href}
29
+ </div>
30
+ </div>
31
+
32
+ <div class="card">
33
+ <h2>State Management Works Too</h2>
34
+ <p>{message}</p>
35
+ <button
36
+ class="btn btn-primary"
37
+ onClick={() => setMessage('State updated! ✅')}
38
+ >
39
+ Update Message
40
+ </button>
41
+ </div>
42
+
43
+ <div class="card">
44
+ <h2>Navigation</h2>
45
+ <div style={{ display: 'flex', gap: '12px' }}>
46
+ <button class="btn btn-primary" onClick={() => navigate("home")}>
47
+ Home
48
+ </button>
49
+ <button class="btn btn-primary" onClick={() => navigate("about")}>
50
+ About
51
+ </button>
52
+ <button class="btn btn-primary" onClick={() => navigate("test")}>
53
+ Test
54
+ </button>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ );
59
+ }
@@ -1,7 +1,6 @@
1
1
  /** @jsx h */
2
2
 
3
- registerPage("home", HomePage);
4
-
3
+ // Auto-registered as "home" route
5
4
  function HomePage() {
6
5
  return (
7
6
  <div>
@@ -1,7 +1,6 @@
1
1
  /** @jsx h */
2
2
 
3
- registerPage("test", TestPage);
4
-
3
+ // Auto-registered as "test" route
5
4
  function TestPage() {
6
5
  const [count, setCount] = useState(0);
7
6
  const [name, setName] = useState('');
@@ -26,7 +26,8 @@ function generateImports() {
26
26
 
27
27
  // Notify all clients to reload
28
28
  function notifyClients() {
29
- console.log('🔄 Notifying browser to reload...');
29
+ const timestamp = new Date().toLocaleTimeString();
30
+ console.log(`🔄 [${timestamp}] Notifying ${clients.length} browser(s) to reload...`);
30
31
  clients.forEach(client => {
31
32
  client.write('data: reload\n\n');
32
33
  });
@@ -35,59 +36,74 @@ function notifyClients() {
35
36
  // Live reload script to inject
36
37
  const liveReloadScript = `
37
38
  <script>
38
- // Hot Module Replacement (HMR) - Smart reload
39
- const evtSource = new EventSource('/live-reload');
40
- let isReloading = false;
41
-
42
- evtSource.onmessage = (event) => {
43
- if (event.data === 'reload' && !isReloading) {
44
- isReloading = true;
45
- console.log('🔄 Changes detected, hot reloading...');
46
-
47
- // Save current state
48
- const currentPath = window.location.hash;
49
- const scrollPos = window.scrollY;
50
-
51
- // Reload only changed scripts
52
- const scripts = document.querySelectorAll('script[type="text/babel"]');
53
- let reloadCount = 0;
54
-
55
- scripts.forEach((script, index) => {
56
- if (script.src && !script.src.includes('babel.min.js')) {
57
- const newScript = document.createElement('script');
58
- newScript.type = 'text/babel';
59
- newScript.src = script.src + '?t=' + Date.now();
60
-
61
- newScript.onload = () => {
62
- reloadCount++;
63
- if (reloadCount === scripts.length - 1) {
64
- // All scripts reloaded, re-render
65
- setTimeout(() => {
66
- if (typeof renderShell === 'function') renderShell();
67
- if (typeof renderPage === 'function') renderPage();
68
- window.scrollTo(0, scrollPos);
69
- console.log('✅ Hot reload complete');
70
- isReloading = false;
71
- }, 100);
72
- }
73
- };
74
-
75
- script.parentNode.replaceChild(newScript, script);
76
- }
77
- });
78
-
79
- // Fallback: if no scripts to reload, just refresh
80
- if (scripts.length <= 1) {
81
- location.reload();
39
+ (function() {
40
+ // Create visual indicator
41
+ const indicator = document.createElement('div');
42
+ indicator.id = 'live-reload-indicator';
43
+ indicator.style.cssText = \`
44
+ position: fixed;
45
+ bottom: 20px;
46
+ right: 20px;
47
+ background: #4caf50;
48
+ color: white;
49
+ padding: 8px 16px;
50
+ border-radius: 20px;
51
+ font-size: 12px;
52
+ font-family: monospace;
53
+ z-index: 9999;
54
+ box-shadow: 0 2px 8px rgba(0,0,0,0.2);
55
+ transition: all 0.3s;
56
+ \`;
57
+ indicator.textContent = '🔌 Connecting...';
58
+ document.body.appendChild(indicator);
59
+
60
+ // Live reload client
61
+ console.log('🔌 Connecting to live reload...');
62
+ const evtSource = new EventSource('/live-reload');
63
+
64
+ evtSource.onopen = () => {
65
+ console.log('✅ Live reload connected');
66
+ indicator.style.background = '#4caf50';
67
+ indicator.textContent = '✅ Live Reload Active';
68
+ // Hide after 2 seconds
69
+ setTimeout(() => {
70
+ indicator.style.opacity = '0.3';
71
+ }, 2000);
72
+ };
73
+
74
+ evtSource.onmessage = (event) => {
75
+ if (event.data === 'reload') {
76
+ console.log('🔄 Changes detected, reloading...');
77
+ indicator.style.opacity = '1';
78
+ indicator.style.background = '#2196f3';
79
+ indicator.textContent = '🔄 Reloading...';
80
+ // Force reload and clear cache
81
+ setTimeout(() => {
82
+ window.location.reload(true);
83
+ }, 100);
82
84
  }
83
- }
84
- };
85
-
86
- evtSource.onerror = () => {
87
- console.log('⚠️ Live reload disconnected');
88
- };
89
-
90
- console.log('✅ Hot reload connected');
85
+ };
86
+
87
+ evtSource.onerror = (err) => {
88
+ console.log('⚠️ Live reload disconnected, retrying...');
89
+ indicator.style.background = '#ff9800';
90
+ indicator.style.opacity = '1';
91
+ indicator.textContent = '⚠️ Reconnecting...';
92
+ evtSource.close();
93
+ // Retry connection after 2 seconds
94
+ setTimeout(() => {
95
+ window.location.reload();
96
+ }, 2000);
97
+ };
98
+
99
+ // Show indicator on hover
100
+ indicator.addEventListener('mouseenter', () => {
101
+ indicator.style.opacity = '1';
102
+ });
103
+ indicator.addEventListener('mouseleave', () => {
104
+ indicator.style.opacity = '0.3';
105
+ });
106
+ })();
91
107
  </script>
92
108
  `;
93
109
 
@@ -159,7 +175,13 @@ function createServer(port) {
159
175
  content = content.toString().replace('</body>', liveReloadScript + '</body>');
160
176
  }
161
177
 
162
- res.writeHead(200, { 'Content-Type': contentType });
178
+ // Add cache control headers to prevent caching during development
179
+ res.writeHead(200, {
180
+ 'Content-Type': contentType,
181
+ 'Cache-Control': 'no-cache, no-store, must-revalidate',
182
+ 'Pragma': 'no-cache',
183
+ 'Expires': '0'
184
+ });
163
185
  res.end(content, 'utf-8');
164
186
  }
165
187
  });
@@ -184,16 +206,35 @@ function startServer(port) {
184
206
  }
185
207
 
186
208
  // Watch directories
187
- const watchDirs = ['api', 'components', 'pages', 'modules'];
209
+ const watchDirs = ['api', 'components', 'pages', 'modules', 'styles'];
210
+
211
+ // Debounce to prevent multiple rapid reloads
212
+ let reloadTimeout = null;
188
213
 
189
214
  console.log('👀 Watching for changes in:');
190
215
  watchDirs.forEach(dir => {
191
216
  if (fs.existsSync(dir)) {
192
217
  console.log(` - ${dir}/`);
193
218
  fs.watch(dir, { recursive: false }, (eventType, filename) => {
194
- if (filename && (filename.endsWith('.js') || filename.endsWith('.jsx'))) {
219
+ if (filename && (filename.endsWith('.js') || filename.endsWith('.jsx') || filename.endsWith('.css'))) {
195
220
  console.log(`\n📝 Detected change: ${dir}/${filename}`);
196
- generateImports();
221
+
222
+ // Clear existing timeout
223
+ if (reloadTimeout) {
224
+ clearTimeout(reloadTimeout);
225
+ }
226
+
227
+ // For CSS changes, just reload without regenerating imports
228
+ if (filename.endsWith('.css')) {
229
+ reloadTimeout = setTimeout(() => {
230
+ notifyClients();
231
+ }, 300);
232
+ } else {
233
+ // For JS/JSX changes, regenerate imports
234
+ reloadTimeout = setTimeout(() => {
235
+ generateImports();
236
+ }, 500);
237
+ }
197
238
  }
198
239
  });
199
240
  }
@@ -0,0 +1,17 @@
1
+ /** @type {import('tailwindcss').Config} */
2
+ module.exports = {
3
+ content: [
4
+ "./pages/**/*.{js,jsx}",
5
+ "./components/**/*.{js,jsx}",
6
+ "./app.html",
7
+ ],
8
+ theme: {
9
+ extend: {
10
+ colors: {
11
+ primary: '#61dafb',
12
+ dark: '#282c34',
13
+ },
14
+ },
15
+ },
16
+ plugins: [],
17
+ }