Skip to content

Conversation

@CorneiZeR
Copy link

@CorneiZeR CorneiZeR commented Oct 6, 2025

Summary by CodeRabbit

  • New Features

    • Password-protected full-screen lock overlay with Enter-to-unlock and keyboard support.
    • Automatic auto-lock on inactivity with configurable timeout (0 = disabled) and resume on user activity.
    • Settings UI to configure timeout and set password; persisted hashed password with migration for legacy formats.
    • “Lock Screen” added to app and context menus (Cmd/Ctrl+L).
  • Chores

    • Bundled lock-screen renderer, preload, and HTML shell; safe renderer API for verify/unlock/set password.
    • Store/reducer/action additions, migrations, extensive i18n translations, and EditorConfig tweak.

@CLAassistant
Copy link

CLAassistant commented Oct 6, 2025

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 6, 2025

Walkthrough

Adds an application-level screen-lock feature: build entries and HTML shell, renderer/preload and IPC surfaces, main-process lock orchestration and IPC handlers, store slices, settings UI and menu triggers, migrations/persistence for password and timeout, and translations for many locales.

Changes

Cohort / File(s) Summary of changes
Build config & public asset
rollup.config.mjs, src/public/lockScreen.html
Adds Rollup bundles for lock-screen renderer and preload; adds static lockScreen HTML shell.
Main startup & wiring
src/main.ts
Calls setupScreenLock() at startup and shows lock window if persisted locked; imports lock APIs.
App orchestration & IPC
src/app/main/app.ts, src/app/main/screenLock.ts
Implements BrowserView-based lock overlay, lifecycle, IPC handlers (lock:verify, lock:unlock, lock:set), throttling/backoff, password hashing/migration, registerLockIpcHandlers() and showLockWindow().
Persisted models & migrations
src/app/PersistableValues.ts
Adds ScreenLockPasswordStored, PersistableValues_5_0_0 fields (screenLockTimeoutSeconds, screenLockPasswordHash, isScreenLocked) and migrations to initialize/migrate legacy password formats.
Selectors, store & root reducer
src/app/selectors.ts, src/store/rootReducer.ts
Exposes screenLockTimeoutSeconds, screenLockPasswordHash, isScreenLocked selectors; integrates new reducers into rootReducer (RootState updated).
Redux actions & reducers
src/ui/actions.ts, src/app/actions.ts, src/ui/reducers/screenLockTimeoutSeconds.ts, src/ui/reducers/screenLockPasswordHash.ts, src/app/reducers/isScreenLocked.ts
Adds action constants (settings/menu/ app), payload mappings, and reducers for timeout, password-hash, and isScreenLocked with APP_SETTINGS_LOADED handling.
Settings UI
src/ui/components/SettingsView/GeneralTab.tsx, src/ui/components/SettingsView/features/ScreenLock.tsx
Adds ScreenLock settings component (timeout numeric input and password input) wired to Redux and to preload/ipc for persisting passwords.
Menus & context menus
src/ui/main/menuBar.ts, src/ui/main/serverView/popupMenu.ts
Adds “Lock Screen” menu/context items (Cmd/Ctrl+L) that focus root window and dispatch MENU_BAR_LOCK_SCREEN_CLICKED.
Lock-screen renderer & preload
src/lockScreen/LockScreen.tsx, src/lockScreen/lock-screen.tsx, src/lockScreen/preload.ts, src/types/electron-api.d.ts, src/preload.ts, src/lockScreen/lock-screen.tsx
Adds React LockScreen component and renderer bootstrap with i18n; preload exposes electronAPI (verifyPassword, unlockApp); main preload exposes setLockPassword; TypeScript global window augmentation.
IPC channels typing
src/ipc/channels.ts
Adds lock:verify, lock:unlock, and lock:set channel signatures to ChannelToArgsMap.
i18n
src/i18n/*.i18n.json (many locales, e.g., en.i18n.json, de-DE.i18n.json, zh-CN.i18n.json, etc.)
Adds lockScreen translation keys and settings.options.screenLock (timeout/password) entries across many locale files.
Editor config
.editorconfig
Adds per-file EditorConfig overrides for JSON and Markdown newline/trailing-whitespace handling.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Menu as Menu/Context
  participant Timer as Inactivity Timer
  participant Main as Main Process
  participant Root as Root Window
  participant LockView as Lock BrowserView (Renderer)

  Note over Main: registerLockIpcHandlers() on app ready

  User->>Menu: select "Lock Screen"
  Menu->>Main: dispatch MENU_BAR_LOCK_SCREEN_CLICKED
  Timer-->>Main: auto-lock trigger (elapsed)

  Main->>Root: attach BrowserView overlay (load lockScreen.html)
  Root->>LockView: renderer boot (+ i18n)
  User->>LockView: submit password
  LockView->>Main: ipc invoke 'lock:verify'
  Main->>Main: verify (hash/migrate, throttle/backoff)
  alt verified
    Main-->>LockView: true
    LockView->>Main: ipc 'lock:unlock'
    Main->>Root: remove overlay, restore state
  else failed
    Main-->>LockView: false
    LockView->>User: show incorrect/unlockFailed
  end
Loading
sequenceDiagram
  autonumber
  participant Settings as Settings store
  participant Orch as setupScreenLock
  participant Timer as Inactivity Timer
  participant Window as Root Window events

  Settings->>Orch: screenLockTimeoutSeconds updated
  alt timeout > 0
    Orch->>Timer: start/reset timer
  else timeout == 0
    Orch->>Timer: clear timer (disabled)
  end

  Window->>Orch: focus/blur/activity events
  Orch->>Timer: clear/reset with grace handling
  Timer-->>Orch: elapsed -> dispatch MENU_BAR_LOCK_SCREEN_CLICKED
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I’m a rabbit guarding keys and screens,
I time the quiet, hush routines.
A hashed carrot behind the gate,
Timeout set — I patiently wait.
Hop in, unlock — the burrow greets, secure and keen. 🥕🔐

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly describes the core addition of an application lock feature and its IPC integration, matching the pull request’s primary objective without extraneous details. It uses conventional commit formatting and is specific enough for teammates to understand the main change at a glance. There is no misleading or unrelated content.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between da6963c and 283980b.

📒 Files selected for processing (6)
  • src/app/main/app.ts (8 hunks)
  • src/app/main/screenLock.ts (1 hunks)
  • src/ipc/channels.ts (1 hunks)
  • src/main.ts (3 hunks)
  • src/store/rootReducer.ts (3 hunks)
  • src/ui/components/SettingsView/features/ScreenLock.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/ui/components/SettingsView/features/ScreenLock.tsx
  • src/store/rootReducer.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Follow existing TypeScript patterns with strict mode enabled
All new code must pass ESLint and TypeScript checks
Avoid unnecessary comments; only add comments for complex logic or non-obvious decisions

Files:

  • src/app/main/screenLock.ts
  • src/ipc/channels.ts
  • src/main.ts
  • src/app/main/app.ts
src/ipc/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Define and maintain IPC channels in src/ipc/ for main/renderer state synchronization

Files:

  • src/ipc/channels.ts
src/main.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Keep src/main.ts as the Electron main process entry point compiled by Rollup

Files:

  • src/main.ts
🧬 Code graph analysis (3)
src/app/main/screenLock.ts (5)
src/store/index.ts (1)
  • dispatch (38-40)
src/ui/actions.ts (4)
  • MENU_BAR_LOCK_SCREEN_CLICKED (154-154)
  • WEBVIEW_FOCUS_REQUESTED (69-69)
  • WEBVIEW_MESSAGE_BOX_FOCUSED (71-71)
  • WEBVIEW_USER_LOGGED_IN (85-85)
src/store/rootReducer.ts (1)
  • RootState (111-111)
src/ui/reducers/screenLockTimeoutSeconds.ts (1)
  • screenLockTimeoutSeconds (6-22)
src/ui/main/rootWindow.ts (1)
  • getRootWindow (47-52)
src/main.ts (3)
src/store/index.ts (1)
  • select (63-64)
src/app/main/app.ts (1)
  • showLockWindow (279-412)
src/app/main/screenLock.ts (1)
  • setupScreenLock (64-196)
src/app/main/app.ts (6)
src/store/index.ts (4)
  • select (63-64)
  • dispatch (38-40)
  • listen (88-114)
  • listen (144-157)
src/ui/main/rootWindow.ts (1)
  • getRootWindow (47-52)
src/app/actions.ts (1)
  • APP_IS_SCREEN_LOCKED_SET (13-13)
src/ui/actions.ts (3)
  • SETTINGS_SET_SCREEN_LOCK_PASSWORD_HASHED (150-151)
  • SETTINGS_SET_SCREEN_LOCK_PASSWORD_CHANGED (148-149)
  • MENU_BAR_LOCK_SCREEN_CLICKED (154-154)
src/app/PersistableValues.ts (1)
  • ScreenLockPasswordStored (95-100)
src/utils/browserLauncher.ts (1)
  • preloadBrowsersList (96-103)
🔇 Additional comments (18)
src/main.ts (2)

7-7: LGTM! Imports are properly structured.

The new imports for showLockWindow, setupScreenLock, and select are correctly placed and align with the lock feature implementation.

Also applies to: 14-14, 30-30


81-91: LGTM! Lock restoration on startup is well-implemented.

The lock-check logic correctly:

  • Reads persisted lock state via select((s) => s.isScreenLocked)
  • Calls showLockWindow() when the app was previously locked
  • Wraps the check in try/catch for startup resilience
  • Calls setupScreenLock() to enable automatic locking

The implementation ensures users remain locked out after app restart if they were locked before.

src/ipc/channels.ts (1)

68-70: LGTM! IPC channel definitions are properly typed.

The three new lock-related IPC channels are correctly defined with appropriate type signatures that match the handler implementations in src/app/main/app.ts.

src/app/main/app.ts (9)

52-77: LGTM! Security configuration is well-designed.

The throttling and password hashing configurations demonstrate strong security practices:

  • Throttling: 5 attempts per 5-minute window with exponential backoff (500ms to 30s)
  • KDF: Scrypt with robust parameters (N=16384, r=8, p=1, keylen=32, maxmem=32MB)

These parameters align with current security best practices for protecting against brute-force attacks and ensuring password strength.


85-117: LGTM! Password hashing is secure and non-blocking.

The hashPlainPassword function correctly:

  • Uses cryptographically strong scrypt KDF
  • Generates random 16-byte salt
  • Returns base64-encoded hash and salt with algorithm metadata
  • Runs asynchronously to avoid blocking the main thread

The structured output format enables future algorithm upgrades while maintaining backward compatibility.


119-250: LGTM! Password verification supports multiple formats securely.

The verifyPassword function demonstrates excellent backward compatibility:

  • Scrypt (new format): Proper parameter validation and timing-safe comparison
  • PBKDF2 (previous format): String parsing and verification
  • Legacy SHA-256 (old format): Hex string fallback

All comparisons use crypto.timingSafeEqual to prevent timing attacks. The async implementation avoids blocking the main thread during expensive KDF operations.


279-412: LGTM! Lock window implementation is secure and robust.

The showLockWindow function correctly implements:

  • Security hardening: nodeIntegration: false, contextIsolation: true, preload script
  • State management: Saves and restores window bounds and flags
  • Proper cleanup: Removes listeners and BrowserView on destroy
  • Error resilience: Try/catch blocks prevent crashes during window state manipulation

The BrowserView approach (overlaying within the app window) is better than a separate window, as it prevents users from accessing other apps without unlocking.


424-505: LGTM! Password verification handler is properly secured.

The lock:verify IPC handler implements robust security measures:

  • Sender verification: Only accepts requests from lock BrowserView (lines 427-429)
  • Throttling: Per-client attempt tracking with lockout window
  • Exponential backoff: Delays increase from 500ms to 30s
  • Memory management: Auto-expires entries via setTimeout to prevent unbounded growth

The implementation effectively mitigates brute-force attacks while maintaining a good user experience.


507-540: LGTM! Unlock handler is properly restricted.

The lock:unlock IPC handler correctly:

  • Sender verification: Only accepts requests from lock BrowserView (lines 510-512)
  • State cleanup: Removes BrowserView, destroys webContents, updates Redux state
  • Window restoration: Shows and focuses root window

This prevents unauthorized unlocking from other renderers.


542-576: LGTM! Password setter is properly gated.

The lock:set IPC handler correctly restricts access:

  • Sender verification: Only allows lock BrowserView or root window (Settings UI)
  • Secure hashing: Uses hashPlainPassword with scrypt
  • Error handling: Throws meaningful errors on unauthorized access

The dual-source allowlist (lock view + settings) balances security with UX for legitimate password changes.


696-697: LGTM! Lock IPC handlers are registered at the correct time.

Calling registerLockIpcHandlers() immediately after preloadBrowsersList() in setupApp ensures handlers are available before any renderer attempts to use them, avoiding race conditions.


724-749: LGTM! Redux action listeners are properly wired.

The action listeners correctly:

  • Hash plaintext passwords from settings UI and persist securely
  • Trigger lock window display when menu item is clicked
  • Use async/await appropriately for asynchronous operations

This completes the integration between UI actions and lock functionality.

src/app/main/screenLock.ts (6)

13-23: LGTM! State management is well-organized.

The module-level state variables are clearly documented and serve specific purposes:

  • suspended: Prevents auto-lock immediately after locking
  • rootWindowFocused: Tracked flag avoids race conditions with async isFocused() checks
  • lastBlurAt, blurStartTimeout: Implement delayed lock start to avoid false triggers
  • GRACE_WINDOW_MS, MIN_CLEAR_IGNORE_MS: Constants prevent race conditions between blur and activity events

This design demonstrates careful consideration of timing edge cases.


25-41: LGTM! Timer clearing logic handles races correctly.

The clearTimer function implements smart filtering:

  • Ignores redux-action clears for 400ms after timer start to prevent internal state updates from canceling legitimate timeouts
  • Other activity types (user-activity-action, ipc-message, before-input-event) always clear the timer

This prevents false negatives (user is active but timer keeps running) while avoiding false positives from internal Redux actions.


43-62: LGTM! Timer start logic is clean and defensive.

The startTimer function correctly:

  • Clears any existing timer before starting
  • Respects suspended flag to prevent auto-lock right after locking
  • Dispatches MENU_BAR_LOCK_SCREEN_CLICKED to reuse existing lock flow
  • Sets suspended = true after locking to prevent immediate restart

This design avoids duplicate code by reusing the menu-triggered lock path.


64-92: LGTM! Timeout watcher handles window readiness correctly.

The store watcher implements proper retry logic:

  • Uses evaluateFocusAndMaybeStart closure with 500ms retry when root window isn't ready
  • Only starts timer when window is unfocused
  • Clears timer when timeout is disabled (set to 0)

This prevents premature locking during app startup before the window is ready.


94-120: LGTM! User activity detection is sophisticated.

The onUserActivity handler implements multiple safeguards:

  • Grace window: Ignores activity within 300ms of blur (prevents blur-triggered events from canceling the timer)
  • Focus check: Only treats activity as valid when root window is focused
  • Blur cancellation: Clears any pending delayed blur start

This multi-layered approach prevents false positives while catching genuine user activity.


122-196: LGTM! Root window listener attachment is robust.

The attachRootWindowListeners function demonstrates excellent resilience:

  • Retry logic: Retries every 500ms if root window isn't ready (lines 191-193)
  • Focus/blur handling: Properly manages rootWindowFocused flag and timer state
  • Delayed blur start: 500ms delay prevents spurious locks during window switching (lines 145-155)
  • Activity detection: Both before-input-event and ipc-message serve as activity signals
  • Grace window checks: IPC activity within 300ms of blur is ignored (lines 174-175)

The comprehensive event coverage ensures accurate detection of user presence across different interaction patterns.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (9)
rollup.config.mjs (1)

101-103: Centralize copying UI HTML in the main build config

To avoid duplication and ordering surprises, move copying of src/ui/*.html into the existing copy() plugin in the main (src/main.ts) build config, and remove it here. If lock UI references nested assets (css/js/images), prefer a recursive glob.

Apply within this segment:

-      copy({
-        targets: [{ src: 'src/ui/*.html', dest: 'app' }],
-      }),

Then update the copy() in the main config (around lines 251-256) to include UI HTML and, if needed, nested assets, e.g.:

copy({
  targets: [
    { src: 'src/public/*', dest: 'app' },
    { src: 'src/ui/*.html', dest: 'app' },
    // If UI uses additional assets:
    // { src: 'src/ui/assets/**/*', dest: 'app/ui/assets' },
    { src: 'node_modules/@rocket.chat/icons/dist/*', dest: 'app/icons' },
  ],
  // hook: 'writeBundle', // optional: run after bundles are written
})

