u2a 2.1.6 → 2.1.8

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
@@ -1,10 +1,10 @@
1
1
  <div align="center">
2
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;"
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
8
  />
9
9
  <h1 align="center">U2A (URL to App)</h1>
10
10
  </a>
@@ -25,6 +25,7 @@ U2A is a command-line utility that allows you to transform any web URL into a st
25
25
  - 🔄 Automatic favicon retrieval for app icons
26
26
  - 📋 Easy management of created applications
27
27
  - 📊 Detailed logging for troubleshooting
28
+ - 🏷️ Customizable application names and window sizes
28
29
 
29
30
  ## Installation
30
31
 
@@ -39,17 +40,17 @@ npm install -g u2a
39
40
  To create a desktop application from a website:
40
41
 
41
42
  ```bash
42
- u2a create <url>
43
+ u2a create <url> [--name <appName>] [--width <width>] [--height <height>]
43
44
  ```
44
45
 
45
46
  Example:
46
47
  ```bash
47
- u2a create github.com
48
+ u2a create github.com --name "GitHub App" --width 1200 --height 800
48
49
  ```
49
50
 
50
51
  This will:
51
52
  1. Download the website's favicon (if available)
52
- 2. Create an Electron wrapper application
53
+ 2. Create an Electron wrapper application with the specified name and window size
53
54
  3. Add the application to your system menu/launcher
54
55
  4. Track the application in the U2A database
55
56
 
@@ -62,7 +63,7 @@ u2a list
62
63
  ```
63
64
 
64
65
  This will display a list of all created applications with their details:
65
- - Domain name
66
+ - Application name
66
67
  - Original URL
67
68
  - Creation date
68
69
  - Application directory
@@ -72,12 +73,12 @@ This will display a list of all created applications with their details:
72
73
  To remove an application:
73
74
 
74
75
  ```bash
75
- u2a remove <url>
76
+ u2a remove <appName>
76
77
  ```
77
78
 
78
79
  Example:
79
80
  ```bash
80
- u2a remove github.com
81
+ u2a remove "GitHub App"
81
82
  ```
82
83
 
83
84
  This will:
@@ -127,7 +128,3 @@ This project is licensed under the GNU General Public License v3.0 - see the [LI
127
128
  ## Author
128
129
 
129
130
  Created by [Douxx](https://douxx.tech)
130
-
131
- ## Disclaimer
132
-
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.6",
3
+ "version": "2.1.8",
4
4
  "description": "URL to App - Turn any URL into a desktop application",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -11,14 +11,14 @@ const logger = new Logger('create');
11
11
 
12
12
  function createWindowsShortcut(appInfo) {
13
13
  try {
14
- const { domain, appDir, iconPath } = appInfo;
14
+ const { appName, appDir, iconPath } = appInfo;
15
15
  const startMenuPath = path.join(process.env.APPDATA, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'U2A Apps');
16
16
 
17
17
  if (!fs.existsSync(startMenuPath)) {
18
18
  fs.mkdirSync(startMenuPath, { recursive: true });
19
19
  }
20
20
 
21
- const shortcutPath = path.join(startMenuPath, `${domain}.lnk`);
21
+ const shortcutPath = path.join(startMenuPath, `${appName}.lnk`);
22
22
  const targetPath = path.join(appDir, 'node_modules', '.bin', 'electron.cmd');
23
23
  const workingDir = appDir;
24
24
 
@@ -29,11 +29,11 @@ function createWindowsShortcut(appInfo) {
29
29
  $Shortcut.Arguments = "."
30
30
  $Shortcut.WorkingDirectory = "${workingDir.replace(/\\/g, '\\\\')}"
31
31
  $Shortcut.IconLocation = "${iconPath.replace(/\\/g, '\\\\')}"
32
- $Shortcut.Description = "Application Web pour ${domain}"
32
+ $Shortcut.Description = "Application Web pour ${appName}"
33
33
  $Shortcut.Save()
34
34
  `;
35
35
 
36
- const tempScriptPath = path.join(os.tmpdir(), `create_shortcut_${domain}.ps1`);
36
+ const tempScriptPath = path.join(os.tmpdir(), `create_shortcut_${appName}.ps1`);
37
37
  fs.writeFileSync(tempScriptPath, psScript);
