From 227090af162c9d8bc94117ff9463db87a45ea23b Mon Sep 17 00:00:00 2001 From: "Grigorii K. Shartsev" Date: Thu, 19 Feb 2026 20:21:22 +0100 Subject: [PATCH 1/2] build(deps): add mri Signed-off-by: Grigorii K. Shartsev --- package-lock.json | 15 +++++++++++++++ package.json | 1 + 2 files changed, 16 insertions(+) diff --git a/package-lock.json b/package-lock.json index 60cc2c36..db0b4f57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "core-js": "^3.47.0", "electron-squirrel-startup": "^1.0.1", "howler": "^2.2.4", + "mri": "^1.2.0", "pinia": "^3.0.4", "semver": "^7.7.3", "unzip-crx-3": "^0.2.0", @@ -13408,6 +13409,15 @@ "node": ">=10" } }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/ms": { "version": "2.0.0", "license": "MIT" @@ -27341,6 +27351,11 @@ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true }, + "mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==" + }, "ms": { "version": "2.0.0" }, diff --git a/package.json b/package.json index 498e58fc..4b3ab72b 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "core-js": "^3.47.0", "electron-squirrel-startup": "^1.0.1", "howler": "^2.2.4", + "mri": "^1.2.0", "pinia": "^3.0.4", "semver": "^7.7.3", "unzip-crx-3": "^0.2.0", From cc0894f1f5c2afd3e52eff0328aa8803a1eeb9ab Mon Sep 17 00:00:00 2001 From: "Grigorii K. Shartsev" Date: Mon, 9 Feb 2026 12:04:28 +0100 Subject: [PATCH 2/2] feat: add `config --accounts` to preset acounts config Signed-off-by: Grigorii K. Shartsev --- README.md | 30 ++++++++++++++++++++++ src/app/cli.ts | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.js | 15 +++++------ 3 files changed, 106 insertions(+), 8 deletions(-) create mode 100644 src/app/cli.ts diff --git a/README.md b/README.md index 04434c36..7a1cd16c 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,36 @@ However, using portable `zip` distribution, you can have several Nextcloud Talk └── ... ``` +## ⌨️ CLI usage + +### Application flags + +Adjust how the application runs when launching. + +| Flag | Description | +|----------------|-------------------------------------------------------------------------------| +| `--background` | Start minimized to the system tray without a window (used for run at startup) | + +### CLI commands + +Run a command in the app and quit without launching the entire app. + +#### `config` + +Set application configuration. + +| Option | Description | +|----------------------------|--------------------------------------------| +| `--accounts=[user@]server` | Comma-separated list of prefilled accounts | + +Examples: + +```sh +./Nextcloud\ Talk config --accounts=cloud.company.tld +./Nextcloud\ Talk config --accounts='Name Surname@cloud.company.tld' +./Nextcloud\ Talk config --accounts=name@email.tld@company.tld/nextcloud +``` + ## 🛠️ Development Setup 1. Install dependencies diff --git a/src/app/cli.ts b/src/app/cli.ts new file mode 100644 index 00000000..1adf9818 --- /dev/null +++ b/src/app/cli.ts @@ -0,0 +1,69 @@ +/*! + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import type { Argv } from 'mri' + +import { app } from 'electron' +import { setAppConfig } from './AppConfig.ts' + +/** + * Handle CLI usage + * + * @param argv - Parsed by mri CLI arguments + */ +export async function cli(argv: Argv) { + // TODO: Add --help command + + if (!argv._.length) { + return + } + + if (argv._[0] === 'config' && argv._.length === 1) { + await handleConfigCommand(argv) + } else { + console.log('Unknown command:', argv._.join(' ')) + app.exit(1) + } + + app.exit(0) +} + +/** + * Handle "config" CLI command + * + * @param argv - Parsed by mri CLI arguments + */ +async function handleConfigCommand(argv: Argv) { + if (!argv.accounts) { + console.log('No config to set') + app.exit(1) + } + + // --accounts=user@email.tld@server.tld/nextcloud,nextcloud.local + const accounts = argv.accounts.split(',') + + // Validate + for (const account of accounts) { + const atIndex = account.lastIndexOf('@') + const [server, user] = atIndex === -1 + ? [account, ''] + : [account.slice(atIndex + 1), account.slice(0, atIndex)] + try { + new URL(`https://${server}`) + } catch { + console.error(`Invalid server: ${server}`) + app.exit(1) + } + if (user && (!/^[a-zA-Z0-9 _.@\-']{1,64}$/.test(user) || user !== user.trim())) { + console.error(`Invalid user: ${user}`) + app.exit(1) + } + } + + // Set + await setAppConfig('accounts', accounts) + + console.log('Accounts configuration updated') +} diff --git a/src/main.js b/src/main.js index 6520b996..e9ce45dd 100644 --- a/src/main.js +++ b/src/main.js @@ -4,6 +4,7 @@ */ const { app, ipcMain, desktopCapturer, systemPreferences, shell, session } = require('electron') +const { default: mri } = require('mri') const { spawn } = require('node:child_process') const path = require('node:path') const { setupMenu } = require('./app/app.menu.js') @@ -11,6 +12,7 @@ const { loadAppConfig, getAppConfig, setAppConfig } = require('./app/AppConfig.t const { appData } = require('./app/AppData.js') const { registerAppProtocolHandler } = require('./app/appProtocol.ts') const { verifyCertificate, promptCertificateTrust } = require('./app/certificate.service.ts') +const { cli } = require('./app/cli.ts') const { openChromeWebRtcInternals } = require('./app/dev.utils.ts') const { triggerDownloadUrl } = require('./app/downloads.ts') const { setupReleaseNotificationScheduler, checkForUpdate } = require('./app/githubRelease.service.ts') @@ -30,13 +32,7 @@ const { createTalkWindow } = require('./talk/talk.window.js') const { createUpgradeWindow } = require('./upgrade/upgrade.window.ts') const { createWelcomeWindow } = require('./welcome/welcome.window.js') -/** - * Parse command line arguments - */ -const ARGUMENTS = { - // Open Talk window in the background, minimized to the system tray - openInBackground: process.argv.includes('--background'), -} +const argv = mri(process.argv.slice(app.isPackaged ? 1 : 2)) // Electron 36 with Chromium 136 is not compatible with GNOME due to GTK3 with GTK4 mixing // Workaround: force GTK3 @@ -74,6 +70,7 @@ if (require('electron-squirrel-startup')) { * Only one instance is allowed at the same time */ if (!app.requestSingleInstanceLock()) { + console.log('Another instance of the app is already running') app.quit() } @@ -137,6 +134,8 @@ app.whenReady().then(async () => { await loadAppConfig() await runMigrations() + await cli(argv) + applyTheme() initLaunchAtStartupListener() registerAppProtocolHandler() @@ -149,7 +148,7 @@ app.whenReady().then(async () => { } // Open in the background if it is explicitly set, or the app was open at login on macOS - const openInBackground = ARGUMENTS.openInBackground || app.getLoginItemSettings().wasOpenedAtLogin + const openInBackground = argv.background || app.getLoginItemSettings().wasOpenedAtLogin try { await installVueDevtools()