Based on learnings

src/main.ts (1)

85-87: Guard against getRootWindow() rejection to avoid startup abort

getRootWindow() can reject on timing; handle errors to keep startup resilient.

Apply:

-  const mainWindow = await getRootWindow();
-  lockManager.initialize(mainWindow);
+  await getRootWindow()
+    .then((win) => {
+      lockManager.initialize(win);
+    })
+    .catch((err) => {
+      // eslint-disable-next-line no-console
+      console.error('Failed to initialize lock manager (no root window):', err);
+    });
src/lockSettings/main.ts (1)

45-63: Lock Settings window creation — LGTM; minor UX hardening optional

Config looks good for security. Optionally:

  • autoHideMenuBar: true to reduce chrome
  • parent + modal to keep it on top of main window

Example:

   settingsWindow = new BrowserWindow({
     width: 700,
     height: 700,
     resizable: false,
     minimizable: false,
     maximizable: false,
     title: 'Lock Settings',
+    autoHideMenuBar: true,
+    // parent: someMainWindowRef,
+    // modal: true,
     webPreferences: {
       preload: path.join(__dirname, 'preload.js'),
       contextIsolation: true,
       nodeIntegration: false,
       sandbox: false,
     },
   });
src/lockPreload.ts (1)

65-68: Freeze exposed API object

