Skip to content

Commit f22b0b0

Browse files
feat(launcher): periodic updates, startup maximize, bigger editor, iOS force-touch (#112)
* refactor: rename BotBrowserConsole to BotBrowserLauncher * feat: add kernel management, proxy management, and setup scripts * fix: enable macOS native Edit menu for Cmd+C/V/X/A keyboard shortcuts * chore: upgrade Neutralino runtime to 6.4.0 * chore: remove Neutralino binaries from repo, download via neu update * fix: improve macOS clipboard support with JS fallback for Cmd+V paste * fix: expand macOS clipboard fallback to Cmd+C/X, auto-download Neutralino via postinstall * chore: upgrade Neutralino to 6.5.0 and simplify setup scripts * fix: fix PowerShell setup script ZIP download corruption * feat: enhance proxy workflow with IP check, quick change, and reusable input component * fix: correct checkbox color override and setup script stdin handling * feat: improve profile deletion with auto-stop, group WebRTC settings, prioritize custom exec path * feat: add port protection toggle, fix kernel auto-update cleanup and UI refresh * fix: detect kernel assets by file extension to support renamed Windows/Linux packages * fix: handle non-browser process exits, add crash detection, validate kernel executable paths * docs: add --bot-time-seed documentation, changelog 2026-03-03, value range and per-context cross-links * feat: add proxy save/check, improve table layout and kernel date display - Add "Save to proxy list" button in proxy input for reusing manually entered proxies - Add batch proxy connectivity check with IP/status display in proxy management - Sort proxy list by creation time (newest first) - Fix kernel date display: parse from asset filename, fix timezone offset bug - Simplify profile status column to icon-only (play/stop) with tooltips - Remove year from "Last Launch" date to prevent truncation - Widen window to 1080px, use fixed table layout to prevent horizontal scrollbar - Add column width constraints for profile and proxy tables * feat: redesign profile editor with left nav, advanced config modes, and new fields - Add left-side anchor navigation with scrollspy (IntersectionObserver) - Split Fingerprint into separate Noise and Rendering sections - Replace expansion panel with flat Advanced section layout - Add mode toggles for Executable (kernel/custom), Cookies and Bookmarks (file/input) - Add FPS dropdown (profile/real/custom number) instead of free-text input - Add Save IP button in proxy section for explicit IP saving - Add new fields: Time Seed, Proxy Bypass Regex, Cookies, Bookmarks, Custom Headers - Put username/password on same row in proxy input - Widen status column to prevent icon clipping * fix: replace Save IP with Save to proxy list, unify dropdown option labels * fix: include username and password in proxy duplicate detection Normalize both sides with `|| ''` to handle undefined values, and add password to the comparison so proxies with the same host:port but different credentials are treated as distinct entries. * fix: prevent editing running profiles and remove auto-save IP on proxy check - Block profile editing when browser is running/launching/stopping, show alert dialog instead of silently darkening the background - Remove onIpCheckResult that auto-saved checked IP to proxyIp field * feat(launcher): add automatic self-update with GitHub commit tracking Check for launcher updates on startup and every hour by comparing the local commit hash against the latest launcher-specific commit from GitHub API (path=launcher). When an update is found, silently download, rebuild, and prompt the user to restart. - Add UpdateService with periodic check, ZIP download, and rebuild - Show update status (checking/downloading/building/ready) in sidebar - Display current version (commit hash) at sidebar bottom - Update setup scripts to save commit hash after install/build - Fix sidebar footer layout to stay at bottom via flex container * feat(launcher): improve table UX, add new CLI flags, and proxy input enhancements - Separate row highlight (single click) from checkbox selection (batch ops) - Add double-click to edit on both profile and proxy tables - Add clear proxy button in profile editor - Combine proxy type/host/port into single row layout - Add --bot-stack-seed and --bot-network-info-override CLI flags - Update noise seed from float to integer range - Stack seed uses dropdown (profile/real/custom) like FPS - Rearrange noise settings: seed+scale+timeseed row, fps+stackseed row - Widen proxy type column in proxy list table * fix(launcher): auto-highlight new profiles/proxies and default noise text rects off - Return created profile/proxy ID from dialog so the table highlights the new row after creation, matching the edit-then-highlight behavior - Change botConfigNoiseTextRects default from on to off * feat(launcher): add new CLI flags, clear user data, and unsaved changes guard - Add --bot-config-orientation dropdown in Display & Input section with all orientation variants, noted as mobile profiles only - Add --bot-gpu-emulation toggle in Behavior section for auto GPU rendering backend selection - Add "Clear User Data" action in both profile edit dialog and profile list context menu to delete user-data-dir - Guard profile and proxy edit dialogs against accidental close: Escape, backdrop click, and Cancel now prompt when forms have unsaved changes * feat(launcher): reorganize flag categories, add CLI preview, new 2026-03-26 flags - Restructure sections: Proxy → Proxy & Network, move GPU Emulation to Rendering, History Injection to Identity (now supports count) - Add --bot-enable-variations-in-context and --bot-inject-random-history={number} - Add CLI command preview in Advanced tab + Copy CLI in context menu - Backward compat for old profiles with flags in behavior.* - Fix history count type coercion, add copy snackbar feedback * fix(launcher): prevent kernel update corruption and improve extraction - Use unique directory per install (tagName_githubAssetId) to avoid overwriting running kernel files during auto-update - Verify directory deletion before removing kernel record to prevent orphaned records when files are locked on Windows - Clean up outdated kernels at runtime when browser exits, not only at launcher startup - Check 7z availability early at init and before download, show warning banner when missing - Fix nested 7z/zip extraction: use recursive dir search and trim \r from Windows dir output so chrome.7z is found and cleaned up - Add open-folder button in kernel list to reveal install directory - deleteKernel throws if files are locked instead of silently failing * refactor(launcher): add ShellService, fix UI feedback patterns - Add ShellService using non-blocking spawnProcess with event buffering to fix IPC event loop blocking during downloads and extraction - Migrate all execCommand calls to ShellService (zero remaining) - Replace native showMessageBox with snackbar (success) or AlertDialog (errors/warnings) for consistent in-app feedback - Unify all clipboard copy actions with snackbar confirmation - Add Copy URL button in proxy input * feat(launcher): add clear-proxy confirm, kernel column, fix multi-build kernel updates - Proxy input: move Clear button left of Check IP and gate it behind a confirm dialog; enable Clear in the profile-list "Change Proxy" flow. - Profile list: add Kernel column (derived from bot profile's Chrome major version) with sorting. - Kernel service: when a release contains several same-platform builds (e.g. botbrowser_20260514_148.0.7778.168 and _20260518_...), pick the asset with the latest assetDate instead of the first one returned by GitHub, so auto-update actually downloads the freshest build. * feat(launcher): WebKit profile support, toolchain upgrade, safer Windows CLI quoting, drop opinionated defaults WebKit/Safari profile support: - run() and getKernelVersion resolve via override → UA Chrome major → Safari→Chromium map (currently 26→149) → latest-installed-kernel fallback → version field. Profiles whose UA carries no Chrome major (e.g. webkit26_*.enc) now launch correctly. - Snapshot of the latest installed kernel refreshes on kernel.service.installedKernelsVersion so the Kernel column matches what the launcher will actually use. - Removed the hardcoded default that always emitted --bot-config-browser-brand=chrome; WebKit profiles must NOT receive this flag or they get coerced into Chrome identity. Dropdown gains a "Default (from profile)" option. shQuoteWin rewrite (Windows CLI quoting): - Now cmd.exe-quote-parity aware: walks the CommandLineToArgvW-escaped string byte by byte, toggles state on every literal `"`, inserts `"^%"` around % only when cmd is inside quotes, doubles backslash runs preceding the synthetic quote, and appends one extra `"` when input has an odd number of `"` so cmd state can't leak into the next argument. - shQuotePosix uses single quotes (suppresses $, backtick, \ expansion). Platform detected once via navigator. Stop overriding BB defaults: - Dropped launcher-side hardcoded fallbacks for botConfigBrowserBrand / Keyboard / Fonts / ColorScheme / Webgl / Webgpu / SpeechVoices / MediaDevices / MediaTypes / Webrtc and the always-on behavior/noise toggles. Each affected dropdown gains an explicit "Default" option so the no-flag state is visible. - Window/Screen Mode dropdowns get a "Default" option; #handleFileSelection no longer patches the form when the bot file is replaced. New features: - Edit Profile shows the bot profile's original source file path. - Profile list has a new Platform column (OS · engine, derived from UA); Group moved to the end. - Proxy list has an Export button that writes selected (or all) proxies to a .txt file in scheme://user:pass@host:port form, round-trippable with Bulk Import. - Forensics section: V8Log mode/dir + CanvasLab/AudioLab record file pickers (V8Log dir gated to mode at flag-emission time). Other fixes: - kernelMajorOverride === 0 treated as "no override" consistently across form init, run() gate, getKernelVersion, and #parseKernelMajorOverride. - Custom binaryPath validation: stat the candidate and require isFile; strip trailing slashes so a Finder-pasted ".app/" still routes through #findMacOSExecutable. - Override validation hoisted above the binaryPath branch in run() so malformed overrides surface even when binaryPath would short-circuit kernel resolution. - Kernel auto-update: when a release has multiple same-platform assets at different build dates, pick the asset with the latest assetDate instead of GitHub's listing order. - Save-time validators reject empty wxh/json window/screen values and empty custom DNS server before persisting. - File picker handlers and #handleFileSelection mutations marked dirty / wrapped in NgZone so the unsaved-changes guard works and mat-select bindings stay in sync. - Clear-proxy prompts a confirm dialog; Clear button moved left of Check IP. Profile-list "Change Proxy" allows clearing. - Early-exit dialog branches on whether a kernel was used, so the "kernel update / files indexed" message no longer fires for custom-binary launches. - index.html: replace stale Create-React-App placeholder %PUBLIC_URL%/__neutralino_globals.js with __neutralino_globals.js so Neutralino globals actually load (this caused the welcome page to appear on app start). Toolchain upgrade: - @angular/* 21 → 22 (ng update migrations applied), Node bumped to 22.22.3. - @neutralinojs/lib 6.5 → 6.8, runtime binary 6.5 → 6.8, neu CLI 11.7.2. - allowedCommonJsDependencies adds @neutralinojs/lib, browserslist trimmed to modern engines (avoids zone.js destructuring error in old Safari target). - Component anyComponentStyle budget raised to 6kB/10kB. * fix(launcher): CLI-flag honesty + tri-state Mobile + legacy-profile migration + Node 24.15 pin Default-on toggle flags now only emit =false when user explicitly overrides (debugger/always-active/console-message + 4 noise toggles), matching BB's own defaults so the CLI preview no longer ships redundant flags. Tri-state mat-select for Mobile Mode (Default/On/Off) replaces the binary toggle that could not distinguish "inherit profile" from "force desktop". Free-text locale fields (languages/locale/timezone/location) treat literal "auto" as the no-emit default. Local DNS dropdown drops the redundant "off" option (was identical to kernel default) and shows "Default (off)". V8Log hides duplicate "none" (functionally identical to Off) and gates the directory input on a chosen mode. --bot-title moves into buildProfileFlags so CLI preview matches the actual spawn. botConfigNoiseTextRects form default matches BB's default-on (was missing the fallback). Legacy compatibility: profiles persisted from earlier UI carry boolean false for Mobile / LocalDns that meant "no override" under the old emit rules; new code would re-interpret those as explicit Off and silently force desktop mode or re-emit the redundant --bot-local-dns=false. Form initializers now coerce legacy false to null on load, and the service emit drops the v === false branch for LocalDns so un-migrated profiles still launch cleanly. botV8Log='none' (no longer a visible mode) similarly normalizes to the Off sentinel so the mat-select renders correctly. Mobile mat-select pre-selects "Default (from profile)" when the form value is undefined via a null fallback matching the V8Log pattern. emitToggle hardened against null leaking through from hand-edited or imported profile JSON (was emitting --bot-config-noise-*=null). Node version pin bumped from 24.13.0 (rejected by Angular 22's engine constraint ^22.22.3 || ^24.15.0 || >=26) to 24.15.0 across .nvmrc and all three setup scripts, plus an engines field on package.json so npm warns early. Install scripts now version-check the existing private Node dir and re-extract on mismatch so re-running setup actually upgrades a stale install instead of short-circuiting. README clarifies the Node LTS support stance. * feat(launcher): expose --bot-video-fps, --proxy-pac-url, --disable-quic Pick up the three flags surfaced in BB 149.0.7827.102 / 2026-06-19 release docs. - --bot-video-fps (ENT Tier2, new): free-text input next to FPS / Stack Seed accepting the documented <actual>[:<reported>] form (e.g. 1, 1:30, 1:real). Empty = default; profiles without Video FPS Control enabled ignore the flag. - --proxy-pac-url (Chromium standard): text input under Advanced Proxy Config. Required entry point for trusted PAC scripts that expose BotBrowser's new request callback workflow (ENT Tier3). - --disable-quic (Chromium standard): slide-toggle in Network Protection. Documented companion flag for SOCKS5 UDP fallback so deployments can keep SOCKS5 routing while opting out of QUIC/HTTP3. * fix: text-rects noise is off-by-default in BB kernel The launcher had `?? true` on the form initializer and `bbDefault=true` in the noise emit helper, plus the toggle hint claimed "(default: on)". All of these matched older PROFILE_CONFIGS.md / CHANGELOG.md docs but contradicted the current kernel behavior, which leaves --bot-config-noise-text-rects off unless overridden (FONT.md states "disabled by default"). Fresh profiles that left the toggle untouched showed ON in the UI while BB shipped OFF at runtime. Launcher: - drop `?? true` fallback on botConfigNoiseTextRects form init - emitToggle bbDefault for text-rects flipped to false (matches kernel) - toggle hint changed to "(default: off)" Docs alignment: - profiles/PROFILE_CONFIGS.md: table + JSON template noiseTextRects default flipped to false to match the kernel - CHANGELOG.md: example CLI and "Example configs Snippet" both flipped - docs/guides/fingerprint/FONT.md: ClientRects line reworded from the ambiguous "(default)" to "(disabled by default)" so it matches every other authoritative source for that flag's actual default * feat(launcher): periodic kernel updates, startup maximize, larger profile editor Kernel auto-update no longer runs only at startup. KernelService gains startPeriodicCheck(): it re-fetches releases, auto-updates/-downloads installed kernels, and skips a tick while a previous run or any download is still in progress. Both the kernel and the launcher self-update now check every 10 minutes (kernel was startup-only, launcher was hourly); UpdateService.startPeriodicCheck takes an interval argument so the cadence is set from app.component. - Maximize the window on startup: neutralino.config maximize=true plus a ready-event window.maximize() so useSavedState can't restore a smaller size on later launches. - Enlarge the Edit Profile dialog: width 860 -> 1100px, content max-height 70 -> 82vh. - Local DNS: rename the boolean mode from "built-in resolver" to "local machine resolver" and reword the hint so it reads as host-side DNS resolution, not a bundled resolver. Internal mode value 'builtin' -> 'local'; persisted botLocalDns (true / string) is unchanged. * fix(launcher): auto force-touch for iOS profiles, not just Android Selecting a bot profile only enabled --bot-mobile-force-touch when the UA contained "android", so iPhone/iPad/iPod profiles (equally touch-first) were left with touch off. Generalize the check to match android/iphone/ ipad/ipod so iOS profiles get the same default on file selection. iPadOS UAs that masquerade as desktop Mac stay undetectable here and are left to the user. Only triggers on file selection, matching the prior Android behavior; opening an existing profile does not flip the toggle.
1 parent 7ed5a2e commit f22b0b0

8 files changed

Lines changed: 46 additions & 25 deletions

launcher/neutralino.config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"icon": "/dist/botbrowser-launcher/browser/favicon.ico",
2929
"enableInspector": false,
3030
"borderless": false,
31-
"maximize": false,
31+
"maximize": true,
3232
"hidden": false,
3333
"resizable": true,
3434
"exitProcessOnClose": true,

launcher/src/app/app.component.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ export class AppComponent implements AfterViewInit {
217217
newProfile(): void {
218218
this.#dialog
219219
.open(EditBrowserProfileComponent, {
220-
width: '860px',
220+
width: '1100px',
221221
maxWidth: '95vw',
222222
})
223223
.afterClosed()
@@ -239,7 +239,7 @@ export class AppComponent implements AfterViewInit {
239239
}
240240
this.#dialog
241241
.open(EditBrowserProfileComponent, {
242-
width: '860px',
242+
width: '1100px',
243243
maxWidth: '95vw',
244244
data: browserProfile,
245245
})
@@ -445,10 +445,10 @@ export class AppComponent implements AfterViewInit {
445445

446446
async ngAfterViewInit(): Promise<void> {
447447
await this.refreshProfiles();
448-
// Start kernel auto-update and auto-download tasks in the background
449-
this.kernelService.performStartupTasks().catch(console.error);
450-
// Check for launcher updates: once now, then every hour
451-
this.updateService.startPeriodicCheck();
448+
// Start kernel auto-update/auto-download: once now, then re-check every 10 minutes
449+
this.kernelService.startPeriodicCheck(10 * 60 * 1000);
450+
// Check for launcher updates: once now, then every 10 minutes
451+
this.updateService.startPeriodicCheck(10 * 60 * 1000);
452452
}
453453

454454
restartApp(): void {

launcher/src/app/edit-browser-profile.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,10 @@ <h2 mat-dialog-title>{{ isEdit ? 'Edit' : 'Create' }} Browser Profile</h2>
130130
<mat-label>Local DNS</mat-label>
131131
<mat-select [(value)]="localDnsMode" (selectionChange)="onLocalDnsModeChange()">
132132
<mat-option value="">Default (off)</mat-option>
133-
<mat-option value="builtin">built-in resolver</mat-option>
133+
<mat-option value="local">local machine resolver</mat-option>
134134
<mat-option value="custom">custom DNS server</mat-option>
135135
</mat-select>
136-
<mat-hint>Default routes DNS through the proxy · built-in keeps DNS local</mat-hint>
136+
<mat-hint>Default routes DNS through the proxy · local resolves DNS on this machine</mat-hint>
137137
</mat-form-field>
138138
@if (localDnsMode === 'custom') {
139139
<mat-form-field class="flex-2">

launcher/src/app/edit-browser-profile.component.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
}
44

55
mat-dialog-content {
6-
max-height: 70vh;
6+
max-height: 82vh;
77
overflow: hidden !important;
88
box-sizing: border-box;
99
padding: 0 !important;

launcher/src/app/edit-browser-profile.component.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -376,14 +376,14 @@ export class EditBrowserProfileComponent implements OnInit, AfterViewInit, OnDes
376376
(this.#injectedData?.launchOptions?.behavior as any)?.botNetworkInfoOverride,
377377
});
378378

379-
// Local DNS mode: '' (kernel default = off) / built-in / custom server.
379+
// Local DNS mode: '' (kernel default = off) / local (resolve on this machine) / custom server.
380380
// Explicit-off is omitted: `--bot-local-dns=false` would match the kernel default, so we map it to ''.
381-
localDnsMode: '' | 'builtin' | 'custom' = (() => {
381+
localDnsMode: '' | 'local' | 'custom' = (() => {
382382
const v =
383383
this.#injectedData?.launchOptions?.proxy?.botLocalDns ??
384384
(this.#injectedData?.launchOptions?.behavior as any)?.botLocalDns;
385385
if (typeof v === 'string' && v) return 'custom';
386-
if (v === true) return 'builtin';
386+
if (v === true) return 'local';
387387
return '';
388388
})();
389389
localDnsServer: string = (() => {
@@ -713,7 +713,7 @@ export class EditBrowserProfileComponent implements OnInit, AfterViewInit, OnDes
713713
if (this.localDnsMode === '') {
714714
this.proxyConfigGroup.patchValue({ botLocalDns: null });
715715
this.localDnsServer = '';
716-
} else if (this.localDnsMode === 'builtin') {
716+
} else if (this.localDnsMode === 'local') {
717717
this.proxyConfigGroup.patchValue({ botLocalDns: true });
718718
this.localDnsServer = '';
719719
} else if (this.localDnsServer) {
@@ -932,9 +932,9 @@ export class EditBrowserProfileComponent implements OnInit, AfterViewInit, OnDes
932932
this.#savedScreenWxh = '';
933933
this.#savedScreenJson = '';
934934
// No launcher-side window/screen override on file change — leave it to BB.
935-
// (botMobileForceTouch on the other hand has no implicit kernel default,
936-
// so we keep the Android touch hint.)
937-
if (this.#isAndroidProfile(basicInfo)) {
935+
// (botMobileForceTouch has no implicit kernel default, so we enable it for
936+
// touch-first mobile profiles — Android and iOS alike.)
937+
if (this.#isTouchMobileProfile(basicInfo)) {
938938
this.behaviorGroup.patchValue({ botMobileForceTouch: true });
939939
this.behaviorGroup.markAsDirty();
940940
}
@@ -950,8 +950,10 @@ export class EditBrowserProfileComponent implements OnInit, AfterViewInit, OnDes
950950
);
951951
}
952952

953-
#isAndroidProfile(basicInfo: BotProfileBasicInfo): boolean {
954-
return basicInfo.userAgent.toLowerCase().includes('android');
953+
// Touch-first mobile profiles (Android phones/tablets and iOS devices) default force-touch on.
954+
// iPadOS UAs that masquerade as desktop Mac aren't detectable here and are left to the user.
955+
#isTouchMobileProfile(basicInfo: BotProfileBasicInfo): boolean {
956+
return /android|iphone|ipad|ipod/.test(basicInfo.userAgent.toLowerCase());
955957
}
956958

957959
#validate(): boolean {

launcher/src/app/shared/kernel.service.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export class KernelService {
4646
#allReleases: KernelRelease[] = [];
4747
#initialized = false;
4848
#extractorAvailable: boolean | null = null; // null = not checked yet
49+
#intervalId: ReturnType<typeof setInterval> | null = null;
4950

5051
async initialize(): Promise<void> {
5152
if (this.#initialized) return;
@@ -126,14 +127,27 @@ export class KernelService {
126127
}
127128
}
128129

129-
// Called on app startup to perform auto-update tasks
130+
/** Run the kernel auto-update routine now, then re-check periodically (default: hourly). */
131+
startPeriodicCheck(intervalMs = 60 * 60 * 1000): void {
132+
if (this.#intervalId) return;
133+
this.performStartupTasks().catch(console.error);
134+
this.#intervalId = setInterval(() => {
135+
// Skip if a previous run or any download (auto or manual) is still in progress
136+
if (this.isAutoUpdating() || this.hasActiveDownloads()) return;
137+
this.performStartupTasks().catch(console.error);
138+
}, intervalMs);
139+
}
140+
141+
// Fetch releases and auto-update/-download kernels.
142+
// Called on startup and on each periodic check; re-entrant calls are ignored.
130143
async performStartupTasks(): Promise<void> {
131-
await this.initialize();
144+
if (this.isAutoUpdating()) return; // prevent overlapping runs
132145

133146
try {
134147
this.isAutoUpdating.set(true);
148+
await this.initialize();
135149

136-
// Fetch all releases
150+
// Always re-fetch so newly published releases are detected on periodic runs
137151
this.#allReleases = await this.#fetchAllReleases();
138152

139153
// Clean up old kernel versions (keep only the latest per major version)

launcher/src/app/shared/update.service.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,16 @@ export class UpdateService {
2323
#isWindows = false;
2424
#intervalId: ReturnType<typeof setInterval> | null = null;
2525

26-
/** Start checking for updates: once immediately, then every hour. */
27-
startPeriodicCheck(): void {
26+
/** Start checking for updates: once immediately, then on an interval (default: hourly). */
27+
startPeriodicCheck(intervalMs = 60 * 60 * 1000): void {
2828
if (this.#intervalId) return;
2929
this.checkForUpdate().catch(console.error);
3030
this.#intervalId = setInterval(() => {
3131
// Only check if idle (skip if already updating or update ready)
3232
if (this.status() === 'idle') {
3333
this.checkForUpdate().catch(console.error);
3434
}
35-
}, 60 * 60 * 1000);
35+
}, intervalMs);
3636
}
3737

3838
async checkForUpdate(): Promise<void> {

launcher/src/main.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ import * as Neutralino from '@neutralinojs/lib';
66
Neutralino.init();
77
Neutralino.events.on('windowClose', () => Neutralino.app.exit());
88

9+
// Maximize the window on startup (guarantees maximize even when useSavedState restores a smaller size)
10+
Neutralino.events.on('ready', () => {
11+
Neutralino.window.maximize().catch((err: unknown) => console.warn('maximize failed:', err));
12+
});
13+
914
// macOS: enable Cmd+C/V/X/A keyboard shortcuts
1015
Neutralino.events.on('ready', () => {
1116
if (NL_OS !== 'Darwin') return;

0 commit comments

Comments
 (0)