u2a 2.1.0 → 2.1.5
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 +12 -2
- package/package.json +9 -2
- package/src/commands/create.js +273 -6
package/README.md
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<a href="#" style="display: block; text-align: center;">
|
|
3
|
+
<img
|
|
4
|
+
alt="Image of this repo"
|
|
5
|
+
src="https://togp.xyz?owner=douxxtech&repo=urltoapp&theme=json-dark-all&cache=false"
|
|
6
|
+
type="image/svg+xml"
|
|
7
|
+
style="border-radius: 20px; overflow: hidden;"
|
|
8
|
+
/>
|
|
9
|
+
<h1 align="center">U2A (URL to App)</h1>
|
|
10
|
+
</a>
|
|
11
|
+
</div>
|
|
2
12
|
|
|
3
13
|

|
|
4
14
|

|
|
@@ -120,4 +130,4 @@ Created by [Douxx](https://douxx.tech)
|
|
|
120
130
|
|
|
121
131
|
## Disclaimer
|
|
122
132
|
|
|
123
|
-
This tool is for personal use only. Always respect the terms of service of websites you convert to desktop apps.
|
|
133
|
+
This tool is for personal use only. Always respect the terms of service of websites you convert to desktop apps.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "u2a",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
4
4
|
"description": "URL to App - Turn any URL into a desktop application",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -28,5 +28,12 @@
|
|
|
28
28
|
"inquirer": "^8.2.5",
|
|
29
29
|
"open": "^8.4.0"
|
|
30
30
|
},
|
|
31
|
-
"homepage": "https://
|
|
31
|
+
"homepage": "https://urltoapp.xyz",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/douxxtech/urltoapp"
|
|
35
|
+
},
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/douxxtech/urltoapp/issues"
|
|
38
|
+
}
|
|
32
39
|
}
|
package/src/commands/create.js
CHANGED
|
@@ -200,11 +200,13 @@ const APP_URL = "${url}";
|
|
|
200
200
|
const APP_ICON_PATH = "${iconPath.replace(/\\/g, '\\\\')}";
|
|
201
201
|
|
|
202
202
|
let mainWindow;
|
|
203
|
+
let splashWindow;
|
|
204
|
+
let loadErrors = [];
|
|
203
205
|
|
|
204
206
|
function logAppInfo() {
|
|
205
207
|
const packageJsonPath = path.join(__dirname, 'package.json');
|
|
206
208
|
let packageInfo = {};
|
|
207
|
-
|
|
209
|
+
|
|
208
210
|
try {
|
|
209
211
|
const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf-8');
|
|
210
212
|
packageInfo = JSON.parse(packageJsonContent);
|
|
@@ -220,25 +222,212 @@ function logAppInfo() {
|
|
|
220
222
|
console.log(\`Started at: \${new Date().toLocaleString()}\`);
|
|
221
223
|
console.log(\`App directory: \${__dirname}\`);
|
|
222
224
|
console.log(\`Icon path: \${APP_ICON_PATH}\`);
|
|
223
|
-
|
|
225
|
+
|
|
224
226
|
console.log('\\n PACKAGE INFO:');
|
|
225
227
|
console.log(\` - Name: \${packageInfo.name || 'N/A'}\`);
|
|
226
228
|
console.log(\` - Version: \${packageInfo.version || 'N/A'}\`);
|
|
227
229
|
console.log(\` - Description: \${packageInfo.description || 'N/A'}\`);
|
|
228
230
|
console.log(\` - Electron version: \${packageInfo.dependencies?.electron || 'N/A'}\`);
|
|
229
|
-
|
|
231
|
+
|
|
230
232
|
if (packageInfo.build) {
|
|
231
233
|
console.log('\\n BUILD CONFIG:');
|
|
232
234
|
console.log(\` - App ID: \${packageInfo.build.appId || 'N/A'}\`);
|
|
233
235
|
console.log(\` - Product Name: \${packageInfo.build.productName || 'N/A'}\`);
|
|
234
236
|
}
|
|
235
|
-
|
|
237
|
+
|
|
236
238
|
console.log('--------------------------------\\n');
|
|
237
239
|
}
|
|
238
240
|
|
|
241
|
+
function updateSplashScreen(message, isError = false) {
|
|
242
|
+
if (splashWindow && !splashWindow.isDestroyed()) {
|
|
243
|
+
const script = \`
|
|
244
|
+
document.getElementById('loading-text').innerText = "\${message.replace(/"/g, '\\"')}";
|
|
245
|
+
\${isError ?
|
|
246
|
+
\`document.getElementById('loading-text').classList.add('error');
|
|
247
|
+
document.getElementById('errors-container').style.display = 'block';
|
|
248
|
+
const errorItem = document.createElement('li');
|
|
249
|
+
errorItem.textContent = "\${message.replace(/"/g, '\\"')}";
|
|
250
|
+
document.getElementById('errors-list').appendChild(errorItem);\`
|
|
251
|
+
: ''}
|
|
252
|
+
\`;
|
|
253
|
+
splashWindow.webContents.executeJavaScript(script).catch(err => console.error('Failed to update splash screen:', err));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function createSplashScreen() {
|
|
258
|
+
splashWindow = new BrowserWindow({
|
|
259
|
+
width: 550,
|
|
260
|
+
height: 400,
|
|
261
|
+
transparent: true,
|
|
262
|
+
frame: false,
|
|
263
|
+
alwaysOnTop: true,
|
|
264
|
+
icon: APP_ICON_PATH,
|
|
265
|
+
webPreferences: {
|
|
266
|
+
nodeIntegration: false,
|
|
267
|
+
contextIsolation: true
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const splashHtml = \`
|
|
272
|
+
<!DOCTYPE html>
|
|
273
|
+
<html>
|
|
274
|
+
<head>
|
|
275
|
+
<meta charset="UTF-8">
|
|
276
|
+
<title>Loading...</title>
|
|
277
|
+
<style>
|
|
278
|
+
body {
|
|
279
|
+
margin: 0;
|
|
280
|
+
padding: 0;
|
|
281
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
282
|
+
background-color: var(--bg-primary, #0f172a);
|
|
283
|
+
color: var(--text-primary, #f8fafc);
|
|
284
|
+
height: 100vh;
|
|
285
|
+
display: flex;
|
|
286
|
+
flex-direction: column;
|
|
287
|
+
justify-content: center;
|
|
288
|
+
align-items: center;
|
|
289
|
+
overflow: hidden;
|
|
290
|
+
border-radius: 20px;
|
|
291
|
+
overflow: hidden;
|
|
292
|
+
box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.2);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.container {
|
|
296
|
+
text-align: center;
|
|
297
|
+
width: 90%;
|
|
298
|
+
max-width: 500px;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.domain {
|
|
302
|
+
font-size: 24px;
|
|
303
|
+
margin-bottom: 20px;
|
|
304
|
+
color: var(--primary, #2563eb);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.spinner {
|
|
308
|
+
width: 50px;
|
|
309
|
+
height: 50px;
|
|
310
|
+
border: 5px solid rgba(37, 99, 235, 0.2);
|
|
311
|
+
border-radius: 50%;
|
|
312
|
+
border-top-color: var(--primary, #2563eb);
|
|
313
|
+
animation: spin 1s ease-in-out infinite;
|
|
314
|
+
margin: 0 auto 20px;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
@keyframes spin {
|
|
318
|
+
to { transform: rotate(360deg); }
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.loading-text {
|
|
322
|
+
font-size: 16px;
|
|
323
|
+
color: var(--text-secondary, #cbd5e1);
|
|
324
|
+
margin-top: 15px;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.loading-text.error {
|
|
328
|
+
color: var(--danger, #ef4444);
|
|
329
|
+
font-weight: bold;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.progress-bar {
|
|
333
|
+
width: 100%;
|
|
334
|
+
height: 4px;
|
|
335
|
+
background-color: var(--bg-secondary, #1e293b);
|
|
336
|
+
border-radius: 2px;
|
|
337
|
+
overflow: hidden;
|
|
338
|
+
margin-top: 15px;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.progress {
|
|
342
|
+
height: 100%;
|
|
343
|
+
width: 0%;
|
|
344
|
+
background-color: var(--primary, #2563eb);
|
|
345
|
+
animation: progress 3s ease-in-out infinite;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
@keyframes progress {
|
|
349
|
+
0% { width: 0%; }
|
|
350
|
+
50% { width: 70%; }
|
|
351
|
+
100% { width: 100%; }
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
#errors-container {
|
|
355
|
+
display: none;
|
|
356
|
+
margin-top: 20px;
|
|
357
|
+
background-color: rgba(239, 68, 68, 0.1);
|
|
358
|
+
border-left: 3px solid var(--danger, #ef4444);
|
|
359
|
+
padding: 10px;
|
|
360
|
+
border-radius: 4px;
|
|
361
|
+
text-align: left;
|
|
362
|
+
max-height: 150px;
|
|
363
|
+
overflow-y: auto;
|
|
364
|
+
width: 100%;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
#errors-list {
|
|
368
|
+
margin: 0;
|
|
369
|
+
padding-left: 20px;
|
|
370
|
+
color: var(--danger, #ef4444);
|
|
371
|
+
font-size: 14px;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
#errors-list li {
|
|
375
|
+
margin-bottom: 5px;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.retry-button {
|
|
379
|
+
background-color: var(--primary, #2563eb);
|
|
380
|
+
color: white;
|
|
381
|
+
border: none;
|
|
382
|
+
padding: 8px 16px;
|
|
383
|
+
border-radius: 4px;
|
|
384
|
+
margin-top: 15px;
|
|
385
|
+
cursor: pointer;
|
|
386
|
+
font-weight: bold;
|
|
387
|
+
transition: background-color 0.2s;
|
|
388
|
+
display: none;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.retry-button:hover {
|
|
392
|
+
background-color: var(--primary-dark, #1d4ed8);
|
|
393
|
+
}
|
|
394
|
+
</style>
|
|
395
|
+
</head>
|
|
396
|
+
<body>
|
|
397
|
+
<div class="container">
|
|
398
|
+
<div class="domain">\${APP_DOMAIN}</div>
|
|
399
|
+
<div class="spinner"></div>
|
|
400
|
+
<div id="loading-text" class="loading-text">Loading...</div>
|
|
401
|
+
<div class="progress-bar">
|
|
402
|
+
<div class="progress"></div>
|
|
403
|
+
</div>
|
|
404
|
+
<div id="errors-container">
|
|
405
|
+
<h3 style="margin-top: 0; color: var(--danger, #ef4444);">Errors detected:</h3>
|
|
406
|
+
<ul id="errors-list"></ul>
|
|
407
|
+
</div>
|
|
408
|
+
<button id="retry-button" class="retry-button" onclick="window.location.reload()">Retry</button>
|
|
409
|
+
</div>
|
|
410
|
+
<script>
|
|
411
|
+
window.showRetryButton = function() {
|
|
412
|
+
document.getElementById('retry-button').style.display = 'inline-block';
|
|
413
|
+
document.querySelector('.spinner').style.animationPlayState = 'paused';
|
|
414
|
+
document.querySelector('.progress').style.animationPlayState = 'paused';
|
|
415
|
+
}
|
|
416
|
+
</script>
|
|
417
|
+
</body>
|
|
418
|
+
</html>
|
|
419
|
+
\`;
|
|
420
|
+
|
|
421
|
+
const splashPath = path.join(app.getPath('temp'), \`\${APP_DOMAIN}-splash.html\`);
|
|
422
|
+
fs.writeFileSync(splashPath, splashHtml);
|
|
423
|
+
|
|
424
|
+
splashWindow.loadFile(splashPath);
|
|
425
|
+
splashWindow.center();
|
|
426
|
+
}
|
|
427
|
+
|
|
239
428
|
function createWindow() {
|
|
240
429
|
logAppInfo();
|
|
241
|
-
|
|
430
|
+
|
|
242
431
|
app.setAppUserModelId(APP_DOMAIN);
|
|
243
432
|
|
|
244
433
|
mainWindow = new BrowserWindow({
|
|
@@ -246,12 +435,74 @@ function createWindow() {
|
|
|
246
435
|
height: 800,
|
|
247
436
|
title: APP_DOMAIN,
|
|
248
437
|
icon: APP_ICON_PATH,
|
|
438
|
+
show: false,
|
|
249
439
|
webPreferences: {
|
|
250
440
|
nodeIntegration: false,
|
|
251
441
|
contextIsolation: true
|
|
252
442
|
}
|
|
253
443
|
});
|
|
254
444
|
|
|
445
|
+
mainWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription, validatedURL, isMainFrame) => {
|
|
446
|
+
const errorMessage = \`Load error (\${errorCode}): \${errorDescription}\`;
|
|
447
|
+
console.error(errorMessage);
|
|
448
|
+
loadErrors.push(errorMessage);
|
|
449
|
+
|
|
450
|
+
updateSplashScreen(errorMessage, true);
|
|
451
|
+
|
|
452
|
+
if (isMainFrame) {
|
|
453
|
+
splashWindow.webContents.executeJavaScript('window.showRetryButton()').catch(err => {
|
|
454
|
+
console.error('Failed to show retry button:', err);
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
mainWindow.webContents.on('certificate-error', (event, url, error, certificate, callback) => {
|
|
460
|
+
event.preventDefault();
|
|
461
|
+
const errorMessage = \`Certificate error: \${error}\`;
|
|
462
|
+
console.error(errorMessage);
|
|
463
|
+
loadErrors.push(errorMessage);
|
|
464
|
+
|
|
465
|
+
updateSplashScreen(errorMessage, true);
|
|
466
|
+
callback(false);
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
mainWindow.webContents.on('console-message', (event, level, message, line, sourceId) => {
|
|
470
|
+
if (level === 2) {
|
|
471
|
+
const errorMessage = \`Console: \${message} (line \${line})\`;
|
|
472
|
+
console.error(errorMessage);
|
|
473
|
+
loadErrors.push(errorMessage);
|
|
474
|
+
|
|
475
|
+
updateSplashScreen('JavaScript error detected', true);
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
mainWindow.webContents.on('did-start-loading', () => {
|
|
480
|
+
updateSplashScreen('Connecting to ' + APP_DOMAIN + '...');
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
mainWindow.webContents.on('did-start-navigation', (event, url) => {
|
|
484
|
+
updateSplashScreen('Navigating to ' + new URL(url).host + '...');
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
mainWindow.webContents.on('dom-ready', () => {
|
|
488
|
+
updateSplashScreen('DOM ready, loading resources...');
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
mainWindow.webContents.on('did-finish-load', () => {
|
|
492
|
+
updateSplashScreen('Loading complete!');
|
|
493
|
+
|
|
494
|
+
setTimeout(() => {
|
|
495
|
+
if (splashWindow && !splashWindow.isDestroyed()) {
|
|
496
|
+
splashWindow.close();
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (!mainWindow.isVisible()) {
|
|
500
|
+
mainWindow.show();
|
|
501
|
+
mainWindow.focus();
|
|
502
|
+
}
|
|
503
|
+
}, 500);
|
|
504
|
+
});
|
|
505
|
+
|
|
255
506
|
mainWindow.loadURL(APP_URL);
|
|
256
507
|
|
|
257
508
|
mainWindow.webContents.on('context-menu', (e, params) => {
|
|
@@ -272,13 +523,28 @@ function createWindow() {
|
|
|
272
523
|
|
|
273
524
|
const menu = Menu.buildFromTemplate(template);
|
|
274
525
|
Menu.setApplicationMenu(menu);
|
|
526
|
+
|
|
527
|
+
setTimeout(() => {
|
|
528
|
+
if (splashWindow && !splashWindow.isDestroyed()) {
|
|
529
|
+
if (loadErrors.length === 0) {
|
|
530
|
+
splashWindow.close();
|
|
531
|
+
if (!mainWindow.isVisible()) {
|
|
532
|
+
mainWindow.show();
|
|
533
|
+
mainWindow.focus();
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}, 15000);
|
|
275
538
|
}
|
|
276
539
|
|
|
277
540
|
if (process.platform === 'win32') {
|
|
278
541
|
app.setAppUserModelId(app.name);
|
|
279
542
|
}
|
|
280
543
|
|
|
281
|
-
app.whenReady().then(
|
|
544
|
+
app.whenReady().then(() => {
|
|
545
|
+
createSplashScreen();
|
|
546
|
+
createWindow();
|
|
547
|
+
});
|
|
282
548
|
|
|
283
549
|
app.on('window-all-closed', () => {
|
|
284
550
|
if (process.platform !== 'darwin') {
|
|
@@ -295,6 +561,7 @@ app.on('activate', () => {
|
|
|
295
561
|
}
|
|
296
562
|
|
|
297
563
|
|
|
564
|
+
|
|
298
565
|
function generatePackageJson(domain, iconPath) {
|
|
299
566
|
const u2aPackagePath = path.resolve(__dirname, '../../package.json');
|
|
300
567
|
|