create-aziz-app 2.0.10 → 2.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/create-aziz-app.js +36 -0
- package/package.json +1 -1
- package/template/app.html +3 -0
- package/template/docs/DEV-SERVER.md +108 -22
- package/template/docs/HOW-IT-WORKS.md +362 -0
- package/template/package-lock.json +13 -0
- package/template/package.json +1 -1
- package/template/scripts/dev-server.js +95 -171
- package/template/scripts/live-reload-client.js +105 -0
package/bin/create-aziz-app.js
CHANGED
|
@@ -217,6 +217,18 @@ function setupCSSFramework(targetDir, framework) {
|
|
|
217
217
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">`
|
|
218
218
|
);
|
|
219
219
|
|
|
220
|
+
// Ensure live reload script is present
|
|
221
|
+
if (!html.includes('live-reload-client.js')) {
|
|
222
|
+
html = html.replace(
|
|
223
|
+
'<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>',
|
|
224
|
+
`<!-- Live Reload (only active in development) -->
|
|
225
|
+
<script src="scripts/live-reload-client.js"></script>
|
|
226
|
+
|
|
227
|
+
<!-- Babel compiles JSX in the browser -->
|
|
228
|
+
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>`
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
220
232
|
// Add Bootstrap JS before closing body tag
|
|
221
233
|
const bootstrapJS = `
|
|
222
234
|
<!-- Bootstrap 5 JS -->
|
|
@@ -249,8 +261,32 @@ function setupCSSFramework(targetDir, framework) {
|
|
|
249
261
|
</script>`
|
|
250
262
|
);
|
|
251
263
|
|
|
264
|
+
// Ensure live reload script is present
|
|
265
|
+
if (!html.includes('live-reload-client.js')) {
|
|
266
|
+
html = html.replace(
|
|
267
|
+
'<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>',
|
|
268
|
+
`<!-- Live Reload (only active in development) -->
|
|
269
|
+
<script src="scripts/live-reload-client.js"></script>
|
|
270
|
+
|
|
271
|
+
<!-- Babel compiles JSX in the browser -->
|
|
272
|
+
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>`
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
252
276
|
// Update components
|
|
253
277
|
updateComponentsForTailwind(targetDir);
|
|
278
|
+
} else {
|
|
279
|
+
// For 'none', ensure live reload script is present
|
|
280
|
+
if (!html.includes('live-reload-client.js')) {
|
|
281
|
+
html = html.replace(
|
|
282
|
+
'<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>',
|
|
283
|
+
`<!-- Live Reload (only active in development) -->
|
|
284
|
+
<script src="scripts/live-reload-client.js"></script>
|
|
285
|
+
|
|
286
|
+
<!-- Babel compiles JSX in the browser -->
|
|
287
|
+
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>`
|
|
288
|
+
);
|
|
289
|
+
}
|
|
254
290
|
}
|
|
255
291
|
// If 'none', keep globals.css as is
|
|
256
292
|
|
package/package.json
CHANGED
package/template/app.html
CHANGED
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
<!-- Global Styles -->
|
|
12
12
|
<link rel="stylesheet" href="styles/globals.css">
|
|
13
13
|
|
|
14
|
+
<!-- Live Reload (only active in development) -->
|
|
15
|
+
<script src="scripts/live-reload-client.js"></script>
|
|
16
|
+
|
|
14
17
|
<!-- Babel compiles JSX in the browser -->
|
|
15
18
|
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
|
16
19
|
</head>
|
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
The dev server provides a smooth development experience with live reload, automatic import generation, and file watching.
|
|
5
|
+
The dev server provides a smooth development experience with live reload using WebSocket, automatic import generation, and file watching.
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
### 🔄 Live Reload
|
|
9
|
+
### 🔄 Live Reload (WebSocket-based)
|
|
10
10
|
- **Automatic browser refresh** when you save files
|
|
11
11
|
- **Visual indicator** in bottom-right corner shows connection status
|
|
12
|
-
- **
|
|
12
|
+
- **WebSocket connection** - reliable, fast, no caching issues
|
|
13
13
|
- **CSS hot reload** - instant updates without regenerating imports
|
|
14
|
+
- **Always loaded** - script is part of your HTML, not injected
|
|
14
15
|
|
|
15
16
|
### 👀 File Watching
|
|
16
17
|
The dev server watches these directories:
|
|
@@ -41,22 +42,36 @@ Then open the URL shown in console (usually `http://localhost:3000/`)
|
|
|
41
42
|
## Visual Indicators
|
|
42
43
|
|
|
43
44
|
The live reload indicator in the bottom-right corner shows:
|
|
44
|
-
- 🔌 **Connecting...** - Initial connection
|
|
45
|
-
- ✅ **Live Reload
|
|
46
|
-
- 🔄 **Reloading...** - Changes detected, refreshing page
|
|
47
|
-
- ⚠️ **
|
|
45
|
+
- 🔌 **Connecting...** - Initial connection (gray/green)
|
|
46
|
+
- ✅ **Live Reload** - Connected and watching (green, fades to 30%)
|
|
47
|
+
- 🔄 **Reloading...** - Changes detected, refreshing page (blue)
|
|
48
|
+
- ⚠️ **Disconnected** - Connection lost, attempting to reconnect (orange)
|
|
49
|
+
- ❌ **Offline** - Max reconnection attempts reached (red)
|
|
50
|
+
|
|
51
|
+
**Click the indicator** to manually reload the page.
|
|
48
52
|
|
|
49
53
|
Hover over the indicator to make it fully visible.
|
|
50
54
|
|
|
51
55
|
## How It Works
|
|
52
56
|
|
|
53
57
|
1. **Server starts** and generates initial imports
|
|
54
|
-
2. **Browser
|
|
55
|
-
3. **
|
|
56
|
-
4. **
|
|
57
|
-
5. **
|
|
58
|
-
6. **
|
|
59
|
-
7. **
|
|
58
|
+
2. **Browser loads** `live-reload-client.js` (always in HTML)
|
|
59
|
+
3. **WebSocket connects** to dev server
|
|
60
|
+
4. **File watcher** detects changes in watched directories
|
|
61
|
+
5. **Debouncing** prevents multiple rapid reloads (300ms for CSS, 500ms for JS)
|
|
62
|
+
6. **Import regeneration** (for JS/JSX changes only)
|
|
63
|
+
7. **WebSocket message** sent to all connected browsers
|
|
64
|
+
8. **Page reload** happens immediately
|
|
65
|
+
|
|
66
|
+
## Why WebSocket?
|
|
67
|
+
|
|
68
|
+
Previous versions used Server-Sent Events (SSE) with runtime injection, which had caching issues. The new approach:
|
|
69
|
+
|
|
70
|
+
- ✅ **Always loaded** - Script is in HTML, not injected at runtime
|
|
71
|
+
- ✅ **No caching issues** - Browser always has the script
|
|
72
|
+
- ✅ **Bidirectional** - WebSocket allows two-way communication
|
|
73
|
+
- ✅ **Reliable** - Built-in reconnection logic
|
|
74
|
+
- ✅ **Fast** - Instant notifications, no polling
|
|
60
75
|
|
|
61
76
|
## Cache Control
|
|
62
77
|
|
|
@@ -67,28 +82,52 @@ Pragma: no-cache
|
|
|
67
82
|
Expires: 0
|
|
68
83
|
```
|
|
69
84
|
|
|
70
|
-
|
|
85
|
+
Plus, `app.html` has meta tags:
|
|
86
|
+
```html
|
|
87
|
+
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
|
88
|
+
<meta http-equiv="Pragma" content="no-cache">
|
|
89
|
+
<meta http-equiv="Expires" content="0">
|
|
90
|
+
```
|
|
71
91
|
|
|
72
92
|
## Troubleshooting
|
|
73
93
|
|
|
94
|
+
### Indicator stuck on "Connecting..."?
|
|
95
|
+
|
|
96
|
+
1. **Check dev server is running** - Terminal should show "✅ Dev server running"
|
|
97
|
+
2. **Check browser console** (F12) - Look for WebSocket errors
|
|
98
|
+
3. **Check port** - Browser URL should match server URL
|
|
99
|
+
4. **Firewall** - Allow Node.js through firewall
|
|
100
|
+
5. **Try different browser** - Rule out browser-specific issues
|
|
101
|
+
|
|
74
102
|
### Changes not showing?
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
103
|
+
|
|
104
|
+
1. **Check indicator is green** - "✅ Live Reload"
|
|
105
|
+
2. **Check terminal** - Should show "📝 Detected change: ..."
|
|
106
|
+
3. **Check file was saved** - No unsaved indicator in editor
|
|
107
|
+
4. **Check file location** - Must be in watched directories
|
|
108
|
+
5. **Check for errors** - Browser console (F12) for JavaScript errors
|
|
109
|
+
|
|
110
|
+
### WebSocket connection failed?
|
|
111
|
+
|
|
112
|
+
1. **Port mismatch** - Check browser URL matches server port
|
|
113
|
+
2. **Proxy/VPN** - May block WebSocket connections
|
|
114
|
+
3. **Browser extension** - Ad blockers can interfere
|
|
115
|
+
4. **Try incognito mode** - Rule out extensions
|
|
79
116
|
|
|
80
117
|
### Port already in use?
|
|
81
|
-
The server will automatically try the next port (3001, 3002, etc.)
|
|
82
118
|
|
|
83
|
-
|
|
84
|
-
|
|
119
|
+
The server automatically tries the next port. Check terminal for actual port:
|
|
120
|
+
```
|
|
121
|
+
🌐 Server running at http://localhost:3001/
|
|
122
|
+
```
|
|
85
123
|
|
|
86
124
|
## Performance
|
|
87
125
|
|
|
88
126
|
- **Debouncing** prevents reload spam when saving multiple files
|
|
89
127
|
- **CSS-only changes** skip import regeneration for faster updates
|
|
90
|
-
- **
|
|
128
|
+
- **WebSocket** is lightweight and efficient
|
|
91
129
|
- **Multiple browsers** can connect simultaneously
|
|
130
|
+
- **Automatic reconnection** if connection drops
|
|
92
131
|
|
|
93
132
|
## Development Tips
|
|
94
133
|
|
|
@@ -96,3 +135,50 @@ The page will automatically try to reconnect after 2 seconds.
|
|
|
96
135
|
2. The visual indicator helps confirm live reload is working
|
|
97
136
|
3. CSS changes are faster than JS changes (no import regeneration)
|
|
98
137
|
4. Multiple browser tabs will all reload simultaneously
|
|
138
|
+
5. Click the indicator to manually reload if needed
|
|
139
|
+
|
|
140
|
+
## Production
|
|
141
|
+
|
|
142
|
+
The live reload script only runs in development (localhost). In production:
|
|
143
|
+
- Script detects non-localhost hostname
|
|
144
|
+
- Exits immediately without connecting
|
|
145
|
+
- No performance impact
|
|
146
|
+
- No visual indicator
|
|
147
|
+
|
|
148
|
+
## Technical Details
|
|
149
|
+
|
|
150
|
+
### WebSocket Protocol
|
|
151
|
+
|
|
152
|
+
The dev server implements a simple WebSocket server without external dependencies:
|
|
153
|
+
- Uses Node.js `http` module's `upgrade` event
|
|
154
|
+
- Performs WebSocket handshake manually
|
|
155
|
+
- Sends text frames to connected clients
|
|
156
|
+
- Handles connection cleanup automatically
|
|
157
|
+
|
|
158
|
+
### Message Format
|
|
159
|
+
|
|
160
|
+
Server sends a single message type:
|
|
161
|
+
- `"reload"` - Triggers browser reload
|
|
162
|
+
|
|
163
|
+
### Reconnection Logic
|
|
164
|
+
|
|
165
|
+
Client attempts to reconnect:
|
|
166
|
+
- Max 10 attempts
|
|
167
|
+
- 2-second delay between attempts
|
|
168
|
+
- Exponential backoff could be added if needed
|
|
169
|
+
|
|
170
|
+
## Comparison with Other Tools
|
|
171
|
+
|
|
172
|
+
| Feature | Aziz App | Live Server | Vite |
|
|
173
|
+
|---------|----------|-------------|------|
|
|
174
|
+
| Setup | Zero config | Zero config | Config file |
|
|
175
|
+
| Speed | Fast | Fast | Very fast |
|
|
176
|
+
| WebSocket | ✅ | ✅ | ✅ |
|
|
177
|
+
| Auto-import | ✅ | ❌ | ✅ |
|
|
178
|
+
| Visual indicator | ✅ | ❌ | ❌ |
|
|
179
|
+
| Dependencies | None | None | Many |
|
|
180
|
+
|
|
181
|
+
## Summary
|
|
182
|
+
|
|
183
|
+
The dev server provides a reliable, fast live reload experience using WebSocket. The script is always loaded (not injected), eliminating caching issues. File watching, debouncing, and automatic reconnection make development smooth and efficient.
|
|
184
|
+
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# How It Works
|
|
2
|
+
|
|
3
|
+
## System Architecture
|
|
4
|
+
|
|
5
|
+
Aziz App has three main systems working together:
|
|
6
|
+
|
|
7
|
+
1. **Auto-Import System** - Automatically generates script tags
|
|
8
|
+
2. **Live Reload System** - Automatically refreshes browser on changes
|
|
9
|
+
3. **Routing System** - File-based routing with auto-registration
|
|
10
|
+
|
|
11
|
+
## 1. Auto-Import System
|
|
12
|
+
|
|
13
|
+
### Files Involved:
|
|
14
|
+
- `scripts/generate-imports.js` - Scans folders and generates script tags
|
|
15
|
+
- `app.html` - Contains the generated script tags
|
|
16
|
+
- `app.config.js` - Configuration (auto-generated)
|
|
17
|
+
|
|
18
|
+
### How It Works:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
1. Dev server starts
|
|
22
|
+
2. Runs generate-imports.js
|
|
23
|
+
3. Scans: api/, components/, pages/
|
|
24
|
+
4. Generates <script> tags for each file
|
|
25
|
+
5. Updates app.html (body section only)
|
|
26
|
+
6. Browser loads all scripts
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### What Gets Generated:
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<!-- Load core modules -->
|
|
33
|
+
<script type="text/babel" src="modules/router.js"></script>
|
|
34
|
+
<script type="text/babel" src="modules/state.js"></script>
|
|
35
|
+
<script type="text/babel" src="modules/api.js"></script>
|
|
36
|
+
|
|
37
|
+
<!-- Load API routes -->
|
|
38
|
+
<script type="text/babel" src="api/hello.js"></script>
|
|
39
|
+
|
|
40
|
+
<!-- Load shared components -->
|
|
41
|
+
<script type="text/babel" src="components/Navbar.jsx"></script>
|
|
42
|
+
|
|
43
|
+
<!-- Load pages -->
|
|
44
|
+
<script type="text/babel" src="pages/Home.jsx"></script>
|
|
45
|
+
|
|
46
|
+
<!-- Start the app -->
|
|
47
|
+
<script type="text/babel">
|
|
48
|
+
startApp();
|
|
49
|
+
</script>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### When It Runs:
|
|
53
|
+
|
|
54
|
+
- **On server start** - Initial generation
|
|
55
|
+
- **On file change** - When .js/.jsx files are added/removed/renamed
|
|
56
|
+
- **Manual** - Run `npm run build`
|
|
57
|
+
|
|
58
|
+
### What It Doesn't Touch:
|
|
59
|
+
|
|
60
|
+
- `<head>` section - Styles, meta tags, live-reload script
|
|
61
|
+
- Files outside watched folders
|
|
62
|
+
- Non-.js/.jsx files
|
|
63
|
+
|
|
64
|
+
## 2. Live Reload System
|
|
65
|
+
|
|
66
|
+
### Files Involved:
|
|
67
|
+
- `scripts/live-reload-client.js` - Browser-side WebSocket client
|
|
68
|
+
- `scripts/dev-server.js` - Server-side WebSocket server + file watcher
|
|
69
|
+
- `app.html` - Loads the client script
|
|
70
|
+
|
|
71
|
+
### How It Works:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
1. Browser loads app.html
|
|
75
|
+
2. app.html includes live-reload-client.js
|
|
76
|
+
3. Client creates WebSocket connection
|
|
77
|
+
4. Server watches: api/, components/, pages/, modules/, styles/
|
|
78
|
+
5. File changes detected
|
|
79
|
+
6. Server sends "reload" message via WebSocket
|
|
80
|
+
7. Client receives message
|
|
81
|
+
8. Browser reloads page
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Architecture:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
┌─────────────┐ ┌─────────────┐
|
|
88
|
+
│ Browser │ │ Dev Server │
|
|
89
|
+
│ │ │ │
|
|
90
|
+
│ live- │ WebSocket │ File │
|
|
91
|
+
│ reload- │◄──────────────────►│ Watcher │
|
|
92
|
+
│ client.js │ "reload" message │ │
|
|
93
|
+
│ │ │ │
|
|
94
|
+
└─────────────┘ └─────────────┘
|
|
95
|
+
│ │
|
|
96
|
+
│ Reload page │ Detects changes
|
|
97
|
+
▼ ▼
|
|
98
|
+
app.html api/, pages/, etc.
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Why WebSocket?
|
|
102
|
+
|
|
103
|
+
- **Reliable** - Bidirectional, persistent connection
|
|
104
|
+
- **Fast** - Instant notifications
|
|
105
|
+
- **Simple** - No external dependencies
|
|
106
|
+
- **No caching** - Script is always loaded, not injected
|
|
107
|
+
|
|
108
|
+
### Connection States:
|
|
109
|
+
|
|
110
|
+
1. **Connecting** (gray) - Initial connection
|
|
111
|
+
2. **Connected** (green) - Ready, watching for changes
|
|
112
|
+
3. **Reloading** (blue) - Change detected, reloading
|
|
113
|
+
4. **Disconnected** (orange) - Connection lost, reconnecting
|
|
114
|
+
5. **Offline** (red) - Max reconnection attempts reached
|
|
115
|
+
|
|
116
|
+
## 3. Routing System
|
|
117
|
+
|
|
118
|
+
### Files Involved:
|
|
119
|
+
- `modules/router.js` - Core routing engine
|
|
120
|
+
- `pages/*.jsx` - Page components
|
|
121
|
+
- `app.html` - Root element (#root)
|
|
122
|
+
|
|
123
|
+
### How It Works:
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
1. Browser loads all page files
|
|
127
|
+
2. Each page defines a function: HomePage(), AboutPage(), etc.
|
|
128
|
+
3. startApp() calls autoRegisterPages()
|
|
129
|
+
4. Scans window for functions ending with "Page"
|
|
130
|
+
5. Registers: HomePage → "home", AboutPage → "about"
|
|
131
|
+
6. URL hash changes: #/about
|
|
132
|
+
7. Router finds AboutPage() function
|
|
133
|
+
8. Renders it into #root
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Auto-Registration:
|
|
137
|
+
|
|
138
|
+
```javascript
|
|
139
|
+
// In pages/About.jsx
|
|
140
|
+
function AboutPage() {
|
|
141
|
+
return <div><h1>About</h1></div>;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Router automatically does:
|
|
145
|
+
routes["about"] = AboutPage;
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Manual Registration (Optional):
|
|
149
|
+
|
|
150
|
+
```javascript
|
|
151
|
+
// For custom route names
|
|
152
|
+
registerPage("user-profile", UserProfilePage);
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## How They Work Together
|
|
156
|
+
|
|
157
|
+
### Startup Sequence:
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
1. Run: npm run dev
|
|
161
|
+
2. Dev server starts
|
|
162
|
+
3. Runs generate-imports.js
|
|
163
|
+
4. Scans folders, updates app.html
|
|
164
|
+
5. Starts HTTP server on port 3000
|
|
165
|
+
6. Starts WebSocket server
|
|
166
|
+
7. Starts file watchers
|
|
167
|
+
8. Ready! ✅
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Browser Load Sequence:
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
1. Browser requests http://localhost:3000/
|
|
174
|
+
2. Server sends app.html
|
|
175
|
+
3. Browser loads:
|
|
176
|
+
- styles/globals.css
|
|
177
|
+
- scripts/live-reload-client.js (connects to WebSocket)
|
|
178
|
+
- Babel standalone
|
|
179
|
+
- modules/router.js, state.js, api.js
|
|
180
|
+
- api/*.js
|
|
181
|
+
- components/*.jsx
|
|
182
|
+
- pages/*.jsx
|
|
183
|
+
4. Calls startApp()
|
|
184
|
+
5. Auto-registers pages
|
|
185
|
+
6. Renders current route
|
|
186
|
+
7. Done! ✅
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### File Change Sequence:
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
1. Developer edits pages/Home.jsx
|
|
193
|
+
2. File watcher detects change
|
|
194
|
+
3. Debounce timer starts (500ms)
|
|
195
|
+
4. Timer expires
|
|
196
|
+
5. Runs generate-imports.js
|
|
197
|
+
6. Updates app.html
|
|
198
|
+
7. Sends "reload" via WebSocket
|
|
199
|
+
8. Browser receives message
|
|
200
|
+
9. Browser reloads
|
|
201
|
+
10. New version loads
|
|
202
|
+
11. Done! ✅
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### CSS Change Sequence (Faster):
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
1. Developer edits styles/globals.css
|
|
209
|
+
2. File watcher detects change
|
|
210
|
+
3. Debounce timer starts (300ms)
|
|
211
|
+
4. Timer expires
|
|
212
|
+
5. Skips generate-imports (not needed for CSS)
|
|
213
|
+
6. Sends "reload" via WebSocket
|
|
214
|
+
7. Browser reloads
|
|
215
|
+
8. Done! ✅
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## File Organization
|
|
219
|
+
|
|
220
|
+
### What Goes Where:
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
template/
|
|
224
|
+
├── app.html # Main HTML (auto-generated scripts)
|
|
225
|
+
├── modules/ # Core system (don't modify)
|
|
226
|
+
│ ├── router.js # Routing + rendering
|
|
227
|
+
│ ├── state.js # State management
|
|
228
|
+
│ └── api.js # API system
|
|
229
|
+
├── scripts/ # Build tools (don't modify)
|
|
230
|
+
│ ├── dev-server.js # Dev server + WebSocket
|
|
231
|
+
│ ├── generate-imports.js # Auto-import generator
|
|
232
|
+
│ └── live-reload-client.js # Browser WebSocket client
|
|
233
|
+
├── api/ # Your API routes (auto-imported)
|
|
234
|
+
├── components/ # Your components (auto-imported)
|
|
235
|
+
├── pages/ # Your pages (auto-imported)
|
|
236
|
+
└── styles/ # Your styles (watched for changes)
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### What You Modify:
|
|
240
|
+
|
|
241
|
+
- ✅ `api/` - Add your API routes
|
|
242
|
+
- ✅ `components/` - Add your components
|
|
243
|
+
- ✅ `pages/` - Add your pages
|
|
244
|
+
- ✅ `styles/` - Add your styles
|
|
245
|
+
- ❌ `modules/` - Core system
|
|
246
|
+
- ❌ `scripts/` - Build tools
|
|
247
|
+
- ⚠️ `app.html` - Only modify `<head>` section
|
|
248
|
+
|
|
249
|
+
## Key Concepts
|
|
250
|
+
|
|
251
|
+
### 1. Auto-Import is Automatic
|
|
252
|
+
|
|
253
|
+
You don't need to:
|
|
254
|
+
- Add `<script>` tags manually
|
|
255
|
+
- Run build commands after creating files
|
|
256
|
+
- Restart the dev server
|
|
257
|
+
|
|
258
|
+
Just create a file and save it!
|
|
259
|
+
|
|
260
|
+
### 2. Live Reload is Always On
|
|
261
|
+
|
|
262
|
+
The `live-reload-client.js` script:
|
|
263
|
+
- Is always loaded (in `<head>`)
|
|
264
|
+
- Only runs on localhost
|
|
265
|
+
- Connects automatically
|
|
266
|
+
- Reconnects if disconnected
|
|
267
|
+
- Safe for production (does nothing)
|
|
268
|
+
|
|
269
|
+
### 3. Pages Auto-Register
|
|
270
|
+
|
|
271
|
+
You don't need to:
|
|
272
|
+
- Call `registerPage()` (optional)
|
|
273
|
+
- Import pages manually
|
|
274
|
+
- Configure routes
|
|
275
|
+
|
|
276
|
+
Just name your function `YourNamePage()` and it works!
|
|
277
|
+
|
|
278
|
+
### 4. Everything is Watched
|
|
279
|
+
|
|
280
|
+
The dev server watches:
|
|
281
|
+
- `api/` - API routes
|
|
282
|
+
- `components/` - Components
|
|
283
|
+
- `pages/` - Pages
|
|
284
|
+
- `modules/` - Core modules
|
|
285
|
+
- `styles/` - Stylesheets
|
|
286
|
+
|
|
287
|
+
Changes trigger:
|
|
288
|
+
- Import regeneration (for .js/.jsx)
|
|
289
|
+
- Browser reload (for everything)
|
|
290
|
+
|
|
291
|
+
## Performance Optimizations
|
|
292
|
+
|
|
293
|
+
### 1. Debouncing
|
|
294
|
+
|
|
295
|
+
Prevents multiple rapid reloads:
|
|
296
|
+
- CSS changes: 300ms delay
|
|
297
|
+
- JS/JSX changes: 500ms delay
|
|
298
|
+
|
|
299
|
+
### 2. Selective Regeneration
|
|
300
|
+
|
|
301
|
+
- CSS changes: Skip import generation
|
|
302
|
+
- JS/JSX changes: Regenerate imports
|
|
303
|
+
|
|
304
|
+
### 3. WebSocket Efficiency
|
|
305
|
+
|
|
306
|
+
- Single persistent connection
|
|
307
|
+
- Minimal message size ("reload")
|
|
308
|
+
- Automatic cleanup on disconnect
|
|
309
|
+
|
|
310
|
+
### 4. Smart Watching
|
|
311
|
+
|
|
312
|
+
- Only watches specific folders
|
|
313
|
+
- Ignores non-code files
|
|
314
|
+
- Recursive: false (performance)
|
|
315
|
+
|
|
316
|
+
## Troubleshooting
|
|
317
|
+
|
|
318
|
+
### Import Generation Issues
|
|
319
|
+
|
|
320
|
+
**Problem:** New file not showing up
|
|
321
|
+
|
|
322
|
+
**Check:**
|
|
323
|
+
1. File is in `api/`, `components/`, or `pages/`
|
|
324
|
+
2. File ends with `.js` or `.jsx`
|
|
325
|
+
3. Dev server is running
|
|
326
|
+
4. Check terminal for errors
|
|
327
|
+
|
|
328
|
+
**Solution:** Run `npm run build` manually
|
|
329
|
+
|
|
330
|
+
### Live Reload Issues
|
|
331
|
+
|
|
332
|
+
**Problem:** Changes not showing
|
|
333
|
+
|
|
334
|
+
**Check:**
|
|
335
|
+
1. Indicator is green (✅ Live Reload)
|
|
336
|
+
2. Dev server is running
|
|
337
|
+
3. File was saved
|
|
338
|
+
4. Browser console for errors
|
|
339
|
+
|
|
340
|
+
**Solution:** Click indicator to manually reload
|
|
341
|
+
|
|
342
|
+
### Routing Issues
|
|
343
|
+
|
|
344
|
+
**Problem:** Page shows 404
|
|
345
|
+
|
|
346
|
+
**Check:**
|
|
347
|
+
1. Function name ends with "Page"
|
|
348
|
+
2. File is loaded (check app.html)
|
|
349
|
+
3. Browser console shows auto-registration
|
|
350
|
+
4. URL hash is correct (#/about)
|
|
351
|
+
|
|
352
|
+
**Solution:** Add manual registration
|
|
353
|
+
|
|
354
|
+
## Summary
|
|
355
|
+
|
|
356
|
+
Aziz App combines three systems:
|
|
357
|
+
|
|
358
|
+
1. **Auto-Import** - Scans folders, generates script tags
|
|
359
|
+
2. **Live Reload** - WebSocket connection, file watching
|
|
360
|
+
3. **Auto-Registration** - Scans functions, registers routes
|
|
361
|
+
|
|
362
|
+
Together, they provide a seamless development experience with zero configuration!
|
package/template/package.json
CHANGED
|
@@ -6,7 +6,7 @@ const http = require('http');
|
|
|
6
6
|
const { exec } = require('child_process');
|
|
7
7
|
|
|
8
8
|
const PORT = process.env.PORT || 3000;
|
|
9
|
-
const clients =
|
|
9
|
+
const clients = new Set();
|
|
10
10
|
|
|
11
11
|
console.log('🚀 Dev server starting...\n');
|
|
12
12
|
|
|
@@ -27,193 +27,117 @@ function generateImports() {
|
|
|
27
27
|
// Notify all clients to reload
|
|
28
28
|
function notifyClients() {
|
|
29
29
|
const timestamp = new Date().toLocaleTimeString();
|
|
30
|
-
console.log(`🔄 [${timestamp}] Notifying ${clients.
|
|
30
|
+
console.log(`🔄 [${timestamp}] Notifying ${clients.size} browser(s) to reload...`);
|
|
31
31
|
clients.forEach(client => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Live reload script to inject
|
|
37
|
-
function getLiveReloadScript() {
|
|
38
|
-
return `
|
|
39
|
-
<script>
|
|
40
|
-
(function() {
|
|
41
|
-
// Create visual indicator
|
|
42
|
-
const indicator = document.createElement('div');
|
|
43
|
-
indicator.id = 'live-reload-indicator';
|
|
44
|
-
indicator.style.cssText = 'position: fixed; bottom: 20px; right: 20px; background: #4caf50; color: white; padding: 8px 16px; border-radius: 20px; font-size: 12px; font-family: monospace; z-index: 9999; box-shadow: 0 2px 8px rgba(0,0,0,0.2); transition: all 0.3s;';
|
|
45
|
-
indicator.textContent = '🔌 Connecting...';
|
|
46
|
-
document.body.appendChild(indicator);
|
|
47
|
-
|
|
48
|
-
// Live reload client
|
|
49
|
-
console.log('🔌 Connecting to live reload at ' + window.location.origin + '/live-reload');
|
|
50
|
-
|
|
51
|
-
let evtSource;
|
|
52
|
-
let reconnectAttempts = 0;
|
|
53
|
-
const maxReconnectAttempts = 5;
|
|
54
|
-
|
|
55
|
-
function connect() {
|
|
56
|
-
try {
|
|
57
|
-
evtSource = new EventSource('/live-reload');
|
|
58
|
-
|
|
59
|
-
evtSource.onopen = function() {
|
|
60
|
-
console.log('✅ Live reload connected');
|
|
61
|
-
reconnectAttempts = 0;
|
|
62
|
-
indicator.style.background = '#4caf50';
|
|
63
|
-
indicator.textContent = '✅ Live Reload Active';
|
|
64
|
-
setTimeout(function() {
|
|
65
|
-
indicator.style.opacity = '0.3';
|
|
66
|
-
}, 2000);
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
evtSource.onmessage = function(event) {
|
|
70
|
-
if (event.data === 'reload') {
|
|
71
|
-
console.log('🔄 Changes detected, reloading...');
|
|
72
|
-
indicator.style.opacity = '1';
|
|
73
|
-
indicator.style.background = '#2196f3';
|
|
74
|
-
indicator.textContent = '🔄 Reloading...';
|
|
75
|
-
setTimeout(function() {
|
|
76
|
-
// Add timestamp to force cache bust
|
|
77
|
-
var url = window.location.href.split('?')[0];
|
|
78
|
-
window.location.href = url + '?t=' + Date.now();
|
|
79
|
-
}, 100);
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
evtSource.onerror = function(err) {
|
|
84
|
-
console.log('⚠️ Live reload connection error');
|
|
85
|
-
indicator.style.background = '#ff9800';
|
|
86
|
-
indicator.style.opacity = '1';
|
|
87
|
-
indicator.textContent = '⚠️ Connection Lost';
|
|
88
|
-
|
|
89
|
-
if (evtSource) {
|
|
90
|
-
evtSource.close();
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (reconnectAttempts < maxReconnectAttempts) {
|
|
94
|
-
reconnectAttempts++;
|
|
95
|
-
console.log('Reconnecting... attempt ' + reconnectAttempts);
|
|
96
|
-
setTimeout(connect, 2000);
|
|
97
|
-
} else {
|
|
98
|
-
indicator.textContent = '❌ Reload Failed';
|
|
99
|
-
console.log('Max reconnection attempts reached. Please refresh manually.');
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
} catch (e) {
|
|
103
|
-
console.error('Failed to create EventSource:', e);
|
|
104
|
-
indicator.style.background = '#f44336';
|
|
105
|
-
indicator.textContent = '❌ Connection Error';
|
|
106
|
-
}
|
|
32
|
+
try {
|
|
33
|
+
client.send('reload');
|
|
34
|
+
} catch (e) {
|
|
35
|
+
clients.delete(client);
|
|
107
36
|
}
|
|
108
|
-
|
|
109
|
-
connect();
|
|
110
|
-
|
|
111
|
-
// Show indicator on hover
|
|
112
|
-
indicator.addEventListener('mouseenter', function() {
|
|
113
|
-
indicator.style.opacity = '1';
|
|
114
|
-
});
|
|
115
|
-
indicator.addEventListener('mouseleave', function() {
|
|
116
|
-
if (indicator.textContent.includes('Active')) {
|
|
117
|
-
indicator.style.opacity = '0.3';
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
})();
|
|
121
|
-
</script>
|
|
122
|
-
`;
|
|
37
|
+
});
|
|
123
38
|
}
|
|
124
39
|
|
|
125
40
|
// Create HTTP server
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
41
|
+
const server = http.createServer((req, res) => {
|
|
42
|
+
// Strip query parameters for file serving (cache busting)
|
|
43
|
+
let filePath = '.' + req.url.split('?')[0];
|
|
44
|
+
if (filePath === './') {
|
|
45
|
+
filePath = './app.html';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const extname = String(path.extname(filePath)).toLowerCase();
|
|
49
|
+
const mimeTypes = {
|
|
50
|
+
'.html': 'text/html',
|
|
51
|
+
'.js': 'text/javascript',
|
|
52
|
+
'.jsx': 'text/javascript',
|
|
53
|
+
'.css': 'text/css',
|
|
54
|
+
'.json': 'application/json',
|
|
55
|
+
'.png': 'image/png',
|
|
56
|
+
'.jpg': 'image/jpg',
|
|
57
|
+
'.gif': 'image/gif',
|
|
58
|
+
'.svg': 'image/svg+xml',
|
|
59
|
+
'.wav': 'audio/wav',
|
|
60
|
+
'.mp4': 'video/mp4',
|
|
61
|
+
'.woff': 'application/font-woff',
|
|
62
|
+
'.ttf': 'application/font-ttf',
|
|
63
|
+
'.eot': 'application/vnd.ms-fontobject',
|
|
64
|
+
'.otf': 'application/font-otf',
|
|
65
|
+
'.wasm': 'application/wasm'
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const contentType = mimeTypes[extname] || 'application/octet-stream';
|
|
69
|
+
|
|
70
|
+
fs.readFile(filePath, (error, content) => {
|
|
71
|
+
if (error) {
|
|
72
|
+
if (error.code === 'ENOENT') {
|
|
73
|
+
res.writeHead(404, { 'Content-Type': 'text/html' });
|
|
74
|
+
res.end('<h1>404 - File Not Found</h1>', 'utf-8');
|
|
75
|
+
} else {
|
|
76
|
+
res.writeHead(500);
|
|
77
|
+
res.end('Server Error: ' + error.code, 'utf-8');
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
// Add cache control headers to prevent caching during development
|
|
81
|
+
res.writeHead(200, {
|
|
82
|
+
'Content-Type': contentType,
|
|
132
83
|
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
133
|
-
'
|
|
134
|
-
'
|
|
135
|
-
'X-Accel-Buffering': 'no'
|
|
84
|
+
'Pragma': 'no-cache',
|
|
85
|
+
'Expires': '0'
|
|
136
86
|
});
|
|
137
|
-
|
|
138
|
-
// Send initial connection message
|
|
139
|
-
res.write(':ok\n\n');
|
|
140
|
-
|
|
141
|
-
clients.push(res);
|
|
142
|
-
const timestamp = new Date().toLocaleTimeString();
|
|
143
|
-
console.log(`🔌 [${timestamp}] Browser connected for live reload (${clients.length} total)`);
|
|
144
|
-
|
|
145
|
-
req.on('close', () => {
|
|
146
|
-
const index = clients.indexOf(res);
|
|
147
|
-
if (index !== -1) {
|
|
148
|
-
clients.splice(index, 1);
|
|
149
|
-
}
|
|
150
|
-
const closeTime = new Date().toLocaleTimeString();
|
|
151
|
-
console.log(`🔌 [${closeTime}] Browser disconnected (${clients.length} remaining)`);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
return;
|
|
87
|
+
res.end(content, 'utf-8');
|
|
155
88
|
}
|
|
89
|
+
});
|
|
90
|
+
});
|
|
156
91
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
92
|
+
// WebSocket implementation (simple, no external dependencies)
|
|
93
|
+
server.on('upgrade', (req, socket, head) => {
|
|
94
|
+
// Simple WebSocket handshake
|
|
95
|
+
const key = req.headers['sec-websocket-key'];
|
|
96
|
+
const acceptKey = require('crypto')
|
|
97
|
+
.createHash('sha1')
|
|
98
|
+
.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
|
|
99
|
+
.digest('base64');
|
|
100
|
+
|
|
101
|
+
socket.write(
|
|
102
|
+
'HTTP/1.1 101 Switching Protocols\r\n' +
|
|
103
|
+
'Upgrade: websocket\r\n' +
|
|
104
|
+
'Connection: Upgrade\r\n' +
|
|
105
|
+
`Sec-WebSocket-Accept: ${acceptKey}\r\n` +
|
|
106
|
+
'\r\n'
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
110
|
+
console.log(`🔌 [${timestamp}] Browser connected (${clients.size + 1} total)`);
|
|
111
|
+
|
|
112
|
+
// Wrap socket with send method
|
|
113
|
+
const client = {
|
|
114
|
+
socket: socket,
|
|
115
|
+
send: function(message) {
|
|
116
|
+
const buffer = Buffer.from(message);
|
|
117
|
+
const frame = Buffer.alloc(buffer.length + 2);
|
|
118
|
+
frame[0] = 0x81; // Text frame
|
|
119
|
+
frame[1] = buffer.length;
|
|
120
|
+
buffer.copy(frame, 2);
|
|
121
|
+
socket.write(frame);
|
|
161
122
|
}
|
|
123
|
+
};
|
|
162
124
|
|
|
163
|
-
|
|
164
|
-
const mimeTypes = {
|
|
165
|
-
'.html': 'text/html',
|
|
166
|
-
'.js': 'text/javascript',
|
|
167
|
-
'.jsx': 'text/javascript',
|
|
168
|
-
'.css': 'text/css',
|
|
169
|
-
'.json': 'application/json',
|
|
170
|
-
'.png': 'image/png',
|
|
171
|
-
'.jpg': 'image/jpg',
|
|
172
|
-
'.gif': 'image/gif',
|
|
173
|
-
'.svg': 'image/svg+xml',
|
|
174
|
-
'.wav': 'audio/wav',
|
|
175
|
-
'.mp4': 'video/mp4',
|
|
176
|
-
'.woff': 'application/font-woff',
|
|
177
|
-
'.ttf': 'application/font-ttf',
|
|
178
|
-
'.eot': 'application/vnd.ms-fontobject',
|
|
179
|
-
'.otf': 'application/font-otf',
|
|
180
|
-
'.wasm': 'application/wasm'
|
|
181
|
-
};
|
|
125
|
+
clients.add(client);
|
|
182
126
|
|
|
183
|
-
|
|
127
|
+
socket.on('close', () => {
|
|
128
|
+
clients.delete(client);
|
|
129
|
+
const closeTime = new Date().toLocaleTimeString();
|
|
130
|
+
console.log(`🔌 [${closeTime}] Browser disconnected (${clients.size} remaining)`);
|
|
131
|
+
});
|
|
184
132
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
res.writeHead(404, { 'Content-Type': 'text/html' });
|
|
189
|
-
res.end('<h1>404 - File Not Found</h1>', 'utf-8');
|
|
190
|
-
} else {
|
|
191
|
-
res.writeHead(500);
|
|
192
|
-
res.end('Server Error: ' + error.code, 'utf-8');
|
|
193
|
-
}
|
|
194
|
-
} else {
|
|
195
|
-
// Inject live reload script into HTML files
|
|
196
|
-
if (extname === '.html') {
|
|
197
|
-
content = content.toString().replace('</body>', getLiveReloadScript() + '</body>');
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Add cache control headers to prevent caching during development
|
|
201
|
-
res.writeHead(200, {
|
|
202
|
-
'Content-Type': contentType,
|
|
203
|
-
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
204
|
-
'Pragma': 'no-cache',
|
|
205
|
-
'Expires': '0'
|
|
206
|
-
});
|
|
207
|
-
res.end(content, 'utf-8');
|
|
208
|
-
}
|
|
209
|
-
});
|
|
133
|
+
socket.on('error', (err) => {
|
|
134
|
+
clients.delete(client);
|
|
135
|
+
console.log('⚠️ Socket error:', err.message);
|
|
210
136
|
});
|
|
211
|
-
}
|
|
137
|
+
});
|
|
212
138
|
|
|
213
139
|
// Function to start server on available port
|
|
214
140
|
function startServer(port) {
|
|
215
|
-
const server = createServer(port);
|
|
216
|
-
|
|
217
141
|
server.listen(port, () => {
|
|
218
142
|
console.log(`🌐 Server running at http://localhost:${port}/`);
|
|
219
143
|
console.log(`📱 Open http://localhost:${port}/ in your browser\n`);
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// Live Reload Client - Always loaded, connects to dev server
|
|
2
|
+
(function() {
|
|
3
|
+
// Only run in development (when dev server is running)
|
|
4
|
+
const isDev = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
|
|
5
|
+
if (!isDev) return;
|
|
6
|
+
|
|
7
|
+
// Create visual indicator
|
|
8
|
+
const indicator = document.createElement('div');
|
|
9
|
+
indicator.id = 'live-reload-indicator';
|
|
10
|
+
indicator.style.cssText = 'position: fixed; bottom: 20px; right: 20px; background: #4caf50; color: white; padding: 8px 16px; border-radius: 20px; font-size: 12px; font-family: monospace; z-index: 9999; box-shadow: 0 2px 8px rgba(0,0,0,0.2); transition: all 0.3s; cursor: pointer;';
|
|
11
|
+
indicator.textContent = '🔌 Connecting...';
|
|
12
|
+
|
|
13
|
+
// Wait for body to be ready
|
|
14
|
+
function addIndicator() {
|
|
15
|
+
if (document.body) {
|
|
16
|
+
document.body.appendChild(indicator);
|
|
17
|
+
} else {
|
|
18
|
+
setTimeout(addIndicator, 100);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
addIndicator();
|
|
22
|
+
|
|
23
|
+
let ws;
|
|
24
|
+
let reconnectAttempts = 0;
|
|
25
|
+
const maxReconnectAttempts = 10;
|
|
26
|
+
const port = window.location.port || 3000;
|
|
27
|
+
|
|
28
|
+
function connect() {
|
|
29
|
+
try {
|
|
30
|
+
console.log('🔌 Connecting to live reload WebSocket...');
|
|
31
|
+
ws = new WebSocket(`ws://localhost:${port}`);
|
|
32
|
+
|
|
33
|
+
ws.onopen = function() {
|
|
34
|
+
console.log('✅ Live reload connected');
|
|
35
|
+
reconnectAttempts = 0;
|
|
36
|
+
indicator.style.background = '#4caf50';
|
|
37
|
+
indicator.textContent = '✅ Live Reload';
|
|
38
|
+
setTimeout(function() {
|
|
39
|
+
indicator.style.opacity = '0.3';
|
|
40
|
+
}, 2000);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
ws.onmessage = function(event) {
|
|
44
|
+
if (event.data === 'reload') {
|
|
45
|
+
console.log('🔄 Changes detected, reloading...');
|
|
46
|
+
indicator.style.opacity = '1';
|
|
47
|
+
indicator.style.background = '#2196f3';
|
|
48
|
+
indicator.textContent = '🔄 Reloading...';
|
|
49
|
+
|
|
50
|
+
// Small delay to show the indicator
|
|
51
|
+
setTimeout(function() {
|
|
52
|
+
window.location.reload();
|
|
53
|
+
}, 100);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
ws.onerror = function(err) {
|
|
58
|
+
console.log('⚠️ WebSocket error:', err);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
ws.onclose = function() {
|
|
62
|
+
console.log('⚠️ Live reload disconnected');
|
|
63
|
+
indicator.style.background = '#ff9800';
|
|
64
|
+
indicator.style.opacity = '1';
|
|
65
|
+
indicator.textContent = '⚠️ Disconnected';
|
|
66
|
+
|
|
67
|
+
// Try to reconnect
|
|
68
|
+
if (reconnectAttempts < maxReconnectAttempts) {
|
|
69
|
+
reconnectAttempts++;
|
|
70
|
+
console.log(`Reconnecting... attempt ${reconnectAttempts}`);
|
|
71
|
+
setTimeout(connect, 2000);
|
|
72
|
+
} else {
|
|
73
|
+
indicator.textContent = '❌ Offline';
|
|
74
|
+
indicator.style.background = '#f44336';
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
} catch (e) {
|
|
78
|
+
console.error('Failed to create WebSocket:', e);
|
|
79
|
+
indicator.style.background = '#f44336';
|
|
80
|
+
indicator.textContent = '❌ Error';
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Start connection when page loads
|
|
85
|
+
if (document.readyState === 'loading') {
|
|
86
|
+
document.addEventListener('DOMContentLoaded', connect);
|
|
87
|
+
} else {
|
|
88
|
+
connect();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Show indicator on hover
|
|
92
|
+
indicator.addEventListener('mouseenter', function() {
|
|
93
|
+
indicator.style.opacity = '1';
|
|
94
|
+
});
|
|
95
|
+
indicator.addEventListener('mouseleave', function() {
|
|
96
|
+
if (indicator.textContent.includes('Live Reload')) {
|
|
97
|
+
indicator.style.opacity = '0.3';
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Click to manually reload
|
|
102
|
+
indicator.addEventListener('click', function() {
|
|
103
|
+
window.location.reload();
|
|
104
|
+
});
|
|
105
|
+
})();
|