Conversation
WalkthroughThis update introduces cross-platform support for setting the application as the default browser, including UI, backend, and configuration changes. A new module manages default browser registration logic for Windows, macOS, and Linux, with a Windows-specific batch script and registration function. IPC handlers and preload methods enable communication between the renderer and main process for default browser status and registration. The settings UI now includes a dedicated control and feedback for setting the app as the default browser. The onboarding screen’s animated background is refactored to use a performant canvas-based system. Configuration and documentation are updated to support new protocols and file associations. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Renderer
participant Preload
participant Main
participant OS
User->>Renderer: Click "Set as Default Browser" button
Renderer->>Preload: flow.app.setDefaultBrowser()
Preload->>Main: IPC "app:set-default-browser"
Main->>OS: setDefaultBrowser()
OS-->>Main: Result (true/false)
Main-->>Preload: Result
Preload-->>Renderer: Result
Renderer-->>User: Update UI (success/failure)
sequenceDiagram
participant Renderer
participant Preload
participant Main
participant OS
Renderer->>Preload: flow.app.getDefaultBrowser()
Preload->>Main: IPC "app:get-default-browser"
Main->>OS: isDefaultBrowser()
OS-->>Main: Status (true/false)
Main-->>Preload: Status
Preload-->>Renderer: Status
Suggested labels
Poem
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
src/main/browser/utility/menu/items/app.ts (1)
16-24: Good implementation of the menu checkbox with proper state handling.The checkbox menu item is well implemented with:
- Appropriate label "Set as Default Browser"
- Click handler that calls
setDefaultBrowser()- Dynamic checked state based on
isDefaultBrowser()- Disabling the checkbox when the app is already the default browser
Consider adding error handling or user feedback in case setting the default browser fails:
click: () => { - setDefaultBrowser(); + const success = setDefaultBrowser(); + if (!success) { + // Show notification or log error + } },src/renderer/src/components/settings/sections/general/set-as-default-browser-setting.tsx (1)
9-18: Polling interval might be too aggressive.While the implementation works correctly, polling every 2 seconds (2000ms) could potentially cause unnecessary IPC traffic.
Consider using a slightly longer interval (e.g., 5000ms) to reduce overhead while still providing timely updates:
- const interval = setInterval(refetchDefaultBrowser, 2000); + const interval = setInterval(refetchDefaultBrowser, 5000);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
src/main/browser/utility/menu/items/app.ts(2 hunks)src/main/ipc/app/app.ts(2 hunks)src/main/modules/basic-settings.ts(1 hunks)src/main/modules/default-browser.ts(1 hunks)src/preload/index.ts(1 hunks)src/renderer/src/components/settings/sections/general/basic-settings-cards.tsx(2 hunks)src/renderer/src/components/settings/sections/general/set-as-default-browser-setting.tsx(1 hunks)src/shared/flow/interfaces/app/app.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
src/main/browser/utility/menu/items/app.ts (1)
src/main/modules/default-browser.ts (2)
setDefaultBrowser(10-15)isDefaultBrowser(3-8)
src/main/ipc/app/app.ts (1)
src/main/modules/default-browser.ts (2)
setDefaultBrowser(10-15)isDefaultBrowser(3-8)
src/renderer/src/components/settings/sections/general/set-as-default-browser-setting.tsx (1)
src/main/modules/default-browser.ts (1)
setDefaultBrowser(10-15)
src/renderer/src/components/settings/sections/general/basic-settings-cards.tsx (1)
src/renderer/src/components/settings/sections/general/set-as-default-browser-setting.tsx (1)
SetAsDefaultBrowserSetting(6-42)
🔇 Additional comments (12)
src/main/modules/basic-settings.ts (1)
131-131: LGTM! Good integration of the default browser setting.The addition of
"internal_setAsDefaultBrowser"to the General Settings card looks good. The "internal_" prefix indicates this is handled by internal UI components rather than being stored as a typical user setting.src/preload/index.ts (1)
333-338: Good implementation of the default browser API methods.The
setDefaultBrowserandgetDefaultBrowsermethods follow the established patterns in the file and are correctly implemented as wrappers around IPC calls.src/main/browser/utility/menu/items/app.ts (1)
3-3: Proper import of the new default browser module.The import statement correctly brings in the necessary functions from the default-browser module.
src/shared/flow/interfaces/app/app.ts (1)
26-34: Well-documented API interface extensions.The new methods are properly documented and have appropriate return types. The Promise return type correctly indicates the asynchronous nature of these operations and their success/failure status.
src/renderer/src/components/settings/sections/general/basic-settings-cards.tsx (2)
10-10: Clean import of the new component.This import correctly brings in the new
SetAsDefaultBrowserSettingcomponent that will be used in theBasicSettingsCardcomponent.
64-67: Good handling of the special case for default browser setting.The conditional check correctly renders the custom component before falling through to the default logic. This approach maintains the existing pattern while adding specific handling for the new setting type.
src/main/ipc/app/app.ts (3)
1-1: Clean import of the default browser module functions.The import correctly brings in the two functions needed for the IPC handlers.
16-18: IPC handler for setting default browser status.This handler correctly calls the
setDefaultBrowserfunction and returns its result.
20-22: IPC handler for checking default browser status.This handler correctly calls the
isDefaultBrowserfunction and returns its result.src/main/modules/default-browser.ts (1)
1-8: Good implementation of the default browser check.The
isDefaultBrowserfunction correctly checks if the app is set as the default for both HTTP and HTTPS protocols, which is the appropriate behavior for a web browser.src/renderer/src/components/settings/sections/general/set-as-default-browser-setting.tsx (2)
6-8: Clean function definition with proper state initialization.The component correctly initializes
isDefaulttonullwhich allows for displaying a loading state.
24-41: Well-designed UI with clear state indicators.The component provides an excellent user experience with different UI states for loading, not default, and default states. The use of relevant icons enhances the visual feedback.
| const setDefaultBrowser = () => { | ||
| flow.app.setDefaultBrowser(); | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Missing error handling for the API call.
The setDefaultBrowser function doesn't handle potential errors that might occur during the API call.
Consider adding error handling:
const setDefaultBrowser = () => {
- flow.app.setDefaultBrowser();
+ flow.app.setDefaultBrowser().catch((error) => {
+ console.error("Failed to set as default browser:", error);
+ // Optionally add user notification here
+ });
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const setDefaultBrowser = () => { | |
| flow.app.setDefaultBrowser(); | |
| }; | |
| const setDefaultBrowser = () => { | |
| flow.app.setDefaultBrowser().catch((error) => { | |
| console.error("Failed to set as default browser:", error); | |
| // Optionally add user notification here | |
| }); | |
| }; |
There was a problem hiding this comment.
Actionable comments posted: 5
♻️ Duplicate comments (1)
src/main/modules/default-browser.ts (1)
16-19: Potential logical inconsistency in return value.The function returns a Promise that resolves to true if either HTTP or HTTPS protocol was successfully set, but
isDefaultBrowser()returns true only if both are set. This could lead to confusing behavior wheresetDefaultBrowser()returns true but immediately callingisDefaultBrowser()returns false.Consider changing the return value to ensure consistency:
export function setDefaultBrowser() { - app.setAsDefaultProtocolClient("http"); - app.setAsDefaultProtocolClient("https"); + const httpSucceed = app.setAsDefaultProtocolClient("http"); + const httpsSucceed = app.setAsDefaultProtocolClient("https"); + const bothSucceed = httpSucceed && httpsSucceed; return new Promise((resolve) => {Then use
bothSucceedin your platform-specific logic.
🧹 Nitpick comments (2)
assets/default-app/windows.bat (1)
87-88: Consider security implications of self-deletion.While the self-deletion feature is commented out, implementing it could lead to confusion if users need to run the script again. Additionally, it removes evidence of what changed the system configuration, which could complicate troubleshooting.
Either remove this commented code or add more context about when self-deletion would be appropriate.
src/main/modules/default-browser-windows.ts (1)
10-13: Consider fetching app description from package.json.The app description is hardcoded in this file. For better maintainability, consider extracting it from your package.json file.
const APP_NAME_SHORT = "flow"; const APP_NAME = app.getName(); - const APP_DESCRIPTION = "An experimental browser built on Electron."; + // Get description from package.json + const APP_DESCRIPTION = require("../../../package.json").description || "An experimental browser built on Electron.";
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
assets/default-app/windows.bat(1 hunks)electron-builder.json(2 hunks)src/main/ipc/app/app.ts(2 hunks)src/main/modules/default-browser-windows.ts(1 hunks)src/main/modules/default-browser.ts(1 hunks)src/main/modules/paths.ts(2 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/main/modules/paths.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- electron-builder.json
- src/main/ipc/app/app.ts
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/main/modules/default-browser.ts (1)
src/main/modules/default-browser-windows.ts (1)
registerAppForCurrentUserOnWindows(25-89)
src/main/modules/default-browser-windows.ts (1)
src/main/modules/paths.ts (1)
PATHS(18-23)
🪛 Biome (1.9.4)
src/main/modules/default-browser-windows.ts
[error] 31-88: Promise executor functions should not be async.
(lint/suspicious/noAsyncPromiseExecutor)
🔇 Additional comments (9)
assets/default-app/windows.bat (5)
1-7: Well-structured batch script with proper header information.The script has clear header comments explaining its purpose, attribution, and the fact that it doesn't require administrator privileges.
14-25: Robust input validation and error handling.The script properly validates all required arguments and checks if the executable exists before proceeding. This prevents potential errors from propagating and provides clear error messages to users.
67-75: Good association capability structure.The script creates the necessary registry structure but doesn't force specific associations, instead allowing users to choose their preferences via the Windows settings UI. This is a user-friendly approach that respects user choice.
84-86: Appropriate UI integration.Opening the Default Apps settings page after registration is a good UX choice that helps users complete the process of setting the app as their default browser.
92-106: Excellent usage documentation.The script includes clear usage instructions, examples, and notes about argument formatting. This makes it easier for developers to use the script correctly.
src/main/modules/default-browser.ts (2)
30-32: Well-structured Windows implementation.The code properly delegates Windows-specific implementation to a dedicated module, which helps with maintainability and separation of concerns.
33-37: Good platform detection logic.The code correctly handles platform-specific implementations with clear separation and appropriate fallbacks.
src/main/modules/default-browser-windows.ts (2)
57-66: Well-implemented argument quoting for PowerShell.The code correctly handles complex quoting rules for PowerShell and the batch script. The comments explaining the quoting strategy are very helpful.
80-86: Good temporary file cleanup.The code properly cleans up the temporary file after execution, even if there's an error, which is a good practice to prevent accumulation of temporary files.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
assets/default-app/register_app_user.bat (2)
18-25: Consider enhancing executable validation with additional checks.The executable existence check is good, but could be enhanced for robustness.
rem Check if executable exists if not exist "%APP_PATH%" ( echo ERROR: Executable not found at "%APP_PATH%" pause goto :eof ) +rem Ensure the path has .exe extension for an application +if /i not "%APP_PATH:~-4%" == ".exe" ( + echo WARNING: The specified path does not end with .exe: "%APP_PATH%" + echo This might not be a valid application. + pause +)
84-88: Consider fallback for older Windows versions.The script tries to open Settings using
ms-settings:defaultapps, which works on Windows 10/11, but might fail on older Windows versions.rem Try opening the modern Default Apps settings page (Win 10/11) start "" ms-settings:defaultapps +rem Fallback for older Windows versions if the above fails +if %ERRORLEVEL% NEQ 0 ( + echo Falling back to classic Control Panel for Default Programs... + control /name Microsoft.DefaultPrograms /page pageDefaultProgram +) rem Optional: Self-deletion - remove the 'rem' below if you want the script to delete itself rem (goto) 2>nul & del "%~f0"src/main/modules/default-browser.ts (1)
29-32: Document macOS behavior for better maintainability.On macOS, the function assumes Electron's API shows the default app request dialog, but this should be better documented.
} else if (process.platform === "darwin") { - // Electron API should be enough to show a popup for default app request + // On macOS, calling setAsDefaultProtocolClient shows a system dialog + // asking the user for permission. We've already called it above. + // Reference: https://www.electronjs.org/docs/latest/api/app#appsetasdefaultprotocolclientprotocol-path-args resolve(true); return; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
assets/default-app/register_app_user.bat(1 hunks)src/main/modules/default-browser.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/modules/default-browser.ts (1)
src/main/modules/default-browser-windows.ts (1)
registerAppForCurrentUserOnWindows(25-89)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: release (ubuntu-latest)
- GitHub Check: release (windows-latest)
- GitHub Check: release (macos-latest)
🔇 Additional comments (3)
assets/default-app/register_app_user.bat (1)
1-107: Well-structured Windows registry script that follows best practices.This batch script is well-designed for registering the application as a default browser on Windows at the user level (without requiring admin rights). The script:
- Properly validates input parameters
- Uses HKCU registry keys (current user only, safer approach)
- Includes informative comments
- Has good error handling
- Opens Windows Default Apps settings for the user to complete the process
src/main/modules/default-browser.ts (2)
5-10: Good implementation of the isDefaultBrowser function.The function correctly checks if both HTTP and HTTPS protocols are registered to this application, which aligns with browser functionality expectations.
18-18:Details
❓ Verification inconclusive
Verify the .desktop filename for Linux.
Ensure
flow.desktopis the correct filename for your application's desktop entry on Linux systems.
🏁 Script executed:
#!/bin/bash # Check if we can find any .desktop files related to the application find . -name "*.desktop" | grep -i flow # Check the typical installation location for desktop files echo "Standard locations for .desktop files that might be relevant:" echo "- ~/.local/share/applications/" echo "- /usr/share/applications/"Length of output: 308
Verify and update the Linux .desktop filename
I didn’t find a
flow.desktopentry in the repo. Please ensure that:
- Your build or packaging step installs a
flow.desktopfile (or whatever your desktop entry is named) to one of these locations:
•~/.local/share/applications/
•/usr/share/applications/- The name you pass to
xdg-settings set default-web-browser <name>.desktopexactly matches the installed.desktopfilename.- If your desktop entry uses a different name (e.g.
myapp.desktop), update the command accordingly:- exec("xdg-settings set default-web-browser flow.desktop", (err) => { + exec("xdg-settings set default-web-browser <your-app>.desktop", (err) => {
| export function setDefaultBrowser() { | ||
| app.setAsDefaultProtocolClient("http"); | ||
| app.setAsDefaultProtocolClient("https"); | ||
|
|
||
| return new Promise((resolve) => { | ||
| if (process.platform === "linux" || process.platform.includes("bsd")) { | ||
| exec("xdg-settings set default-web-browser flow.desktop", (err) => { | ||
| if (err?.message) { | ||
| resolve(false); | ||
| } else { | ||
| resolve(true); | ||
| } | ||
| }); | ||
| return; | ||
| } else if (process.platform === "win32") { | ||
| registerAppForCurrentUserOnWindows().then(resolve); | ||
| return; | ||
| } else if (process.platform === "darwin") { | ||
| // Electron API should be enough to show a popup for default app request | ||
| resolve(true); | ||
| return; | ||
| } | ||
|
|
||
| // If we don't know how to set the default browser, return false | ||
| resolve(false); | ||
| }); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Improve error handling in the setDefaultBrowser function.
The function attempts to set protocol handlers before platform-specific logic runs, which is good. However, error handling could be improved, especially for Linux.
export function setDefaultBrowser() {
const httpResult = app.setAsDefaultProtocolClient("http");
const httpsResult = app.setAsDefaultProtocolClient("https");
+
+ // Log if initial protocol registration fails
+ if (!httpResult || !httpsResult) {
+ console.warn(`Protocol registration partial success or failure: HTTP=${httpResult}, HTTPS=${httpsResult}`);
+ }
return new Promise((resolve) => {
if (process.platform === "linux" || process.platform.includes("bsd")) {
exec("xdg-settings set default-web-browser flow.desktop", (err) => {
- if (err?.message) {
+ if (err) {
+ console.error(`Linux/BSD default browser setting failed:`, err.message || err);
resolve(false);
} else {
+ console.log("Successfully set as default browser on Linux/BSD");
resolve(true);
}
});
return;
} else if (process.platform === "win32") {
+ console.log("Attempting to set as default browser on Windows...");
registerAppForCurrentUserOnWindows().then(resolve);
return;
} else if (process.platform === "darwin") {
// Electron API should be enough to show a popup for default app request
+ console.log("Default browser request initiated on macOS");
resolve(true);
return;
}
// If we don't know how to set the default browser, return false
+ console.warn(`Unsupported platform for default browser: ${process.platform}`);
resolve(false);
});
}Committable suggestion skipped: line range outside the PR's diff.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
src/renderer/src/components/onboarding/screen.tsx (1)
180-188: Per-shapefilter: blur()is a GPU hotspotApplying
ctx.filter = 'blur(...)'inside the inner drawing loop triggers
a state change for every shape, which is one of the slowest canvas
operations – especially noticeable whentargetShapeCountcreeps towards
maxShapes.Consider:
- Drawing each shape to an off-screen canvas once and blitting it.
- Moving the blur to CSS by layering an extra
<canvas>with
filter: blur(...)applied globally.- Dialling down the blur or only applying it to circles, which benefit
visually the most.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/main/modules/basic-settings.ts(2 hunks)src/renderer/src/components/onboarding/screen.tsx(3 hunks)src/renderer/src/components/onboarding/stages/onboarding-drag-disabler.tsx(0 hunks)src/renderer/src/components/onboarding/stages/sidebar-collapse-mode.tsx(2 hunks)
💤 Files with no reviewable changes (1)
- src/renderer/src/components/onboarding/stages/onboarding-drag-disabler.tsx
✅ Files skipped from review due to trivial changes (1)
- src/renderer/src/components/onboarding/stages/sidebar-collapse-mode.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- src/main/modules/basic-settings.ts
⏰ Context from checks skipped due to timeout of 90000ms (2)
- GitHub Check: release (ubuntu-latest)
- GitHub Check: release (windows-latest)
🔇 Additional comments (1)
src/renderer/src/components/onboarding/screen.tsx (1)
427-430: Total duration mis-calculates when speed is “per segment”
totalDuration = path.x.length * speed * 1000counts the points, not
the segments. For an n-point path there are n − 1 segments, so the
animation finishesspeedseconds early.-const totalDuration = path.x.length * speed * 1000; +const totalDuration = (path.x.length - 1) * speed * 1000;If the current timing matches your visual spec, add a clarifying comment
so future maintainers know it’s deliberate.
| import React, { useEffect, useState, useRef, useCallback } from "react"; | ||
| import { motion } from "motion/react"; // Used for backdrop blur and gradient orbs |
There was a problem hiding this comment.
Incorrect package import – likely to break at runtime
"motion/react" is not a published entry-point in either Motion One or Framer-Motion.
• Motion One’s React bindings are published as @motionone/react
• Framer-Motion is imported as framer-motion
Unless you have a patched fork published under that name, this will throw Cannot find module 'motion/react' in production.
-import { motion } from "motion/react";
+// If you use Motion One:
+import { motion } from "@motionone/react";
+// …or if you meant Framer Motion:
+// import { motion } from "framer-motion";📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import React, { useEffect, useState, useRef, useCallback } from "react"; | |
| import { motion } from "motion/react"; // Used for backdrop blur and gradient orbs | |
| import React, { useEffect, useState, useRef, useCallback } from "react"; | |
| // If you use Motion One: | |
| import { motion } from "@motionone/react"; | |
| // …or if you meant Framer Motion: | |
| // import { motion } from "framer-motion"; |
| canvas.width = windowSize.width * dpr; | ||
| canvas.height = windowSize.height * dpr; | ||
|
|
||
| // Set display size of canvas | ||
| canvas.style.width = `${windowSize.width}px`; | ||
| canvas.style.height = `${windowSize.height}px`; | ||
|
|
||
| const ctx = canvas.getContext("2d"); | ||
| if (!ctx) return; | ||
|
|
||
| // Scale the drawing context to handle HiDPI displays correctly | ||
| // All drawing operations below will be scaled automatically | ||
| ctx.scale(dpr, dpr); | ||
|
|
There was a problem hiding this comment.
Progressive over-scaling on Hi-DPI resize
ctx.scale(dpr, dpr) is executed every time the window resizes, but the
canvas transform is multiplicative.
Scaling twice on a 2× DPR screen → 4×, three times → 8×, etc., quickly pushing everything off-screen.
Reset the transform before re-scaling:
-const ctx = canvas.getContext("2d");
-if (ctx) {
- ctx.scale(dpr, dpr);
-}
+const ctx = canvas.getContext("2d");
+if (ctx) {
+ ctx.setTransform(1, 0, 0, 1, 0, 0); // reset
+ ctx.scale(dpr, dpr);
+}Committable suggestion skipped: line range outside the PR's diff.
| if (animationFrameId.current) { | ||
| cancelAnimationFrame(animationFrameId.current); | ||
| } | ||
| }; | ||
| // Re-run the effect if the shapes array, window size, or canvas ref instance changes | ||
| }, [shapes, windowSize, canvasRef]); | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Effect restarts every shape tick – unnecessary re-initialisation
useEffect depends on shapes; each time the shape array mutates (every
2 s, plus initial burst) the effect tears down and recreates the entire
animation loop, causing:
• superfluous cancelAnimationFrame / requestAnimationFrame churn
• potential one-frame flicker on low-end devices
Store shapes in a ref instead, and keep the draw loop stable:
-const useCanvasShapes = (canvasRef, shapes, windowSize) => {
+const useCanvasShapes = (canvasRef, shapes, windowSize) => {
const shapesRef = useRef<ShapeData[]>([]);
shapesRef.current = shapes; // update ref on every render
@@
-}, [shapes, windowSize, canvasRef]);
+}, [windowSize, canvasRef]); // remove `shapes` from depsCommittable suggestion skipped: line range outside the PR's diff.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
src/main/modules/default-browser-windows.ts (2)
17-17:⚠️ Potential issueFile path mismatch for batch script.
The code is looking for
register_app_user.batbut according to a previous review comment, the actual file is namedwindows.bat. This path mismatch will cause the registration process to fail.- const scriptPath = path.join(PATHS.ASSETS, "default-app", "register_app_user.bat"); + const scriptPath = path.join(PATHS.ASSETS, "default-app", "windows.bat");
30-31:⚠️ Potential issueAvoid using async Promise executor.
Using an async function as a Promise executor is problematic because errors thrown from the async function can't be caught by the Promise's catch handler. This pattern is discouraged and flagged by static analysis tools.
- // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve) => { + return new Promise((resolve) => { + (async () => { let scriptContents: Buffer | string; try { // Implementation... + })().catch(err => { + console.error("Unexpected error in registerAppForCurrentUserOnWindows:", err); + resolve(false); + });
🧹 Nitpick comments (1)
src/main/modules/default-browser-windows.ts (1)
76-77: Add timeout handling for the script execution.Currently, there's no timeout for the script execution, which could potentially hang indefinitely. Consider adding a timeout mechanism to ensure the operation completes within a reasonable timeframe.
exec(command, async (err) => { if (err) { // Log the specific error from the exec call console.error("Error executing registration script:", err.message); resolve(false); } else { console.log(`Registration script executed successfully for "${APP_NAME}". Check Default Apps settings.`); + // Consider observing if the default browser setting has actually changed resolve(true); }You could also implement a timeout with something like:
const execWithTimeout = (command: string, timeoutMs = 30000): Promise<boolean> => { return new Promise((resolve) => { const process = exec(command); const timeoutId = setTimeout(() => { if (process.pid) { // Kill the process if it's still running exec(`taskkill /pid ${process.pid} /f /t`); } console.error("Registration script execution timed out"); resolve(false); }, timeoutMs); process.on('exit', (code) => { clearTimeout(timeoutId); resolve(code === 0); }); process.on('error', (err) => { clearTimeout(timeoutId); console.error("Error executing registration script:", err.message); resolve(false); }); }); };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
build/entitlements.mac.plist(0 hunks)src/main/modules/default-browser-windows.ts(1 hunks)
💤 Files with no reviewable changes (1)
- build/entitlements.mac.plist
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/modules/default-browser-windows.ts (1)
src/main/modules/paths.ts (1)
PATHS(18-23)
🪛 Biome (1.9.4)
src/main/modules/default-browser-windows.ts
[error] 31-88: Promise executor functions should not be async.
(lint/suspicious/noAsyncPromiseExecutor)
| const arg1 = `"""${appExecutablePath}"""`; // Path to exe | ||
| const arg2 = `"${APP_NAME_SHORT}"`; // Short name (quotes optional if no spaces) | ||
| const arg3 = `"""${APP_NAME}"""`; // Full name | ||
| const arg4 = `"""${APP_DESCRIPTION}"""`; // Description |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Consider input validation for strings that will be used in command execution.
The application name, description, and paths are directly incorporated into a command string. Though you've added quotes to handle spaces, it's good practice to validate or sanitize these inputs to prevent potential command injection issues.
+ // Sanitize inputs to prevent command injection
+ const sanitizeForCommand = (input: string): string => {
+ // Remove characters that could break command execution
+ return input.replace(/[&<>^|()%]/g, '');
+ };
+
- const arg1 = `"""${appExecutablePath}"""`;
- const arg2 = `"${APP_NAME_SHORT}"`;
- const arg3 = `"""${APP_NAME}"""`;
- const arg4 = `"""${APP_DESCRIPTION}"""`;
+ const arg1 = `"""${sanitizeForCommand(appExecutablePath)}"""`;
+ const arg2 = `"${sanitizeForCommand(APP_NAME_SHORT)}"`;
+ const arg3 = `"""${sanitizeForCommand(APP_NAME)}"""`;
+ const arg4 = `"""${sanitizeForCommand(APP_DESCRIPTION)}"""`;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const arg1 = `"""${appExecutablePath}"""`; // Path to exe | |
| const arg2 = `"${APP_NAME_SHORT}"`; // Short name (quotes optional if no spaces) | |
| const arg3 = `"""${APP_NAME}"""`; // Full name | |
| const arg4 = `"""${APP_DESCRIPTION}"""`; // Description | |
| // Sanitize inputs to prevent command injection | |
| const sanitizeForCommand = (input: string): string => { | |
| // Remove characters that could break command execution | |
| return input.replace(/[&<>^|()%]/g, ''); | |
| }; | |
| const arg1 = `"""${sanitizeForCommand(appExecutablePath)}"""`; // Path to exe | |
| const arg2 = `"${sanitizeForCommand(APP_NAME_SHORT)}"`; // Short name (quotes optional if no spaces) | |
| const arg3 = `"""${sanitizeForCommand(APP_NAME)}"""`; // Full name | |
| const arg4 = `"""${sanitizeForCommand(APP_DESCRIPTION)}"""`; // Description |
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (2)
src/main/modules/default-browser-windows.ts (2)
53-54:⚠️ Potential issueAvoid using async Promise executor.
Using an async function as a Promise executor is problematic because errors thrown from the async function can't be caught by the Promise's catch handler. This was previously flagged in a review.
- // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve) => { + return new Promise((resolve) => { + (async () => { let scriptTemplate: string; try { // Implementation... + })().catch(err => { + console.error("Unexpected error in registerAppForCurrentUserOnWindows:", err); + resolve(false); + });
113-117:⚠️ Potential issueInput validation needed for command execution.
The application name, description, and paths are directly incorporated into a command string without validation or sanitization. This could lead to command injection issues if any of these values contain special characters.
+ // Sanitize inputs to prevent command injection + const sanitizeForCommand = (input: string): string => { + // Remove characters that could break command execution + return input.replace(/[&<>^|()%]/g, ''); + }; + - const arg1 = `"""${appExecutablePath}"""`; - const arg2 = `"${APP_NAME_SHORT}"`; - const arg3 = `"""${APP_NAME}"""`; - const arg4 = `"""${APP_DESCRIPTION}"""`; + const arg1 = `"""${sanitizeForCommand(appExecutablePath)}"""`; + const arg2 = `"${sanitizeForCommand(APP_NAME_SHORT)}"`; + const arg3 = `"""${sanitizeForCommand(APP_NAME)}"""`; + const arg4 = `"""${sanitizeForCommand(APP_DESCRIPTION)}"""`;
🧹 Nitpick comments (4)
src/main/modules/default-browser-windows.ts (2)
119-120: Consider removing PowerShell window after execution.The current command doesn't hide the PowerShell window. Adding the
-WindowStyle Hiddenparameter would make the registration process more seamless for users.- const command = `Powershell -NoProfile -ExecutionPolicy Bypass -Command "Start-Process -FilePath '${tempFile}' -ArgumentList ${arg1}, ${arg2}, ${arg3}, ${arg4}"`; + const command = `Powershell -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -Command "Start-Process -FilePath '${tempFile}' -ArgumentList ${arg1}, ${arg2}, ${arg3}, ${arg4}"`;
145-154: Remove commented example code in production.The commented example code at the end of the file should be removed before production deployment as it serves no functional purpose and may confuse future developers.
- // --- Example Usage --- - /* - registerAppForCurrentUser().then(success => { - if (success) { - console.log("Application registration process completed successfully."); - } else { - console.error("Application registration process failed."); - } - }); - */assets/default-app/register_app_user_template.bat (2)
84-85: Consider fallback for older Windows versions.The script only attempts to open the modern Settings app with
ms-settings:defaultapps, which may not work on older Windows versions (pre-Windows 10). Consider adding a fallback for compatibility.rem Try opening the modern Default Apps settings page (Win 10/11) start "" ms-settings:defaultapps + rem Fallback for older Windows versions (Win 7/8) + if %ERRORLEVEL% neq 0 ( + start control /name Microsoft.DefaultPrograms /page pageDefaultProgram + )
87-88: Self-deletion mechanism may be unnecessary.Since the script is being called from Node.js which already handles the temporary file deletion (in the TypeScript code), this self-deletion mechanism is redundant and can be removed.
- rem Optional: Self-deletion - remove the 'rem' below if you want the script to delete itself - rem (goto) 2>nul & del "%~f0"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
assets/default-app/register_app_user_template.bat(1 hunks)src/main/modules/default-browser-windows.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/modules/default-browser-windows.ts (1)
src/main/modules/paths.ts (1)
PATHS(18-23)
🪛 Biome (1.9.4)
src/main/modules/default-browser-windows.ts
[error] 54-142: Promise executor functions should not be async.
(lint/suspicious/noAsyncPromiseExecutor)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: release (macos-latest)
- GitHub Check: release (windows-latest)
- GitHub Check: release (ubuntu-latest)
🔇 Additional comments (4)
assets/default-app/register_app_user_template.bat (4)
1-8: LGTM: Well-structured batch script with clear documentation.The script header provides clear documentation about its purpose, requirements, and attribution. It properly sets up the environment with
setlocal enabledelayedexpansionwhich is a good practice for batch scripts.
15-26: Robust input validation with clear error messaging.The script properly validates all required arguments and checks if the executable exists before proceeding. The error messages are clear and helpful.
41-52: Well-organized registry modifications for file and URL associations.The registry entries are properly organized and include all necessary keys for file type and URL protocol handlers. The use of
>nulto suppress output is appropriate for a cleaner user experience.
73-78: Good use of placeholders for dynamic associations.The script uses clear placeholder markers that can be easily identified and replaced by the calling script. The comments make it clear what each placeholder is for.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (3)
src/main/modules/default-browser-windows.ts (3)
124-134: Improve error handling in exec callback.The current error handling in the exec callback only logs the error message. Providing more detailed error information would aid in troubleshooting.
- exec(command, async (err, stdout) => { + exec(command, async (err, stdout, stderr) => { if (err) { - console.error("Error executing registration script:", err.message); + console.error("Error executing registration script:", { + message: err.message, + code: err.code, + stderr: stderr.trim() + }); resolve(false); } else { console.log(`Registration script executed successfully for "${APP_NAME}". Check Default Apps settings.`); console.log("Output:", stdout); resolve(true); }
53-54:⚠️ Potential issueFix the async Promise executor pattern.
Using an async function as a Promise executor is problematic because errors thrown from the async function can't be caught by the Promise's catch handler.
Apply this fix:
- // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve) => { + return new Promise((resolve) => { + (async () => { let scriptTemplate: string; try { // ...implementation... + })().catch(err => { + console.error("Unexpected error in registerAppForCurrentUserOnWindows:", err); + resolve(false); + });
113-118:⚠️ Potential issueAdd input validation for command execution.
The application name, description, and paths are directly incorporated into a command string without validation. This could potentially lead to command injection issues if these values contain special characters.
Add a sanitization function:
+ // Sanitize inputs to prevent command injection + const sanitizeForCommand = (input: string): string => { + // Remove characters that could break command execution + return input.replace(/[&<>^|()%]/g, ''); + }; + - const arg1 = `"""${appExecutablePath}"""`; - const arg2 = `"${APP_NAME_SHORT}"`; - const arg3 = `"""${APP_NAME}"""`; - const arg4 = `"""${APP_DESCRIPTION}"""`; + const arg1 = `"""${sanitizeForCommand(appExecutablePath)}"""`; + const arg2 = `"${sanitizeForCommand(APP_NAME_SHORT)}"`; + const arg3 = `"""${sanitizeForCommand(APP_NAME)}"""`; + const arg4 = `"""${sanitizeForCommand(APP_DESCRIPTION)}"""`;
🧹 Nitpick comments (2)
src/main/index.ts (2)
152-153: Fix typo in comment.There's a typo in the comment "Hnadle" instead of "Handle".
- // Hnadle command line arguments + // Handle command line arguments
94-130: Consider adding a timeout parameter to the handleOpenUrl function.The current implementation tries to find a window 5 times with a fixed 50ms delay. This approach might not be flexible enough for different system conditions.
Consider refactoring the retry logic to be more configurable:
- const handleOpenUrl = async (url: string) => { + const handleOpenUrl = async (url: string, maxRetries = 5, retryDelay = 50) => { if (!browser) return; if (!app.isReady) { await app.whenReady(); } let window: TabbedBrowserWindow | null = null; - for (let i = 0; i < 5; i++) { + for (let i = 0; i < maxRetries; i++) { // Check if there is a focused window const focusedWindow = browser.getFocusedWindow(); if (focusedWindow) { window = focusedWindow; break; } // Check for any window const firstWindow = browser.getWindows()[0]; if (firstWindow) { window = firstWindow; break; } - await sleep(50); + await sleep(retryDelay); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
electron-builder.json(3 hunks)src/main/index.ts(3 hunks)src/main/modules/default-browser-windows.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- electron-builder.json
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/modules/default-browser-windows.ts (1)
src/main/modules/paths.ts (1)
PATHS(18-23)
🪛 Biome (1.9.4)
src/main/modules/default-browser-windows.ts
[error] 54-143: Promise executor functions should not be async.
(lint/suspicious/noAsyncPromiseExecutor)
🔇 Additional comments (1)
src/main/modules/default-browser-windows.ts (1)
79-91: 🛠️ Refactor suggestionUse URL constructor instead of deprecated URL.parse.
The
URL.parse()method is deprecated. Use theURLconstructor instead for better reliability and standards compliance.function isValidOpenerUrl(url: string): boolean { // Check if the URL is a valid URL - const urlObject = URL.parse(url); - if (!urlObject) { - return false; - } + try { + const urlObject = new URL(url); + const VALID_PROTOCOLS = ["http:", "https:"]; + // Check if the URL has a valid protocol + return VALID_PROTOCOLS.includes(urlObject.protocol); + } catch (e) { + return false; + } - - const VALID_PROTOCOLS = ["http:", "https:"]; - // Check if the URL has a valid protocol - if (!VALID_PROTOCOLS.includes(urlObject.protocol)) { - return false; - } - - return true; }Likely an incorrect or invalid review comment.
| const url = commandLine.pop(); | ||
| if (url) { | ||
| if (url && isValidOpenerUrl(url)) { | ||
| // Handle the URL if it is valid | ||
| handleOpenUrl(url); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Improve second-instance URL handling consistency.
Similar to the issue above, the second-instance handler pops the last argument which might not always be the URL.
- const url = commandLine.pop();
- if (url && isValidOpenerUrl(url)) {
- // Handle the URL if it is valid
- handleOpenUrl(url);
- }
+ // Look for a URL in the command line arguments
+ for (const arg of commandLine) {
+ if (isValidOpenerUrl(arg)) {
+ // Handle the URL if it is valid
+ handleOpenUrl(arg);
+ break; // Handle only the first valid URL
+ }
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const url = commandLine.pop(); | |
| if (url) { | |
| if (url && isValidOpenerUrl(url)) { | |
| // Handle the URL if it is valid | |
| handleOpenUrl(url); | |
| } | |
| // Look for a URL in the command line arguments | |
| for (const arg of commandLine) { | |
| if (isValidOpenerUrl(arg)) { | |
| // Handle the URL if it is valid | |
| handleOpenUrl(arg); | |
| break; // Handle only the first valid URL | |
| } | |
| } |
| const targetUrl = commandLine.pop(); | ||
| if (targetUrl && isValidOpenerUrl(targetUrl)) { | ||
| // Handle the URL if it is valid | ||
| handleOpenUrl(targetUrl); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Improve command line URL handling.
The current implementation pops the last argument and checks if it's a URL, which might not be reliable. Command-line arguments don't always have the URL as the last item.
- const commandLine = process.argv.slice(1);
- const targetUrl = commandLine.pop();
- if (targetUrl && isValidOpenerUrl(targetUrl)) {
- // Handle the URL if it is valid
- handleOpenUrl(targetUrl);
- }
+ const commandLine = process.argv.slice(1);
+ // Look for a URL in the command line arguments
+ for (const arg of commandLine) {
+ if (isValidOpenerUrl(arg)) {
+ // Handle the URL if it is valid
+ handleOpenUrl(arg);
+ break; // Handle only the first valid URL
+ }
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const targetUrl = commandLine.pop(); | |
| if (targetUrl && isValidOpenerUrl(targetUrl)) { | |
| // Handle the URL if it is valid | |
| handleOpenUrl(targetUrl); | |
| } | |
| const commandLine = process.argv.slice(1); | |
| // Look for a URL in the command line arguments | |
| for (const arg of commandLine) { | |
| if (isValidOpenerUrl(arg)) { | |
| // Handle the URL if it is valid | |
| handleOpenUrl(arg); | |
| break; // Handle only the first valid URL | |
| } | |
| } |
Summary by CodeRabbit
New Features
Improvements
Platform Support
Other Changes