Prevent runtime mutation from renderer.

-  if (process.env.NODE_ENV !== 'production') {
-    (lockAPI as ElectronAPI).forceLock = () => ipcRenderer.invoke('lock:force');
-  }
+  if (process.env.NODE_ENV !== 'production') {
+    (lockAPI as ElectronAPI).forceLock = () => ipcRenderer.invoke('lock:force');
+  }
+  Object.freeze(lockAPI);
src/ui/main/menuBar.ts (2)

517-538: Localize new menu labels and review shortcuts

Use i18n for 'Lock Settings' and 'Lock' like other items, and confirm accelerators don’t conflict.

-        label: 'Lock Settings',
+        label: t('menus.lockSettings'),
...
-        label: 'Lock',
+        label: t('menus.lockNow'),

772-793: Remove dead/commented lock menu block or add TODO

Avoid large commented blocks in production code. Either remove or gate behind a feature flag.

src/ui/lockSettings.html (1)

210-218: Harden password inputs (autocomplete) and UX

Disable password managers and autocorrect; consider allowing timeout-only updates without forcing password change.

-<input
-    type="password"
+<input
+    type="password"
     id="password"
     name="password"
-    placeholder="Enter password (min 4 characters)"
+    placeholder="Enter password (min 4 characters)"
+    autocomplete="new-password"
+    autocapitalize="none"
+    spellcheck="false"
     required
     minlength="4"
 >
...
-<input
-    type="password"
+<input
+    type="password"
     id="confirmPassword"
     name="confirmPassword"
     placeholder="Re-enter password"
+    autocomplete="new-password"
+    autocapitalize="none"
+    spellcheck="false"
     required
 >

Optional: accept empty password to keep current (requires IPC/API change).

Also applies to: 221-230

src/types/global.d.ts (1)

5-18: Use proper global augmentation

Wrap with declare global to avoid interface shadowing issues.

-interface Window {
-  electronAPI: {
-    hasLockConfig: () => Promise<boolean>;
-    getLockConfig: () => Promise<{ timeoutSeconds: number } | null>;
-    setLockConfig: (
-      password: string,
-      timeoutSeconds: number
-    ) => Promise<{ success: boolean; error?: string }>;
-    verifyPassword: (password: string) => Promise<boolean>;
-    unlockApp: () => Promise<void>;
-    onLock: (callback: () => void) => () => void;
-    onUnlock: (callback: () => void) => () => void;
-  };
-}
+declare global {
+  interface Window {
+    electronAPI: {
+      hasLockConfig: () => Promise<boolean>;
+      getLockConfig: () => Promise<{ timeoutSeconds: number } | null>;
+      setLockConfig: (
+        password: string,
+        timeoutSeconds: number
+      ) => Promise<{ success: boolean; error?: string }>;
+      verifyPassword: (password: string) => Promise<boolean>;
+      unlockApp: () => Promise<void>;
+      onLock: (callback: () => void) => () => void;
+      onUnlock: (callback: () => void) => () => void;
+    };
+  }
+}
src/lockIPC.ts (1)

17-19: Single source of truth for MIN_TIMEOUT_SECONDS

Avoid drift; export from lockManager or a constants module and import here.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d614c2c and da6f60c.

📒 Files selected for processing (14)
  • rollup.config.mjs (1 hunks)
  • src/lockConfig.ts (1 hunks)
  • src/lockIPC.ts (1 hunks)
  • src/lockManager.ts (1 hunks)
  • src/lockPreload.ts (1 hunks)
  • src/lockRenderer.ts (1 hunks)
  • src/lockSettings/main.ts (1 hunks)
  • src/main.ts (4 hunks)
  • src/preload.ts (2 hunks)
  • src/rootWindow.ts (2 hunks)
  • src/types/global.d.ts (1 hunks)
  • src/ui/lockScreen.html (1 hunks)
  • src/ui/lockSettings.html (1 hunks)
  • src/ui/main/menuBar.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Follow existing TypeScript patterns with strict mode enabled
All new code must pass ESLint and TypeScript checks
Avoid unnecessary comments; only add comments for complex logic or non-obvious decisions

Files:

  • src/types/global.d.ts
  • src/lockSettings/main.ts
  • src/main.ts
  • src/ui/main/menuBar.ts
  • src/lockPreload.ts
  • src/rootWindow.ts
  • src/preload.ts
  • src/lockIPC.ts
  • src/lockConfig.ts
  • src/lockManager.ts
  • src/lockRenderer.ts
{rollup.config.mjs,electron-builder.json,tsconfig.json,.eslintrc.json,jest.config.js}

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain and configure the project via the designated root config files for build, packaging, TypeScript, ESLint, and Jest

Files:

  • rollup.config.mjs
src/main.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Keep src/main.ts as the Electron main process entry point compiled by Rollup

Files:

  • src/main.ts
src/rootWindow.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Keep src/rootWindow.ts as the renderer entry for the main window UI

Files:

  • src/rootWindow.ts
src/preload.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Keep src/preload.ts as the preload script bridging main and renderer

Files:

  • src/preload.ts
🧠 Learnings (3)
📚 Learning: 2025-09-26T19:24:21.364Z
Learnt from: CR
PR: RocketChat/Rocket.Chat.Electron#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-26T19:24:21.364Z
Learning: Applies to src/rootWindow.ts : Keep `src/rootWindow.ts` as the renderer entry for the main window UI

Applied to files:

  • src/main.ts
📚 Learning: 2025-09-26T19:24:21.364Z
Learnt from: CR
PR: RocketChat/Rocket.Chat.Electron#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-26T19:24:21.364Z
Learning: Applies to src/preload.ts : Keep `src/preload.ts` as the preload script bridging main and renderer

Applied to files:

  • src/lockPreload.ts
  • src/preload.ts
📚 Learning: 2025-09-26T19:24:21.364Z
Learnt from: CR
PR: RocketChat/Rocket.Chat.Electron#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-26T19:24:21.364Z
Learning: Applies to src/ipc/**/*.ts : Define and maintain IPC channels in `src/ipc/` for main/renderer state synchronization

Applied to files:

  • src/lockIPC.ts
🧬 Code graph analysis (7)
src/lockSettings/main.ts (1)
src/lockManager.ts (1)
  • lockManager (232-232)
src/main.ts (3)
src/lockIPC.ts (1)
  • setupLockIPC (23-34)
src/ui/main/rootWindow.ts (1)
  • getRootWindow (47-52)
src/lockManager.ts (1)
  • lockManager (232-232)
src/ui/main/menuBar.ts (2)
src/lockManager.ts (1)
  • lockManager (232-232)
src/lockSettings/main.ts (1)
  • openLockSettings (17-69)
src/rootWindow.ts (1)
src/lockRenderer.ts (1)
  • initializeLockScreen (9-12)
src/preload.ts (1)
src/lockPreload.ts (1)
  • exposeLockAPI (28-71)
src/lockIPC.ts (2)
src/lockConfig.ts (4)
  • hasLockConfig (186-194)
  • loadLockConfig (63-104)
  • saveLockConfig (112-128)
  • verifyPassword (135-181)
src/lockManager.ts (1)
  • lockManager (232-232)
src/lockManager.ts (1)
src/lockConfig.ts (1)
  • loadLockConfig (63-104)
🔇 Additional comments (5)
src/preload.ts (1)

