cc4-embedded-system 3.1.4 → 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 +63 -15
- package/dist/public/index.html +102 -5
- package/package.json +2 -2
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,32 +117,66 @@ 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
|
};
|
|
148
|
+
const buildWindowsDialogScript = (dialogLines) => {
|
|
149
|
+
return [
|
|
150
|
+
'Add-Type -AssemblyName System.Windows.Forms',
|
|
151
|
+
'Add-Type -AssemblyName System.Drawing',
|
|
152
|
+
'$screen = [System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea',
|
|
153
|
+
'$owner = New-Object System.Windows.Forms.Form',
|
|
154
|
+
'$owner.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual',
|
|
155
|
+
'$owner.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::None',
|
|
156
|
+
'$owner.ShowInTaskbar = $false',
|
|
157
|
+
'$owner.TopMost = $true',
|
|
158
|
+
'$owner.Width = 1',
|
|
159
|
+
'$owner.Height = 1',
|
|
160
|
+
'$owner.Left = $screen.Left + [int]($screen.Width / 2)',
|
|
161
|
+
'$owner.Top = $screen.Top + [int]($screen.Height / 2)',
|
|
162
|
+
'$owner.Opacity = 0',
|
|
163
|
+
'$null = $owner.Show()',
|
|
164
|
+
'$null = $owner.Activate()',
|
|
165
|
+
'$owner.BringToFront()',
|
|
166
|
+
'[System.Windows.Forms.Application]::DoEvents()',
|
|
167
|
+
...dialogLines,
|
|
168
|
+
'$owner.Close()',
|
|
169
|
+
'$owner.Dispose()'
|
|
170
|
+
].join('\n');
|
|
171
|
+
};
|
|
124
172
|
app.get('/api/version', (_req, res) => {
|
|
125
173
|
cancelShutdown();
|
|
126
174
|
res.json({ version: getPackageVersion() });
|
|
127
175
|
});
|
|
128
176
|
app.post('/api/shutdown', (_req, res) => {
|
|
129
177
|
res.json({ success: true });
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
console.log('👋 Window closed, shutting down ...');
|
|
133
|
-
process.exit(0);
|
|
134
|
-
}, 2000);
|
|
135
|
-
}
|
|
178
|
+
closeBrowseProcess();
|
|
179
|
+
scheduleShutdown();
|
|
136
180
|
});
|
|
137
181
|
app.post('/api/cancel-shutdown', (_req, res) => {
|
|
138
182
|
cancelShutdown();
|
|
@@ -198,31 +242,35 @@ app.post('/api/change-port', (req, res) => {
|
|
|
198
242
|
});
|
|
199
243
|
app.get('/api/browse', async (req, res) => {
|
|
200
244
|
cancelShutdown();
|
|
245
|
+
res.on('close', () => {
|
|
246
|
+
if (!res.writableEnded) {
|
|
247
|
+
closeBrowseProcess();
|
|
248
|
+
scheduleShutdown();
|
|
249
|
+
}
|
|
250
|
+
});
|
|
201
251
|
const isDir = req.query.type === 'dir';
|
|
202
252
|
const platform = os.platform();
|
|
203
253
|
try {
|
|
204
254
|
let result = '';
|
|
205
255
|
if (platform === 'win32') {
|
|
206
256
|
const script = isDir
|
|
207
|
-
? [
|
|
208
|
-
'Add-Type -AssemblyName System.Windows.Forms',
|
|
257
|
+
? buildWindowsDialogScript([
|
|
209
258
|
'$dialog = New-Object System.Windows.Forms.FolderBrowserDialog',
|
|
210
259
|
'$dialog.Description = "Select Input Directory"',
|
|
211
|
-
'if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {',
|
|
260
|
+
'if ($dialog.ShowDialog($owner) -eq [System.Windows.Forms.DialogResult]::OK) {',
|
|
212
261
|
' $dialog.SelectedPath',
|
|
213
262
|
'}'
|
|
214
|
-
]
|
|
215
|
-
: [
|
|
216
|
-
'Add-Type -AssemblyName System.Windows.Forms',
|
|
263
|
+
])
|
|
264
|
+
: buildWindowsDialogScript([
|
|
217
265
|
'$dialog = New-Object System.Windows.Forms.SaveFileDialog',
|
|
218
266
|
'$dialog.Filter = "C Files (*.c)|*.c|All Files (*.*)|*.*"',
|
|
219
267
|
'$dialog.DefaultExt = "c"',
|
|
220
268
|
'$dialog.AddExtension = $true',
|
|
221
269
|
'$dialog.OverwritePrompt = $true',
|
|
222
|
-
'if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {',
|
|
270
|
+
'if ($dialog.ShowDialog($owner) -eq [System.Windows.Forms.DialogResult]::OK) {',
|
|
223
271
|
' $dialog.FileName',
|
|
224
272
|
'}'
|
|
225
|
-
]
|
|
273
|
+
]);
|
|
226
274
|
result = await runDialogCommand('powershell.exe', buildPowerShellArgs(script));
|
|
227
275
|
}
|
|
228
276
|
else if (platform === 'darwin') {
|
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>
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cc4-embedded-system",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.6",
|
|
4
4
|
"description": "A modern typescript version of makefsdata based on html-minifier-next and lwIP v1.3.1",
|
|
5
5
|
"bin": {
|
|
6
|
-
"cc4es": "
|
|
6
|
+
"cc4es": "dist/gui.js"
|
|
7
7
|
},
|
|
8
8
|
"type": "module",
|
|
9
9
|
"scripts": {
|