cc4-embedded-system 3.1.5 → 3.1.6
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/README.md +6 -2
- package/dist/gui.js +33 -7
- package/dist/public/index.html +102 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
- This version based on [html-minifier-next](https://github.com/j9t/html-minifier-next) and rewrite [lwIP makefsdata](https://github.com/m-labs/lwip/tree/master/src/apps/httpd/makefsdata), run on localhost, default port: ```3000```.
|
|
3
3
|
- This tool is deployed on [npm package](https://www.npmjs.com/package/cc4-embedded-system).
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|
|
|
7
7
|
## Structure
|
|
8
8
|
```text
|
|
@@ -14,7 +14,7 @@ CC4EmbeddedSystem/
|
|
|
14
14
|
├── public/
|
|
15
15
|
│ └── index.html // Web GUI dashboard
|
|
16
16
|
├── Sereenshot/
|
|
17
|
-
│ └── v3.1.
|
|
17
|
+
│ └── v3.1.6.png // Demo image
|
|
18
18
|
├── node_modules/ // Required submodules during development
|
|
19
19
|
├── dist/ // Compiled JavaScript output (Auto-generated)
|
|
20
20
|
├── package.json // Project configuration & dependencies
|
|
@@ -33,6 +33,10 @@ CC4EmbeddedSystem/
|
|
|
33
33
|
```bash
|
|
34
34
|
npx cc4-embedded-system
|
|
35
35
|
```
|
|
36
|
+
- Specific port set during initialization
|
|
37
|
+
```bash
|
|
38
|
+
cc4es --port 3002
|
|
39
|
+
```
|
|
36
40
|
### Development
|
|
37
41
|
```bash
|
|
38
42
|
# build
|
package/dist/gui.js
CHANGED
|
@@ -83,6 +83,7 @@ if (portArgIndex !== -1 && process.argv[portArgIndex + 1]) {
|
|
|
83
83
|
if (!isNaN(parsedPort))
|
|
84
84
|
PORT = parsedPort;
|
|
85
85
|
}
|
|
86
|
+
let activeBrowseProcess = null;
|
|
86
87
|
let server;
|
|
87
88
|
let shutdownTimer = null;
|
|
88
89
|
app.use(express.json());
|
|
@@ -98,6 +99,15 @@ const cancelShutdown = () => {
|
|
|
98
99
|
// console.log('🛡️ Shutdown cancelled (keep alive)');
|
|
99
100
|
}
|
|
100
101
|
};
|
|
102
|
+
const scheduleShutdown = () => {
|
|
103
|
+
if (!shutdownTimer) {
|
|
104
|
+
shutdownTimer = setTimeout(() => {
|
|
105
|
+
closeBrowseProcess();
|
|
106
|
+
console.log('👋 Window closed, shutting down ...');
|
|
107
|
+
process.exit(0);
|
|
108
|
+
}, 2000);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
101
111
|
const buildPowerShellArgs = (script) => {
|
|
102
112
|
const encodedCommand = Buffer.from(script, 'utf16le').toString('base64');
|
|
103
113
|
return [
|
|
@@ -107,18 +117,32 @@ const buildPowerShellArgs = (script) => {
|
|
|
107
117
|
encodedCommand
|
|
108
118
|
];
|
|
109
119
|
};
|
|
120
|
+
const closeBrowseProcess = () => {
|
|
121
|
+
if (activeBrowseProcess && !activeBrowseProcess.killed) {
|
|
122
|
+
try {
|
|
123
|
+
activeBrowseProcess.kill();
|
|
124
|
+
}
|
|
125
|
+
catch (e) {
|
|
126
|
+
// ignore child cleanup error
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
activeBrowseProcess = null;
|
|
130
|
+
};
|
|
110
131
|
const runDialogCommand = (command, args) => {
|
|
111
132
|
return new Promise((resolve, reject) => {
|
|
112
|
-
execFile(command, args, {
|
|
133
|
+
const child = execFile(command, args, {
|
|
113
134
|
windowsHide: false,
|
|
114
135
|
maxBuffer: 1024 * 1024
|
|
115
136
|
}, (error, stdout, stderr) => {
|
|
137
|
+
if (activeBrowseProcess === child)
|
|
138
|
+
activeBrowseProcess = null;
|
|
116
139
|
if (error) {
|
|
117
140
|
reject(new Error(stderr.trim() || error.message));
|
|
118
141
|
return;
|
|
119
142
|
}
|
|
120
143
|
resolve(stdout.trim());
|
|
121
144
|
});
|
|
145
|
+
activeBrowseProcess = child;
|
|
122
146
|
});
|
|
123
147
|
};
|
|
124
148
|
const buildWindowsDialogScript = (dialogLines) => {
|
|
@@ -151,12 +175,8 @@ app.get('/api/version', (_req, res) => {
|
|
|
151
175
|
});
|
|
152
176
|
app.post('/api/shutdown', (_req, res) => {
|
|
153
177
|
res.json({ success: true });
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
console.log('👋 Window closed, shutting down ...');
|
|
157
|
-
process.exit(0);
|
|
158
|
-
}, 2000);
|
|
159
|
-
}
|
|
178
|
+
closeBrowseProcess();
|
|
179
|
+
scheduleShutdown();
|
|
160
180
|
});
|
|
161
181
|
app.post('/api/cancel-shutdown', (_req, res) => {
|
|
162
182
|
cancelShutdown();
|
|
@@ -222,6 +242,12 @@ app.post('/api/change-port', (req, res) => {
|
|
|
222
242
|
});
|
|
223
243
|
app.get('/api/browse', async (req, res) => {
|
|
224
244
|
cancelShutdown();
|
|
245
|
+
res.on('close', () => {
|
|
246
|
+
if (!res.writableEnded) {
|
|
247
|
+
closeBrowseProcess();
|
|
248
|
+
scheduleShutdown();
|
|
249
|
+
}
|
|
250
|
+
});
|
|
225
251
|
const isDir = req.query.type === 'dir';
|
|
226
252
|
const platform = os.platform();
|
|
227
253
|
try {
|
package/dist/public/index.html
CHANGED
|
@@ -110,23 +110,99 @@
|
|
|
110
110
|
<button onclick="startBuild()">Generate C Code</button>
|
|
111
111
|
</div>
|
|
112
112
|
|
|
113
|
+
<style>
|
|
114
|
+
body.browse-locked .container {
|
|
115
|
+
pointer-events: none;
|
|
116
|
+
user-select: none;
|
|
117
|
+
filter: blur(1px);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
#browseOverlay {
|
|
121
|
+
position: fixed;
|
|
122
|
+
inset: 0;
|
|
123
|
+
display: none;
|
|
124
|
+
align-items: center;
|
|
125
|
+
justify-content: center;
|
|
126
|
+
background: rgba(0, 0, 0, 0.55);
|
|
127
|
+
z-index: 9999;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
body.browse-locked #browseOverlay {
|
|
131
|
+
display: flex;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.browse-overlay-card {
|
|
135
|
+
background: #252526;
|
|
136
|
+
color: #d4d4d4;
|
|
137
|
+
border: 1px solid #555;
|
|
138
|
+
border-radius: 8px;
|
|
139
|
+
padding: 20px 24px;
|
|
140
|
+
width: min(420px, calc(100vw - 40px));
|
|
141
|
+
text-align: center;
|
|
142
|
+
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.45);
|
|
143
|
+
font-family: 'Consolas', monospace;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.browse-overlay-title {
|
|
147
|
+
color: #9cdcfe;
|
|
148
|
+
font-size: 16px;
|
|
149
|
+
font-weight: bold;
|
|
150
|
+
margin-bottom: 10px;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.browse-overlay-text {
|
|
154
|
+
color: #cccccc;
|
|
155
|
+
font-size: 14px;
|
|
156
|
+
line-height: 1.6;
|
|
157
|
+
}
|
|
158
|
+
</style>
|
|
159
|
+
|
|
113
160
|
<script>
|
|
114
161
|
let isRedirecting = false;
|
|
162
|
+
let isBrowsing = false;
|
|
163
|
+
let isBrowsePending = false;
|
|
115
164
|
document.getElementById('guiPort').value = window.location.port || '80';
|
|
116
165
|
|
|
166
|
+
function setBrowseState(active) {
|
|
167
|
+
isBrowsing = active;
|
|
168
|
+
document.body.classList.toggle('browse-locked', active);
|
|
169
|
+
}
|
|
170
|
+
|
|
117
171
|
async function handleBrowse(targetId, type) {
|
|
172
|
+
if (isBrowsing || isBrowsePending) return;
|
|
173
|
+
|
|
174
|
+
isBrowsePending = true;
|
|
175
|
+
|
|
118
176
|
try {
|
|
119
177
|
const res = await fetch(`/api/browse?type=${type}`);
|
|
120
178
|
const data = await res.json();
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
179
|
+
|
|
180
|
+
if (data.success && data.path) document.getElementById(targetId).value = data.path;
|
|
124
181
|
}
|
|
125
182
|
catch (e) {
|
|
126
183
|
console.error('Failed to open system dialog', e);
|
|
127
184
|
}
|
|
185
|
+
finally {
|
|
186
|
+
isBrowsePending = false;
|
|
187
|
+
setBrowseState(false);
|
|
188
|
+
}
|
|
128
189
|
}
|
|
129
190
|
|
|
191
|
+
window.addEventListener('blur', () => {
|
|
192
|
+
if (isBrowsePending) setBrowseState(true);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
window.addEventListener('keydown', (event) => {
|
|
197
|
+
const isReloadKey = event.key === 'F5';
|
|
198
|
+
const isRefreshShortcut = (event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'r';
|
|
199
|
+
|
|
200
|
+
if (isBrowsing && (isReloadKey || isRefreshShortcut)) {
|
|
201
|
+
event.preventDefault();
|
|
202
|
+
event.stopPropagation();
|
|
203
|
+
}
|
|
204
|
+
}, true);
|
|
205
|
+
|
|
130
206
|
window.addEventListener('DOMContentLoaded', async () => {
|
|
131
207
|
await fetch('/api/cancel-shutdown', { method: 'POST' });
|
|
132
208
|
try {
|
|
@@ -211,10 +287,31 @@
|
|
|
211
287
|
alert('❌ Error: ' + result.message);
|
|
212
288
|
}
|
|
213
289
|
}
|
|
214
|
-
|
|
215
|
-
|
|
290
|
+
|
|
291
|
+
function notifyShutdown() {
|
|
216
292
|
if (! isRedirecting) navigator.sendBeacon('/api/shutdown');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
window.addEventListener('beforeunload', (event) => {
|
|
296
|
+
if (isBrowsing || isBrowsePending) {
|
|
297
|
+
event.preventDefault();
|
|
298
|
+
event.returnValue = '';
|
|
299
|
+
return '';
|
|
300
|
+
}
|
|
217
301
|
});
|
|
302
|
+
|
|
303
|
+
window.addEventListener('pagehide', notifyShutdown);
|
|
304
|
+
window.addEventListener('unload', notifyShutdown);
|
|
218
305
|
</script>
|
|
306
|
+
<div id="browseOverlay">
|
|
307
|
+
<div class="browse-overlay-card">
|
|
308
|
+
<div class="browse-overlay-title">Browse Dialog Open</div>
|
|
309
|
+
<div class="browse-overlay-text">
|
|
310
|
+
Please finish the system browse dialog first.<br>
|
|
311
|
+
Refresh and page navigation are temporarily disabled.
|
|
312
|
+
</div>
|
|
313
|
+
</div>
|
|
314
|
+
</div>
|
|
315
|
+
|
|
219
316
|
</body>
|
|
220
317
|
</html>
|