38
38
 
39
39
  execSync(`powershell -ExecutionPolicy Bypass -File "${tempScriptPath}"`, {
@@ -53,7 +53,7 @@ function createWindowsShortcut(appInfo) {
53
53
 
54
54
  function createLinuxDesktopEntry(appInfo) {
55
55
  try {
56
- const { domain, url, appDir, iconPath } = appInfo;
56
+ const { appName, url, appDir, iconPath } = appInfo;
57
57
  const appsDir = path.join(os.homedir(), '.local', 'share', 'applications');
58
58
 
59
59
  if (!fs.existsSync(appsDir)) {
@@ -62,7 +62,7 @@ function createLinuxDesktopEntry(appInfo) {
62
62
 
63
63
  const desktopEntry = `[Desktop Entry]
64
64
  Type=Application
65
- Name=${domain}
65
+ Name=${appName}
66
66
  Exec=${path.join(appDir, 'node_modules', '.bin', 'electron')} ${path.join(appDir, 'main.js')}
67
67
  Icon=${iconPath}
68
68
  Comment=Application Web pour ${url}
@@ -70,7 +70,7 @@ Categories=Network;WebBrowser;
70
70
  Terminal=false
71
71
  `;
72
72
 
73
- const desktopFilePath = path.join(appsDir, `u2a-${domain}.desktop`);
73
+ const desktopFilePath = path.join(appsDir, `u2a-${appName}.desktop`);
74
74
  fs.writeFileSync(desktopFilePath, desktopEntry);
75
75
 
76
76
  fs.chmodSync(desktopFilePath, '755');
@@ -85,15 +85,14 @@ Terminal=false
85
85
 
86
86
  function createMacOSApp(appInfo) {
87
87
  try {
88
- const { domain, appDir, iconPath } = appInfo;
88
+ const { appName, appDir, iconPath } = appInfo;
89
89
  const appsDir = path.join(os.homedir(), 'Applications', 'U2A Apps');
90
90
 
91
91
  if (!fs.existsSync(appsDir)) {
92
92
  fs.mkdirSync(appsDir, { recursive: true });
93
93
  }
94
94
 
95
- const appName = `${domain}.app`;
96
- const appPath = path.join(appsDir, appName);
95
+ const appPath = path.join(appsDir, `${appName}.app`);
97
96
  const macOsPath = path.join(appPath, 'Contents', 'MacOS');
98
97
  const resourcesPath = path.join(appPath, 'Contents', 'Resources');
99
98
 
@@ -109,11 +108,11 @@ function createMacOSApp(appInfo) {
109
108
  <key>CFBundleIconFile</key>
110
109
  <string>icon.icns</string>
111
110
  <key>CFBundleIdentifier</key>
112
- <string>com.u2a.${domain}</string>
111
+ <string>com.u2a.${appName.replace(/\s+/g, '-')}</string>
113
112
  <key>CFBundleName</key>
114
- <string>${domain}</string>
113
+ <string>${appName}</string>
115
114
  <key>CFBundleDisplayName</key>
116
- <string>${domain}</string>
115
+ <string>${appName}</string>
117
116
  <key>CFBundlePackageType</key>
118
117
  <string>APPL</string>
119
118
  <key>CFBundleVersion</key>
@@ -143,8 +142,8 @@ cd "${appDir}"
143
142
  }
144
143
  }
145
144
 
146
- function addAppToOS(domain, url, appDir, iconPath) {
147
- const appInfo = { domain, url, appDir, iconPath };
145
+ function addAppToOS(appName, url, appDir, iconPath) {
146
+ const appInfo = { appName, url, appDir, iconPath };
148
147
  let desktopPath = null;
149
148
 
150
149
  if (process.platform === 'win32') {
@@ -160,41 +159,46 @@ function addAppToOS(domain, url, appDir, iconPath) {
160
159
  return desktopPath;
161
160
  }
162
161
 
163
- function removeAppFromOS(domain) {
162
+ function removeAppFromOS(appName) {
164
163
  try {
165
164
  if (process.platform === 'win32') {
166
- const startMenuPath = path.join(process.env.APPDATA, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'U2A Apps', `${domain}.lnk`);
165
+ const startMenuPath = path.join(process.env.APPDATA, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'U2A Apps', `${appName}.lnk`);
167
166
  if (fs.existsSync(startMenuPath)) {
168
167
  fs.unlinkSync(startMenuPath);
169
168
  logger.success(`Shortcut removed from the Start Menu: ${startMenuPath}`);
170
169
  }
171
170
  } else if (process.platform === 'darwin') {
172
- const appPath = path.join(os.homedir(), 'Applications', 'U2A Apps', `${domain}.app`);
171
+ const appPath = path.join(os.homedir(), 'Applications', 'U2A Apps', `${appName}.app`);
173
172
  if (fs.existsSync(appPath)) {
174
173
  fs.rmSync(appPath, { recursive: true, force: true });
175
174
  logger.success(`macOS application removed: ${appPath}`);
176
175
  }
177
176
  } else if (process.platform === 'linux') {
178
- const desktopFilePath = path.join(os.homedir(), '.local', 'share', 'applications', `u2a-${domain}.desktop`);
177
+ const desktopFilePath = path.join(os.homedir(), '.local', 'share', 'applications', `u2a-${appName}.desktop`);
179
178
  if (fs.existsSync(desktopFilePath)) {
180
179
  fs.unlinkSync(desktopFilePath);
181
180
  logger.success(`Linux desktop entry removed: ${desktopFilePath}`);
182
181
  }
183
182
  }
184
183
  } catch (error) {
185
- logger.error(`Error while removing desktop integration for ${domain}`, error);
184
+ logger.error(`Error while removing desktop integration for ${appName}`, error);
186
185
  }
187
186
  }
188
187
 
189
- function generateMainJs(domain, url, iconPath) {
188
+ function generateMainJs(appName, url, iconPath, options = {}) {
189
+ const width = options.width || 1200;
190
+ const height = options.height || 800;
191
+
190
192
  return `
191
193
  const { app, BrowserWindow, Menu, shell } = require('electron');
192
194
  const path = require('path');
193
195
  const fs = require('fs');
194
196
 
195
- const APP_DOMAIN = "${domain}";
197
+ const APP_NAME = "${appName}";
196
198
  const APP_URL = "${url}";
197
199
  const APP_ICON_PATH = "${iconPath.replace(/\\/g, '\\\\')}";
200
+ const WINDOW_WIDTH = ${width};
201
+ const WINDOW_HEIGHT = ${height};
198
202
 
199
203
  let mainWindow;
200
204
  let splashWindow;
@@ -214,7 +218,7 @@ function logAppInfo() {
214
218
  console.log('\\n--------------------------------');
215
219
  console.log(' APPLICATION INFORMATION');
216
220
  console.log('--------------------------------');
217
- console.log(\`Application: \${APP_DOMAIN}\`);
221
+ console.log(\`Application: \${APP_NAME}\`);
218
222
  console.log(\`URL: \${APP_URL}\`);
219
223
  console.log(\`Started at: \${new Date().toLocaleString()}\`);
220
224
  console.log(\`App directory: \${__dirname}\`);
@@ -390,7 +394,7 @@ function createSplashScreen() {
390
394
  </head>
391
395
  <body>
392
396
  <div class="container">
393
- <div class="domain">\${APP_DOMAIN}</div>
397
+ <div class="domain">\${APP_NAME}</div>
394
398
  <div class="spinner"></div>
395
399
  <div id="loading-text" class="loading-text">Loading...</div>
396
400
  <div class="progress-bar">
@@ -413,7 +417,7 @@ function createSplashScreen() {
413
417
  </html>
414
418
  \`;
415
419
 
416
- const splashPath = path.join(app.getPath('temp'), \`\${APP_DOMAIN}-splash.html\`);
420
+ const splashPath = path.join(app.getPath('temp'), \`\${APP_NAME}-splash.html\`);
417
421
  fs.writeFileSync(splashPath, splashHtml);
418
422
 
419
423
  splashWindow.loadFile(splashPath);
@@ -423,12 +427,12 @@ function createSplashScreen() {
423
427
  function createWindow() {
424
428
  logAppInfo();
425
429
 
426
- app.setAppUserModelId(APP_DOMAIN);
430
+ app.setAppUserModelId(APP_NAME);
427
431
 
428
432
  mainWindow = new BrowserWindow({
429
- width: 1200,
430
- height: 800,
431
- title: APP_DOMAIN,
433
+ width: WINDOW_WIDTH,
434
+ height: WINDOW_HEIGHT,
435
+ title: APP_NAME,
432
436
  icon: APP_ICON_PATH,
433
437
  show: false,
434
438
  webPreferences: {
@@ -470,7 +474,7 @@ function createWindow() {
470
474
  });
471
475
 
472
476
  mainWindow.webContents.on('did-start-loading', () => {
473
- updateSplashScreen('Connecting to ' + APP_DOMAIN + '...');
477
+ updateSplashScreen('Connecting to ' + APP_NAME + '...');
474
478
  });
475
479
 
476
480
  mainWindow.webContents.on('did-start-navigation', (event, url) => {
@@ -553,7 +557,7 @@ app.on('activate', () => {
553
557
  `;
554
558
  }
555
559
 
556
- function generatePackageJson(domain, iconPath) {
560
+ function generatePackageJson(appName, iconPath) {
557
561
  const u2aPackagePath = path.resolve(__dirname, '../../package.json');
558
562
 
559
563
  let u2aVersion = '1.0.0';
@@ -566,9 +570,9 @@ function generatePackageJson(domain, iconPath) {
566
570
  }
567
571
 
568
572
  return {
569
- name: `u2a-${domain}`,
573
+ name: `u2a-${appName.replace(/\s+/g, '-')}`,
570
574
  version: u2aVersion,
571
- description: `Web app for ${domain}`,
575
+ description: `Web app for ${appName}`,
572
576
  main: 'main.js',
573
577
  scripts: {
574
578
  start: 'electron .'
@@ -577,45 +581,46 @@ function generatePackageJson(domain, iconPath) {
577
581
  electron: '^22.0.0'
578
582
  },
579
583
  build: {
580
- appId: `com.u2a.${domain.replace(/\./g, '-')}`,
581
- productName: domain,
584
+ appId: `com.u2a.${appName.replace(/\s+/g, '-')}`,
585
+ productName: appName,
582
586
  icon: iconPath
583
587
  }
584
588
  };
585
589
  }
586
590
 
587
- async function createApp(url) {
591
+ async function createApp(url, options) {
588
592
  logger.info(`Creating application for ${url}`);
589
593
 
590
594
  try {
591
595
  url = await normalizeUrl(url);
592
596
  const domain = getDomainName(url);
597
+ const appName = options.name || domain;
593
598
 
594
599
  const db = readDB();
595
- if (db.hasOwnProperty(domain)) {
596
- logger.warn(`Application for ${domain} already exists`);
600
+ if (db.hasOwnProperty(appName)) {
601
+ logger.warn(`Application for ${appName} already exists`);
597
602
  return;
598
603
  }
599
604
 
600
605
  const iconPath = await getFavicon(url);
601
606
 
602
- const appDir = path.join(APPS_DIR, domain);
607
+ const appDir = path.join(APPS_DIR, appName);
603
608
  if (!fs.existsSync(appDir)) {
604
609
  fs.mkdirSync(appDir, { recursive: true });
605
610
  logger.debug(`Directory created: ${appDir}`);
606
611
  }
607
612
 
608
613
  const mainJsPath = path.join(appDir, 'main.js');
609
- const mainJsContent = generateMainJs(domain, url, iconPath);
614
+ const mainJsContent = generateMainJs(appName, url, iconPath, options);
610
615
  fs.writeFileSync(mainJsPath, mainJsContent);
611
616
  logger.debug(`main.js file created`);
612
617
 
613
618
  const packageJsonPath = path.join(appDir, 'package.json');
614
- const packageJsonContent = generatePackageJson(domain, iconPath);
619
+ const packageJsonContent = generatePackageJson(appName, iconPath);
615
620
  fs.writeFileSync(packageJsonPath, JSON.stringify(packageJsonContent, null, 2));
616
621
  logger.debug(`package.json file created`);
617
622
 
618
- logger.info(`Installing dependencies for ${domain}`);
623
+ logger.info(`Installing dependencies for ${appName}`);
619
624
 
620
625
  const installOptions = {
621
626
  cwd: appDir,
@@ -626,17 +631,20 @@ async function createApp(url) {
626
631
  const stdout = execSync('npm install --only=prod', installOptions);
627
632
  logger.debug(`npm install completed: ${stdout.toString().trim()}`);
628
633
 
629
- const desktopPath = addAppToOS(domain, url, appDir, iconPath);
634
+ const desktopPath = addAppToOS(appName, url, appDir, iconPath);
630
635
 
631
636
  const appData = {
632
637
  url,
633
638
  created: new Date().toISOString(),
634
639
  path: appDir,
635
640
  icon: iconPath,
636
- desktopPath
641
+ desktopPath,
642
+ name: options.name,
643
+ width: options.width,
644
+ height: options.height
637
645
  };
638
646
 
639
- db[domain] = appData;
647
+ db[appName] = appData;
640
648
  writeDB(db);
641
649
 
642
650
  logger.success(`Application successfully created for ${url}`);
@@ -8,13 +8,12 @@ const path = require('path');
8
8
 
9
9
  const logger = new Logger('remove');
10
10
 
11
- async function removeApp(url) {
11
+ async function removeApp(appName) {
12
12
  try {
13
- const domain = getDomainName(await normalizeUrl(url));
14
13
  const db = readDB();
15
14
 
16
- if (!db.hasOwnProperty(domain)) {
17
- logger.warn(`The application for ${domain} does not exist`);
15
+ if (!db.hasOwnProperty(appName)) {
16
+ logger.warn(`The application for ${appName} does not exist`);
18
17
  return;
19
18
  }
20
19
 
@@ -22,7 +21,7 @@ async function removeApp(url) {
22
21
  {
23
22
  type: 'confirm',
24
23
  name: 'confirm',
25
- message: `Are you sure you want to remove the application for ${domain}?`,
24
+ message: `Are you sure you want to remove the application for ${appName}?`,
26
25
  default: false
27
26
  }
28
27
  ]);
@@ -32,25 +31,25 @@ async function removeApp(url) {
32
31
  return;
33
32
  }
34
33
 
35
- const appInfo = db[domain];
34
+ const appInfo = db[appName];
36
35
  const appDir = appInfo.path;
37
36
 
38
- removeAppFromOS(domain);
39
- logger.info(`Removing the application ${domain}...`);
37
+ removeAppFromOS(appName);
38
+ logger.info(`Removing the application ${appName}...`);
40
39
 
41
- const iconPath = path.join(APPS_DIR, `${domain}.ico`);
40
+ const iconPath = path.join(APPS_DIR, `${appName}.ico`);
42
41
  if (fs.existsSync(iconPath)) {
43
42
  fs.unlinkSync(iconPath);
44
- logger.success(`Icon for ${domain} removed`);
43
+ logger.success(`Icon for ${appName} removed`);
45
44
  }
46
45
 
47
46
  fs.rmSync(appDir, { recursive: true, force: true });
48
- delete db[domain];
47
+ delete db[appName];
49
48
  writeDB(db);
50
49
 
51
- logger.success(`The application for ${domain} has been successfully removed`);
50
+ logger.success(`The application for ${appName} has been successfully removed`);
52
51
  } catch (error) {
53
- logger.error(`Error removing the application ${url}`, error);
52
+ logger.error(`Error removing the application ${appName}`, error);
54
53
  }
55
54
  }
56
55
 
package/src/index.js CHANGED
@@ -14,10 +14,15 @@ program
14
14
  .description('Convert websites into desktop applications')
15
15
  .version(version);
16
16
 
17
- program
17
+ program
18
18
  .command('create <url>')
19
19
  .description('Create a new application from a URL')
20
- .action(createApp);
20
+ .option('--name <name>', 'Specify the application name')
21
+ .option('--width <width>', 'Specify the window width', parseInt)
22
+ .option('--height <height>', 'Specify the window height', parseInt)
23
+ .action((url, options) => {
24
+ createApp(url, options);
25
+ });
21
26
 
22
27
  program
23
28
  .command('list')