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 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
- ![](./Screenshot/v3.1.1.png)
5
+ ![](./Screenshot/v3.1.6.png)
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.1.png // Demo image
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
- if (!shutdownTimer) {
131
- shutdownTimer = setTimeout(() => {
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
- ].join('\n')
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
- ].join('\n');
273
+ ]);
226
274
  result = await runDialogCommand('powershell.exe', buildPowerShellArgs(script));
227
275
  }
228
276
  else if (platform === 'darwin') {
@@ -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
- if (data.success && data.path) {
122
- document.getElementById(targetId).value = data.path;
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
- window.addEventListener('beforeunload', () => {
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.4",
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": "./dist/gui.js"
6
+ "cc4es": "dist/gui.js"
7
7
  },
8
8
  "type": "module",
9
9
  "scripts": {