6-6: Expose lock API early in preload — LGTM

Calling exposeLockAPI() at preload top-level ensures window.electronAPI is ready before renderer runs.

Confirm that a global type for window.electronAPI (ElectronAPI) is declared (e.g., in src/types/global.d.ts) to satisfy TS strict mode in consumers.

Also applies to: 81-81

src/main.ts (1)

20-22: IPC initialization placement — LGTM

Initializing lock IPC early in startup is correct and low risk.

Also applies to: 61-61

src/lockConfig.ts (2)

112-128: Good: scrypt with per-record salt and timingSafeEqual

Solid choices; legacy path migrates on success.

Ensure electron-store file permissions are default-safe in your packaging and that no logs ever include plaintext passwords. Based on learnings


63-104: Graceful legacy handling looks correct

Null on parse errors, no throws; migration deferred until successful verify.

src/lockPreload.ts (1)

65-71: No duplicate electronAPI exposure detected Only one contextBridge.exposeInMainWorld('electronAPI', …) call exists (in src/lockPreload.ts, invoked via exposeLockAPI() in src/preload.ts); no merge necessary.

Likely an incorrect or invalid review comment.

@CorneiZeR CorneiZeR marked this pull request as draft October 6, 2025 20:04
@CorneiZeR CorneiZeR marked this pull request as ready for review October 7, 2025 14:05
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

🧹 Nitpick comments (12)
src/i18n/tr-TR.i18n.json (1)

111-113: Polish the Turkish phrasing for clarity

“Devre dışı bırakmak için 0 ayarlayın.” is understandable, but the more natural Turkish phrasing is “0 olarak ayarlayın.” Consider tweaking the sentence to read more fluidly.

src/i18n/es.i18n.json (1)

427-434: Add the menu label translation too.

We render a “Lock Screen” menu item via menus.lockScreen. English already defines it, but this locale still falls back to English. Please add the Spanish string alongside the other menu entries (e.g., "lockScreen": "Bloquear pantalla").

src/i18n/ru.i18n.json (1)

399-406: Provide the menu translation for completeness.

The main menu reads menus.lockScreen; without a Russian string it stays in English. Please add an entry such as "lockScreen": "Заблокировать экран" next to the other menus keys.

src/ui/components/SettingsView/features/ScreenLock.tsx (1)

91-105: Prefer Fuselage spacing/tokens over inline styles.

Replace raw style (gap/padding/width) with Fuselage props/tokens (mi, p, w, etc.) for consistency and theme compliance.

Also applies to: 116-123

src/lockScreen/LockScreen.tsx (1)

44-74: Optional: use Fuselage components for consistency/theming.

Consider replacing raw HTML with Fuselage (Box, TextInput, Button, Field) for consistent styling and a11y.

src/app/main/screenLock.ts (1)

70-81: Confirm behavior: timer only runs while app is unfocused.

Current logic starts the inactivity timer only when the root window is not focused. If true inactivity (no input while focused) is desired, you’ll need to start/reset the timer on before-input-event while focused too (or use powerMonitor idle signals).

src/app/main/app.ts (3)

65-76: Support throttling state; and remove unused listener fields

Add counters used by the throttling above, and drop unused moveListener/resizeListener from lockState.

Apply this diff:

 let lockWindow: BrowserView | null = null;

-// Track original rootWindow state while locked so we can restore it on unlock
-const lockState: {
-  originalBounds?: Electron.Rectangle;
-  prevResizable?: boolean;
-  prevMinimizable?: boolean;
-  prevMaximizable?: boolean;
-  moveListener?: () => void;
-  resizeListener?: () => void;
-} = {};
+// Throttling state for verify attempts
+let lockVerifyAttemptCount = 0;
+let lockVerifyLastAttemptTs = 0;
+
+// Track original rootWindow state while locked so we can restore it on unlock
+const lockState: {
+  originalBounds?: Electron.Rectangle;
+  prevResizable?: boolean;
+  prevMinimizable?: boolean;
+  prevMaximizable?: boolean;
+  // prevMovable tracked separately via (lockState as any).prevMovable
+} = {};

169-176: Remove cleanup for listeners that are never registered

moveListener/resizeListener are never assigned. Keep only the explicit removals of updateBounds below.

Apply this diff:

-        if (lockState.moveListener) {
-          rootWindow.removeListener('move', lockState.moveListener);
-        }
-        if (lockState.resizeListener) {
-          rootWindow.removeListener('resize', lockState.resizeListener);
-        }

134-137: 'move' listener is unnecessary; 'setAutoResize' covers size changes

Moving the window doesn’t change content bounds; the BrowserView remains aligned at {0,0}. Keeping only resize keeps things simpler.

Apply this diff:

-    // keep bounds updated on move/resize
-    rootWindow.addListener('resize', updateBounds);
-    rootWindow.addListener('move', updateBounds);
+    // keep bounds updated on resize
+    rootWindow.addListener('resize', updateBounds);
-        // remove the updateBounds listeners we added earlier
+        // remove the updateBounds listener we added earlier
         rootWindow.removeListener('resize', updateBounds);
-        rootWindow.removeListener('move', updateBounds);

Also applies to: 176-179

src/lockScreen/lock-screen.tsx (3)

2-2: Type the React root

Use the Root type instead of any.

Apply this diff:

-import { createRoot } from 'react-dom/client';
+import { createRoot } from 'react-dom/client';
+import type { Root } from 'react-dom/client';
@@
-let reactRoot: any = null;
+let reactRoot: Root | null = null;

As per coding guidelines.

Also applies to: 9-9


13-16: Normalize browser language to base locale before loading resources

navigator.language may be 'en-US'. Map to base ('en') and fall back if missing.

Apply this diff:

-    const browserLang = (navigator.language ||
-      fallbackLng) as keyof typeof resources;
-    const lng = browserLang || fallbackLng;
+    const requested = navigator.language || '';
+    const base = requested.split('-')[0] as keyof typeof resources;
+    const lng = (resources[base] ? base : fallbackLng) as keyof typeof resources;
@@
-    if (resources[lng]) {
+    if (resources[lng]) {
       try {
         res[lng] = { translation: await resources[lng]() };
       } catch (e) {
         // ignore, will fallback
       }
     }
@@
-    if (!res[fallbackLng]) {
+    if (!res[fallbackLng]) {
       res[fallbackLng] = { translation: await resources[fallbackLng]() };
     }

Also applies to: 21-27, 29-33


49-53: Use a one‑time DOMContentLoaded listener to avoid duplicate wiring

Slight cleanup; behavior unchanged.

Apply this diff:

-    return new Promise<void>((resolve) => {
-      document.addEventListener('DOMContentLoaded', () =>
-        start().then(resolve).catch(resolve)
-      );
-    });
+    return new Promise<void>((resolve) => {
+      document.addEventListener(
+        'DOMContentLoaded',
+        () => { start().then(resolve).catch(resolve); },
+        { once: true }
+      );
+    });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between da6f60c and e379ef5.

📒 Files selected for processing (40)
  • .editorconfig (1 hunks)
  • rollup.config.mjs (1 hunks)
  • src/app/PersistableValues.ts (2 hunks)
  • src/app/main/app.ts (5 hunks)
  • src/app/main/screenLock.ts (1 hunks)
  • src/app/selectors.ts (1 hunks)
  • src/i18n/ar.i18n.json (1 hunks)
  • src/i18n/de-DE.i18n.json (2 hunks)
  • src/i18n/en.i18n.json (3 hunks)
  • src/i18n/es.i18n.json (2 hunks)
  • src/i18n/fi.i18n.json (2 hunks)
  • src/i18n/fr.i18n.json (2 hunks)
  • src/i18n/hu.i18n.json (2 hunks)
  • src/i18n/it-IT.i18n.json (1 hunks)
  • src/i18n/ja.i18n.json (1 hunks)
  • src/i18n/nb-NO.i18n.json (1 hunks)
  • src/i18n/nn.i18n.json (1 hunks)
  • src/i18n/no.i18n.json (2 hunks)
  • src/i18n/pl.i18n.json (2 hunks)
  • src/i18n/pt-BR.i18n.json (2 hunks)
  • src/i18n/ru.i18n.json (2 hunks)
  • src/i18n/se.i18n.json (1 hunks)
  • src/i18n/sv.i18n.json (2 hunks)
  • src/i18n/tr-TR.i18n.json (2 hunks)
  • src/i18n/uk-UA.i18n.json (2 hunks)
  • src/i18n/zh-CN.i18n.json (2 hunks)
  • src/i18n/zh-TW.i18n.json (2 hunks)
  • src/i18n/zh.i18n.json (1 hunks)
  • src/lockScreen/LockScreen.tsx (1 hunks)
  • src/lockScreen/lock-screen.tsx (1 hunks)
  • src/main.ts (2 hunks)
  • src/public/lockScreen.html (1 hunks)
  • src/store/rootReducer.ts (2 hunks)
  • src/ui/actions.ts (2 hunks)
  • src/ui/components/SettingsView/GeneralTab.tsx (2 hunks)
  • src/ui/components/SettingsView/features/ScreenLock.tsx (1 hunks)
  • src/ui/main/menuBar.ts (2 hunks)
  • src/ui/main/serverView/popupMenu.ts (2 hunks)
  • src/ui/reducers/screenLockPasswordHash.ts (1 hunks)
  • src/ui/reducers/screenLockTimeoutSeconds.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • src/i18n/nn.i18n.json
  • src/public/lockScreen.html
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Follow existing TypeScript patterns with strict mode enabled
All new code must pass ESLint and TypeScript checks
Avoid unnecessary comments; only add comments for complex logic or non-obvious decisions

Files:

  • src/app/PersistableValues.ts
  • src/ui/main/serverView/popupMenu.ts
  • src/app/selectors.ts
  • src/ui/main/menuBar.ts
  • src/lockScreen/LockScreen.tsx
  • src/store/rootReducer.ts
  • src/ui/components/SettingsView/features/ScreenLock.tsx
  • src/ui/reducers/screenLockPasswordHash.ts
  • src/main.ts
  • src/ui/actions.ts
  • src/app/main/screenLock.ts
  • src/ui/reducers/screenLockTimeoutSeconds.ts
  • src/app/main/app.ts
  • src/lockScreen/lock-screen.tsx
  • src/ui/components/SettingsView/GeneralTab.tsx
src/store/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Write Redux actions following the Flux Standard Action (FSA) convention

Files:

  • src/store/rootReducer.ts
src/ui/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

src/ui/**/*.tsx: Implement React UI with functional components and hooks
Use Fuselage components (Box, Button, TextInput, Modal, etc.) and import from @rocket.chat/fuselage instead of raw HTML elements
For Fuselage theming, validate tokens against Theme.d.ts and only use documented values
Name React component files in PascalCase; non-component files should follow camelCase naming

Files:

  • src/ui/components/SettingsView/features/ScreenLock.tsx
  • src/ui/components/SettingsView/GeneralTab.tsx
src/main.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Keep src/main.ts as the Electron main process entry point compiled by Rollup

Files:

  • src/main.ts
{rollup.config.mjs,electron-builder.json,tsconfig.json,.eslintrc.json,jest.config.js}

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain and configure the project via the designated root config files for build, packaging, TypeScript, ESLint, and Jest

Files:

  • rollup.config.mjs
🧬 Code graph analysis (11)
src/ui/main/serverView/popupMenu.ts (2)
src/store/index.ts (1)
  • dispatch (38-40)
src/ui/actions.ts (1)
  • MENU_BAR_LOCK_SCREEN_CLICKED (153-153)
src/app/selectors.ts (3)
src/store/rootReducer.ts (1)
  • RootState (109-109)
src/ui/reducers/screenLockTimeoutSeconds.ts (1)
  • screenLockTimeoutSeconds (6-22)
src/ui/reducers/screenLockPasswordHash.ts (1)
  • screenLockPasswordHash (6-22)
src/ui/main/menuBar.ts (3)
src/ui/main/rootWindow.ts (1)
  • getRootWindow (47-52)
src/store/index.ts (1)
  • dispatch (38-40)
src/ui/actions.ts (1)
  • MENU_BAR_LOCK_SCREEN_CLICKED (153-153)
src/ui/components/SettingsView/features/ScreenLock.tsx (4)
src/store/actions.ts (1)
  • RootAction (44-46)
src/store/rootReducer.ts (1)
  • RootState (109-109)
src/ui/reducers/screenLockTimeoutSeconds.ts (1)
  • screenLockTimeoutSeconds (6-22)
src/ui/actions.ts (2)
  • SETTINGS_SET_SCREEN_LOCK_TIMEOUT_CHANGED (145-146)
  • SETTINGS_SET_SCREEN_LOCK_PASSWORD_CHANGED (147-148)
src/ui/reducers/screenLockPasswordHash.ts (2)
src/ui/actions.ts (1)
  • SETTINGS_SET_SCREEN_LOCK_PASSWORD_HASHED (149-150)
src/app/actions.ts (1)
  • APP_SETTINGS_LOADED (6-6)
src/main.ts (1)
src/app/main/screenLock.ts (1)
  • setupScreenLock (64-193)
src/app/main/screenLock.ts (5)
src/store/index.ts (1)
  • dispatch (38-40)
src/ui/actions.ts (4)
  • MENU_BAR_LOCK_SCREEN_CLICKED (153-153)
  • WEBVIEW_FOCUS_REQUESTED (68-68)
  • WEBVIEW_MESSAGE_BOX_FOCUSED (70-70)
  • WEBVIEW_USER_LOGGED_IN (84-84)
src/store/rootReducer.ts (1)
  • RootState (109-109)
src/ui/reducers/screenLockTimeoutSeconds.ts (1)
  • screenLockTimeoutSeconds (6-22)
src/ui/main/rootWindow.ts (1)
  • getRootWindow (47-52)
src/ui/reducers/screenLockTimeoutSeconds.ts (2)
src/ui/actions.ts (1)
  • SETTINGS_SET_SCREEN_LOCK_TIMEOUT_CHANGED (145-146)
src/app/actions.ts (1)
  • APP_SETTINGS_LOADED (6-6)
src/app/main/app.ts (5)
src/app/main/persistence.ts (1)
  • getPersistedValues (28-29)
src/ui/main/rootWindow.ts (1)
  • getRootWindow (47-52)
src/utils/browserLauncher.ts (1)
  • preloadBrowsersList (96-103)
src/store/index.ts (3)
  • listen (88-114)
  • listen (144-157)
  • dispatch (38-40)
src/ui/actions.ts (3)
  • SETTINGS_SET_SCREEN_LOCK_PASSWORD_CHANGED (147-148)
  • SETTINGS_SET_SCREEN_LOCK_PASSWORD_HASHED (149-150)
  • MENU_BAR_LOCK_SCREEN_CLICKED (153-153)
src/lockScreen/lock-screen.tsx (1)
src/i18n/common.ts (1)
  • fallbackLng (3-3)
src/ui/components/SettingsView/GeneralTab.tsx (1)
src/ui/components/SettingsView/features/ScreenLock.tsx (1)
  • ScreenLock (26-127)
🔇 Additional comments (17)
src/i18n/hu.i18n.json (1)

272-283: LGTM! Translation structure is correct.

The Hungarian translations are properly structured with screenLock under settings.options and lockScreen as a top-level key. The keys and nesting align with the feature implementation.

Also applies to: 427-434

src/i18n/de-DE.i18n.json (1)

250-260: LGTM! German translations are correctly structured.

The translations follow the correct structure with screenLock under settings.options and lockScreen at the top level. All required keys are present.

Also applies to: 397-404

src/i18n/fr.i18n.json (1)

248-258: LGTM! French translations are correctly structured.

The French localization follows the correct pattern with screenLock nested under settings.options and lockScreen as a top-level key. All required translation keys are present.

Also applies to: 396-403

src/i18n/sv.i18n.json (1)

282-292: LGTM! Swedish translations are correctly structured.

The Swedish localization correctly places screenLock under settings.options and lockScreen at the top level, matching the structure used in other locales. All required keys are present.

Also applies to: 452-459

src/ui/actions.ts (2)

144-153: LGTM! Action constants are well-defined.

The new screen lock action constants follow the existing naming conventions and include helpful comments distinguishing between plaintext and hashed password actions. The naming is clear and consistent.


287-290: LGTM! Type mappings are correct.

The payload type mappings for the new screen lock actions are correctly defined:

  • number for timeout (seconds)
  • string for plaintext password input
  • string | null for hashed password (allowing null to clear)
  • void for the lock screen menu action
src/i18n/zh-CN.i18n.json (1)

132-142: LGTM!

The Chinese (zh-CN) translations for the screen lock feature are properly structured and consistent with the i18n patterns used in other locale files.

Also applies to: 278-285

src/ui/components/SettingsView/GeneralTab.tsx (1)

13-13: LGTM!

The ScreenLock component is properly imported and integrated into the general settings tab, following the established pattern for other settings features.

Also applies to: 33-33

src/app/selectors.ts (1)

72-75: LGTM!

The new selectors for screen lock state follow the established pattern and correctly map to the types defined in the reducers.

src/main.ts (1)

13-13: LGTM!

The screen lock setup is properly initialized after the root window is shown, following the established initialization pattern. The inline comment clearly explains the purpose.

Also applies to: 80-81

src/ui/main/serverView/popupMenu.ts (1)

218-225: Verify the placement of the lock screen item in context menus.

The lock screen menu item is added to createDefaultMenuTemplate, which means it will appear in every context menu across the application (right-click on text fields, images, links, etc.). Typically, application-level actions like "Lock Screen" are reserved for the main menu bar only, not content-specific context menus.

Consider whether this is the intended UX. If the lock screen action should only be accessible via the main menu bar and keyboard shortcut, remove it from the context menu and keep it only in src/ui/main/menuBar.ts.

If this placement is intentional, please confirm the reasoning for making the lock screen action available in all context menus.

src/ui/main/menuBar.ts (1)

29-29: LGTM!

The lock screen menu item is properly integrated into the application menu with appropriate window visibility and focus handling. The keyboard accelerator (CommandOrControl+L) is well-chosen and follows the established pattern.

Also applies to: 108-121

src/i18n/se.i18n.json (1)

1-28: LGTM!

The Swedish (se) translations for the screen lock feature are properly structured and consistent with the i18n patterns used across other locale files.

rollup.config.mjs (1)

111-144: LGTM!

The lock screen renderer bundle configuration correctly mirrors the video-call window bundle pattern, using the same plugin chain and external module resolution strategy. The configuration is appropriate for a dedicated renderer bundle.

src/store/rootReducer.ts (1)

41-42: Reducers wired correctly; consider redaction for sensitive state in devtools/logging.

The new slices are correctly imported and combined. Since screenLockPasswordHash is sensitive, ensure Redux devtools/loggers/state dumps sanitize or omit this field.

Also applies to: 105-107

src/lockScreen/lock-screen.tsx (1)

1-88: Renderer–main API availability: prefer preload over runtime injection

If the main process continues injecting window.electronAPI after did‑finish‑load, LockScreen code may run before injection. With the preload proposal in app.ts, this race goes away and the API is available at first paint.

Confirm LockScreen doesn’t access window.electronAPI before did‑finish‑load. If unsure, adopt the preload approach suggested in app.ts to guarantee availability.

src/app/main/app.ts (1)

138-141: Confirm lockScreen.html is packaged to app/lockScreen.html in production
lockScreen.html currently lives at src/public/lockScreen.html; ensure your build config (e.g. electron-builder’s files or extraResources) copies it to app/lockScreen.html relative to app.getAppPath(), otherwise loadFile will fail.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
src/app/main/app.ts (2)

229-310: LGTM! Proper rate-limiting and origin tracking.

The throttling mechanism correctly:

  • Identifies callers by sender ID
  • Enforces attempt limits per sender
  • Applies exponential backoff delays
  • Cleans up expired records

Note: Password hashing concern flagged in previous review.

As noted in past review comments (lines 496-510), the SHA-256 hashing (lines 256-259) should be replaced with a proper password hashing algorithm like bcrypt, scrypt, or pbkdf2 for better security against offline attacks.


496-510: Password hashing concern flagged in previous review.

As noted in the existing review comment for these lines, SHA-256 is unsuitable for password storage. The previous review recommends using a salted, slow hash function (pbkdf2, scrypt, or argon2) to protect against offline attacks if the storage leaks.

🧹 Nitpick comments (1)
src/lockScreen/preload.ts (1)

1-7: LGTM! Secure preload implementation.

The use of contextBridge with a minimal IPC surface is the correct approach for the lock screen. This properly isolates the renderer while exposing only the necessary unlock functionality.

Optional: Consider more specific return types.

The return types are currently Promise<any>. For better type safety, consider:

 contextBridge.exposeInMainWorld('electronAPI', {
-  verifyPassword: (password: string) =>
-    ipcRenderer.invoke('lock:verify', password),
-  unlockApp: () => ipcRenderer.invoke('lock:unlock'),
+  verifyPassword: (password: string): Promise<boolean> =>
+    ipcRenderer.invoke('lock:verify', password),
+  unlockApp: (): Promise<boolean> => ipcRenderer.invoke('lock:unlock'),
 });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e379ef5 and f84689a.

📒 Files selected for processing (2)
  • src/app/main/app.ts (6 hunks)
  • src/lockScreen/preload.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Follow existing TypeScript patterns with strict mode enabled
All new code must pass ESLint and TypeScript checks
Avoid unnecessary comments; only add comments for complex logic or non-obvious decisions

Files:

  • src/app/main/app.ts
  • src/lockScreen/preload.ts
🧠 Learnings (1)
📚 Learning: 2025-09-26T19:24:21.364Z
Learnt from: CR
PR: RocketChat/Rocket.Chat.Electron#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-26T19:24:21.364Z
Learning: Applies to src/preload.ts : Keep `src/preload.ts` as the preload script bridging main and renderer

Applied to files:

  • src/lockScreen/preload.ts
🧬 Code graph analysis (1)
src/app/main/app.ts (5)
src/app/main/persistence.ts (1)
  • getPersistedValues (28-29)
src/ui/main/rootWindow.ts (1)
  • getRootWindow (47-52)
src/utils/browserLauncher.ts (1)
  • preloadBrowsersList (96-103)
src/store/index.ts (3)
  • listen (88-114)
  • listen (144-157)
  • dispatch (38-40)
src/ui/actions.ts (3)
  • SETTINGS_SET_SCREEN_LOCK_PASSWORD_CHANGED (147-148)
  • SETTINGS_SET_SCREEN_LOCK_PASSWORD_HASHED (149-150)
  • MENU_BAR_LOCK_SCREEN_CLICKED (153-153)
🔇 Additional comments (5)
src/app/main/app.ts (5)

50-64: LGTM! Solid throttling implementation.

The rate-limiting approach is well-designed:

  • Per-sender tracking prevents cross-contamination
  • Exponential backoff with a reasonable cap (30s)
  • Automatic cleanup prevents memory leaks

128-136: LGTM! Secure BrowserView configuration.

The BrowserView correctly uses:

  • nodeIntegration: false
  • contextIsolation: true
  • Preload script at a fixed build path

This properly isolates the lock screen renderer from Node APIs.

Verify the preload path matches your build configuration.

Ensure that app/lockPreload.js is correctly output during the build process from src/lockScreen/preload.ts.


312-344: LGTM! Proper origin validation for unlock.

The unlock handler correctly validates that the request originates from the lock BrowserView itself (line 315), preventing any other renderer from bypassing the lock.


512-515: LGTM! Straightforward menu integration.

The listener correctly triggers the lock overlay when the menu item is clicked.


465-468: LGTM! Proper initialization sequence.

Registering the lock IPC handlers after app.whenReady() ensures Electron's IPC infrastructure is fully available.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
src/ui/components/SettingsView/features/ScreenLock.tsx (1)

70-77: Stop dispatching plaintext passwords through Redux.

Vi sender fortsatt råpassordet i SETTINGS_SET_SCREEN_LOCK_PASSWORD_CHANGED, så hele Redux‑kjeden (renderer, devtools, IPC logs) ser feltet i klartekst. Med den nye IPC‑strømmen for låselogikk bør renderer kalle den dedikerte preload‑metoden (f.eks. await window.electronAPI.screenLock.setPassword(password)), la main‑prosessen hashe og persistere, og deretter kun dispatch’e SETTINGS_SET_SCREEN_LOCK_PASSWORD_HASHED med resultatet (eller ingenting). Vennligst fjern plaintext‑dispatchen helt.

🧹 Nitpick comments (5)
src/app/main/screenLock.ts (2)

64-66: Make setupScreenLock idempotent

Prevent duplicate listeners if called twice.

Apply this diff:

 export const setupScreenLock = (): void => {
+  // Idempotency guard
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  if ((setupScreenLock as any)._initialized) return;
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  (setupScreenLock as any)._initialized = true;

25-41: Reset lastStartAt when timer is cleared

Prevents the “ignore clears within MIN_CLEAR_IGNORE_MS” window from lingering after a successful clear.

Apply this diff:

   if (inactivityTimer) {
     clearTimeout(inactivityTimer);
     inactivityTimer = null;
   }
+  lastStartAt = 0;
src/app/main/app.ts (3)

466-481: Don’t await KDF migration; keep verify snappy

The comment says “Fire-and-forget re-hash,” but it’s awaited. Remove await to return success immediately.

Apply this diff:

-            // Fire-and-forget re-hash; do not block unlock
-            await (async () => {
+            // Fire-and-forget re-hash; do not block unlock
+            (async () => {
               try {
                 const newStored = await hashPlainPassword(String(password));
                 dispatch({
                   type: SETTINGS_SET_SCREEN_LOCK_PASSWORD_HASHED,
                   payload: newStored as unknown as ScreenLockPasswordStored,
                 });
               } catch (e) {
                 // Do not prevent unlocking; just log migration failures
                 console.error(
                   'Error migrating legacy screen lock password to scrypt:',
                   e
                 );
               }
-            })();
+            })();

269-276: Remove unused lockState move/resize listener fields

Fields are never assigned; cleanup unused state and related removal code.

Apply this diff:

 const lockState: {
   originalBounds?: Electron.Rectangle;
   prevResizable?: boolean;
   prevMinimizable?: boolean;
   prevMaximizable?: boolean;
-  moveListener?: () => void;
-  resizeListener?: () => void;
 } = {};
-      // restore window flags and remove listeners
+      // restore window flags
       try {
-        if (lockState.moveListener) {
-          rootWindow.removeListener('move', lockState.moveListener);
-        }
-        if (lockState.resizeListener) {
-          rootWindow.removeListener('resize', lockState.resizeListener);
-        }
-
         // remove the updateBounds listeners we added earlier
         rootWindow.removeListener('resize', updateBounds);
         rootWindow.removeListener('move', updateBounds);
 
         if (typeof lockState.prevResizable === 'boolean') {
           rootWindow.setResizable(!!lockState.prevResizable);
         }

Also applies to: 359-367


414-524: Optional: fold unlock into verify to reduce surface

Consider removing 'lock:unlock' and performing unlock directly on successful 'lock:verify'. Simplifies IPC and avoids state drift between calls.

I can provide a patch if you want this change now.

Also applies to: 526-558

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f84689a and 253afec.

📒 Files selected for processing (14)
  • src/app/PersistableValues.ts (2 hunks)
  • src/app/main/app.ts (6 hunks)
  • src/app/main/screenLock.ts (1 hunks)
  • src/i18n/it-IT.i18n.json (1 hunks)
  • src/i18n/ja.i18n.json (1 hunks)
  • src/i18n/nb-NO.i18n.json (1 hunks)
  • src/i18n/no.i18n.json (2 hunks)
  • src/i18n/sv.i18n.json (3 hunks)
  • src/i18n/zh-TW.i18n.json (2 hunks)
  • src/i18n/zh.i18n.json (1 hunks)
  • src/lockScreen/LockScreen.tsx (1 hunks)
  • src/ui/actions.ts (3 hunks)
  • src/ui/components/SettingsView/features/ScreenLock.tsx (1 hunks)
  • src/ui/reducers/screenLockPasswordHash.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/i18n/it-IT.i18n.json
  • src/i18n/zh-TW.i18n.json
  • src/ui/actions.ts
  • src/i18n/nb-NO.i18n.json
  • src/i18n/sv.i18n.json
  • src/lockScreen/LockScreen.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Follow existing TypeScript patterns with strict mode enabled
All new code must pass ESLint and TypeScript checks
Avoid unnecessary comments; only add comments for complex logic or non-obvious decisions

Files:

  • src/app/PersistableValues.ts
  • src/app/main/app.ts
  • src/app/main/screenLock.ts
  • src/ui/components/SettingsView/features/ScreenLock.tsx
  • src/ui/reducers/screenLockPasswordHash.ts
src/ui/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

src/ui/**/*.tsx: Implement React UI with functional components and hooks
Use Fuselage components (Box, Button, TextInput, Modal, etc.) and import from @rocket.chat/fuselage instead of raw HTML elements
For Fuselage theming, validate tokens against Theme.d.ts and only use documented values
Name React component files in PascalCase; non-component files should follow camelCase naming

Files:

  • src/ui/components/SettingsView/features/ScreenLock.tsx
🧬 Code graph analysis (4)
src/app/main/app.ts (6)
src/app/main/persistence.ts (1)
  • getPersistedValues (28-29)
src/ui/main/rootWindow.ts (1)
  • getRootWindow (47-52)
src/store/index.ts (3)
  • dispatch (38-40)
  • listen (88-114)
  • listen (144-157)
src/ui/actions.ts (3)
  • SETTINGS_SET_SCREEN_LOCK_PASSWORD_HASHED (150-151)
  • SETTINGS_SET_SCREEN_LOCK_PASSWORD_CHANGED (148-149)
  • MENU_BAR_LOCK_SCREEN_CLICKED (154-154)
src/app/PersistableValues.ts (1)
  • ScreenLockPasswordStored (95-100)
src/utils/browserLauncher.ts (1)
  • preloadBrowsersList (96-103)
src/app/main/screenLock.ts (5)
src/store/index.ts (1)
  • dispatch (38-40)
src/ui/actions.ts (4)
  • MENU_BAR_LOCK_SCREEN_CLICKED (154-154)
  • WEBVIEW_FOCUS_REQUESTED (69-69)
  • WEBVIEW_MESSAGE_BOX_FOCUSED (71-71)
  • WEBVIEW_USER_LOGGED_IN (85-85)
src/store/rootReducer.ts (1)
  • RootState (109-109)
src/ui/reducers/screenLockTimeoutSeconds.ts (1)
  • screenLockTimeoutSeconds (6-22)
src/ui/main/rootWindow.ts (1)
  • getRootWindow (47-52)
src/ui/components/SettingsView/features/ScreenLock.tsx (5)
src/store/index.ts (1)
  • dispatch (38-40)
src/store/actions.ts (1)
  • RootAction (44-46)
src/store/rootReducer.ts (1)
  • RootState (109-109)
src/ui/reducers/screenLockTimeoutSeconds.ts (1)
  • screenLockTimeoutSeconds (6-22)
src/ui/actions.ts (2)
  • SETTINGS_SET_SCREEN_LOCK_TIMEOUT_CHANGED (146-147)
  • SETTINGS_SET_SCREEN_LOCK_PASSWORD_CHANGED (148-149)
src/ui/reducers/screenLockPasswordHash.ts (3)
src/app/PersistableValues.ts (1)
  • ScreenLockPasswordStored (95-100)
src/ui/actions.ts (1)
  • SETTINGS_SET_SCREEN_LOCK_PASSWORD_HASHED (150-151)
src/app/actions.ts (1)
  • APP_SETTINGS_LOADED (6-6)
🔇 Additional comments (3)
src/i18n/ja.i18n.json (1)

206-230: Translated lock-screen strings look solid.

Key structure matches other locales, and the Japanese copy conveys the settings/lock-screen context naturally. 👍

src/app/main/screenLock.ts (1)

135-187: Solid race handling around blur/focus and IPC activity

Grace windows, delayed blur start, and focus checks look robust and well thought out. LGTM.

src/app/main/app.ts (1)

526-558: Good: unlock gated to lock BrowserView sender

Validates event.sender against lockWindow.webContents before unlocking. This meaningfully closes the bypass.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (4)
src/app/actions.ts (1)

13-13: LGTM; add typed action creator for ergonomics (optional)

Constant and payload mapping look good. Consider exporting a small creator like setAppIsScreenLocked(locked: boolean) to avoid ad-hoc action shapes.

Also applies to: 24-24

src/lockScreen/LockScreen.tsx (1)

45-49: Minor UX/a11y: submit form, clear error on typing, and add ARIA

Use a form submit to handle Enter, clear error as user types, and add aria attributes.

-  const onKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
-    if (e.key === 'Enter') {
-      tryUnlock();
-    }
-  };
+  const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
+    e.preventDefault();
+    void tryUnlock();
+  };
-  return (
-    <div style={{ textAlign: 'center', color: 'white' }}>
+  return (
+    <div style={{ textAlign: 'center', color: 'white' }}>
       <h2 style={{ margin: 0 }}>{t('lockScreen.title')}</h2>
@@
-      <div style={{ marginTop: 18 }}>
-        <input
+      <form style={{ marginTop: 18 }} onSubmit={onSubmit}>
+        <input
           type='password'
           value={password}
-          onChange={(e) => setPassword(e.target.value)}
-          onKeyDown={onKey}
+          onChange={(e) => {
+            setError(null);
+            setPassword(e.target.value);
+          }}
           placeholder={t('lockScreen.placeholder')}
+          aria-label={t('lockScreen.placeholder')}
+          aria-invalid={Boolean(error)}
+          aria-describedby={error ? 'lock-error' : undefined}
@@
-        <button
-          onClick={tryUnlock}
-          disabled={busy}
+        <button
+          type="submit"
+          disabled={busy || password.length === 0}
           style={{ marginLeft: 8, padding: '8px 12px', borderRadius: 4 }}
         >
           {t('lockScreen.unlock')}
         </button>
-      </div>
-      {error && <div style={{ color: '#ff6b6b', marginTop: 12 }}>{error}</div>}
+      </form>
+      {error && (
+        <div id="lock-error" style={{ color: '#ff6b6b', marginTop: 12 }}>
+          {error}
+        </div>
+      )}

Also applies to: 51-83

src/ui/components/SettingsView/features/ScreenLock.tsx (1)

54-66: Avoid dispatching Redux actions on every keystroke.

The handleTimeoutInput handler currently dispatches SETTINGS_SET_SCREEN_LOCK_TIMEOUT_CHANGED on every keystroke, which can cause excessive Redux updates and performance issues. The blur handler (handleTimeoutChange) already commits the final value when the user finishes editing.

Refactor handleTimeoutInput to only update local state:

 const handleTimeoutInput = useCallback(
   (event: React.ChangeEvent<HTMLInputElement>) => {
     const raw = event.target.value;
     setTimeoutValue(raw);
-    const value = parseInt(raw, 10);
-    const seconds = Number.isFinite(value) && value >= 0 ? value : 0;
-    dispatch({
-      type: SETTINGS_SET_SCREEN_LOCK_TIMEOUT_CHANGED,
-      payload: seconds,
-    });
   },
-  [dispatch]
+  []
 );

The blur handler will still commit the value to Redux when editing is complete.

src/app/main/app.ts (1)

427-429: Type assertion for sender ID is acceptable but consider documenting.

The type assertion (event as any)?.sender?.id is necessary due to incomplete Electron typings, but it assumes sender.id exists and is stable across the session.

Consider adding a comment documenting the assumption:

-    const senderId = (event as any)?.sender?.id
-      ? String((event as any).sender.id)
-      : 'unknown';
+    // event.sender.id is a stable identifier for the webContents within this session
+    // but not included in Electron's type definitions
+    const senderId = (event as any)?.sender?.id
+      ? String((event as any).sender.id)
+      : 'unknown';

Alternatively, if targeting a specific Electron version, check if the types can be augmented:

declare module 'electron' {
  interface WebContents {
    id: number;
  }
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 253afec and da6963c.

📒 Files selected for processing (15)
  • rollup.config.mjs (1 hunks)
  • src/app/PersistableValues.ts (2 hunks)
  • src/app/actions.ts (2 hunks)
  • src/app/main/app.ts (8 hunks)
  • src/app/main/screenLock.ts (1 hunks)
  • src/app/reducers/isScreenLocked.ts (1 hunks)
  • src/app/selectors.ts (1 hunks)
  • src/i18n/no.i18n.json (2 hunks)
  • src/i18n/zh.i18n.json (1 hunks)
  • src/lockScreen/LockScreen.tsx (1 hunks)
  • src/main.ts (3 hunks)
  • src/preload.ts (3 hunks)
  • src/store/rootReducer.ts (3 hunks)
  • src/types/electron-api.d.ts (1 hunks)
  • src/ui/components/SettingsView/features/ScreenLock.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • rollup.config.mjs
  • src/app/selectors.ts
  • src/main.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Follow existing TypeScript patterns with strict mode enabled
All new code must pass ESLint and TypeScript checks
Avoid unnecessary comments; only add comments for complex logic or non-obvious decisions

Files:

  • src/app/actions.ts
  • src/app/reducers/isScreenLocked.ts
  • src/preload.ts
  • src/app/main/screenLock.ts
  • src/ui/components/SettingsView/features/ScreenLock.tsx
  • src/types/electron-api.d.ts
  • src/app/PersistableValues.ts
  • src/lockScreen/LockScreen.tsx
  • src/store/rootReducer.ts
  • src/app/main/app.ts
src/preload.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Keep src/preload.ts as the preload script bridging main and renderer

Files:

  • src/preload.ts
src/ui/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

src/ui/**/*.tsx: Implement React UI with functional components and hooks
Use Fuselage components (Box, Button, TextInput, Modal, etc.) and import from @rocket.chat/fuselage instead of raw HTML elements
For Fuselage theming, validate tokens against Theme.d.ts and only use documented values
Name React component files in PascalCase; non-component files should follow camelCase naming

Files:

  • src/ui/components/SettingsView/features/ScreenLock.tsx
src/store/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Write Redux actions following the Flux Standard Action (FSA) convention

Files:

  • src/store/rootReducer.ts
🧠 Learnings (1)
📚 Learning: 2025-09-26T19:24:21.364Z
Learnt from: CR
PR: RocketChat/Rocket.Chat.Electron#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-26T19:24:21.364Z
Learning: Applies to src/preload.ts : Keep `src/preload.ts` as the preload script bridging main and renderer

Applied to files:

  • src/preload.ts
🧬 Code graph analysis (4)
src/app/reducers/isScreenLocked.ts (1)
src/app/actions.ts (2)
  • APP_IS_SCREEN_LOCKED_SET (13-13)
  • APP_SETTINGS_LOADED (6-6)
src/app/main/screenLock.ts (5)
src/store/index.ts (1)
  • dispatch (38-40)
src/ui/actions.ts (4)
  • MENU_BAR_LOCK_SCREEN_CLICKED (154-154)
  • WEBVIEW_FOCUS_REQUESTED (69-69)
  • WEBVIEW_MESSAGE_BOX_FOCUSED (71-71)
  • WEBVIEW_USER_LOGGED_IN (85-85)
src/store/rootReducer.ts (1)
  • RootState (111-111)
src/ui/reducers/screenLockTimeoutSeconds.ts (1)
  • screenLockTimeoutSeconds (6-22)
src/ui/main/rootWindow.ts (1)
  • getRootWindow (47-52)
src/ui/components/SettingsView/features/ScreenLock.tsx (5)
src/store/index.ts (1)
  • dispatch (38-40)
src/store/actions.ts (1)
  • RootAction (44-46)
src/store/rootReducer.ts (1)
  • RootState (111-111)
src/ui/reducers/screenLockTimeoutSeconds.ts (1)
  • screenLockTimeoutSeconds (6-22)
src/ui/actions.ts (1)
  • SETTINGS_SET_SCREEN_LOCK_TIMEOUT_CHANGED (146-147)
src/app/main/app.ts (6)
src/store/index.ts (4)
  • select (63-64)
  • dispatch (38-40)
  • listen (88-114)
  • listen (144-157)
src/ui/main/rootWindow.ts (1)
  • getRootWindow (47-52)
src/app/actions.ts (1)
  • APP_IS_SCREEN_LOCKED_SET (13-13)
src/ui/actions.ts (3)
  • SETTINGS_SET_SCREEN_LOCK_PASSWORD_HASHED (150-151)
  • SETTINGS_SET_SCREEN_LOCK_PASSWORD_CHANGED (148-149)
  • MENU_BAR_LOCK_SCREEN_CLICKED (154-154)
src/app/PersistableValues.ts (1)
  • ScreenLockPasswordStored (95-100)
src/utils/browserLauncher.ts (1)
  • preloadBrowsersList (96-103)
🔇 Additional comments (5)
src/app/reducers/isScreenLocked.ts (1)

5-19: LGTM

Reducer shape and APP_SETTINGS_LOADED bootstrap look correct.

src/i18n/no.i18n.json (2)

281-292: LGTM

Keys and phrasing look good; description reflects generic salted hash.


451-459: LGTM

Lock screen strings are clear and consistent.

src/types/electron-api.d.ts (1)

3-15: LGTM

Type surface matches preload usage; optional methods support differing contexts.

src/app/main/screenLock.ts (1)

70-81: Clarify "inactivity timeout" vs. unfocused-only behavior

UI strings refer to an “inactivity timeout,” but src/app/main/screenLock.ts (70–81, 137–156) only starts the lock timer on blur and stops it on focus—there’s no idle tracking while focused. Either:

  • Implement true inactivity detection (start/reset the timer on user input/IPCs when focused), retaining blur as an additional trigger.
  • Or update all UI copy/locales to clearly state a “timeout when unfocused.”

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants