Lepton Immersive Mode Implementation: A Technical Deep Dive

Lepton's immersive mode works by sending an IPC message from the Electron main process to the renderer, which toggles a Redux state flag that triggers CSS class changes and conditional rendering to expand the snippet view to full screen.

Lepton, an open-source snippet manager built by hackjutsu, features an immersive mode that maximizes the code viewing area by hiding chrome elements. This article examines the technical implementation of Lepton's immersive mode, detailing how Electron IPC, Redux state management, and React components coordinate to create a distraction-free coding environment.

Triggering Immersive Mode via IPC

The immersive mode can be activated through three entry points in the Electron main process: the application menu, the Touch Bar, or a keyboard shortcut. Each trigger sends the same IPC channel message 'immersive-mode' to the renderer process.

In main.js, the menu item and Touch Bar button both invoke mainWindow.send('immersive-mode') when clicked:

// main.js – menu definition (lines 71-73)
{
  label: 'Immersive Mode',
  accelerator: shortcuts.keyImmersiveMode,
  click: (item, mainWindow) => mainWindow && mainWindow.send('immersive-mode')
}

// main.js – Touch Bar button (lines 15-20)
new TouchBarButton({
  label: "Immersive",
  icon: makeIcon("immersive"),
  click: () => mainWindow.send("immersive-mode")
})

Keyboard Shortcuts Configuration

The default shortcut ⌘/Ctrl + I is defined in configs/defaultConfig.js at line 44:

"keyImmersiveMode": "CommandOrControl+I"

Redux State Management

Once the renderer receives the IPC message, it validates the UI state and updates the Redux store to reflect the new immersive mode status.

IPC Listener and Action Dispatching

In app/index.js (lines 44-69), the ipcRenderer listens for the 'immersive-mode' event. It checks that no modal dialogs are open using allDialogsClosed(), then toggles the current state and dispatches the updateImmersiveModeStatus action:

ipcRenderer.on('immersive-mode', data => {
  const { immersiveMode } = reduxStore.getState()
  const dialogs = [/* …list of open modal flags… */]
  if (allDialogsClosed(dialogs)) {
    const newStatus = immersiveMode === 'ON' ? 'OFF' : 'ON'
    reduxStore.dispatch(updateImmersiveModeStatus(newStatus))
  }
})

The action creator is defined in app/actions/index.js (lines 90-95):

export function updateImmersiveModeStatus (status) {
  return { type: UPDATE_IMMERSIVE_MODE_STATUS, payload: status }
}

Reducer Implementation

The app/reducers/reducer_immersive_mode.js file manages the state slice, defaulting to 'OFF' and updating to the action payload when UPDATE_IMMERSIVE_MODE_STATUS is received:

export default function (state = 'OFF', action) {
  switch (action.type) {
    case UPDATE_IMMERSIVE_MODE_STATUS:
      return action.payload
    default:
      return state
  }
}

This reducer is combined into the root reducer in app/reducers/index.js (line 17) under the key immersiveMode:

import immersiveMode from './reducer_immersive_mode'
export default combineReducers({
  // …other slices…
  immersiveMode,
})

UI Component Adaptations

React components subscribe to the immersiveMode state and adjust their rendering and styling accordingly.

Conditional Rendering Logic

In app/containers/appContainer/index.js (lines 37-40), the container selects between immersive and normal rendering paths:

{ immersiveMode === 'ON'
  ? this.renderActiveImmersiveSection()
  : this.renderActiveNormalSection() }

Similarly, app/containers/snippetPanel/index.js (lines 27-30) switches CSS classes based on the mode:

<div className={ immersiveMode === 'ON' ? 'snippet-panel-immersive' : 'snippet-panel' }>

</div>

CSS Styling for Full-Screen View

The immersive styling is defined in app/containers/snippetPanel/index.scss (lines 12-21). The .snippet-panel-immersive class extends the base panel and expands to full viewport width while applying minimal font styling:

.snippet-panel-immersive {
  @extend .snippet-panel;
  width: 100%;

  .font-style-base { … }
}

These CSS rules hide sidebars, remove scrollbars, and create the distraction-free environment characteristic of immersive mode.

Exiting Immersive Mode

Users can exit immersive mode by pressing Esc, which is registered as 'back-to-normal-mode' in main.js, or by toggling the menu item again. Both methods send the same 'immersive-mode' IPC channel, triggering the toggle logic in app/index.js to set the Redux state back to 'OFF'.

Code Examples

Manually Toggling Immersive Mode from a Custom Button

To programmatically toggle immersive mode from a React component:

import { updateImmersiveModeStatus } from '../actions'
import { useDispatch, useSelector } from 'react-redux'

function ImmersiveToggle() {
  const dispatch = useDispatch()
  const mode = useSelector(state => state.immersiveMode)

  const toggle = () => {
    const newMode = mode === 'ON' ? 'OFF' : 'ON'
    dispatch(updateImmersiveModeStatus(newMode))
  }

  return <button onClick={toggle}>
    {mode === 'ON' ? 'Leave Immersive' : 'Enter Immersive'}
  </button>
}

Listening for Mode Changes in Components

Subscribe to state changes to conditionally render UI elements:

import { useSelector } from 'react-redux'

function Header() {
  const immersive = useSelector(state => state.immersiveMode)
  return (
    <header className={immersive === 'ON' ? 'hidden' : ''}>
      {/* normal header content */}
    </header>
  )
}

Customizing the Keyboard Shortcut

Modify configs/defaultConfig.js to change the shortcut (e.g., to Ctrl+Shift+I):

"keyImmersiveMode": "CommandOrControl+Shift+I"

No additional code changes are required; the main process reads this configuration at startup.

Summary

  • IPC Communication: Immersive mode is triggered via the 'immersive-mode' channel sent from main.js (menu, Touch Bar, or shortcut) to the renderer process.
  • Redux State: The immersiveMode state slice defaults to 'OFF' in app/reducers/reducer_immersive_mode.js and toggles via the updateImmersiveModeStatus action.
  • UI Adaptation: Components in app/containers/appContainer/index.js and app/containers/snippetPanel/index.js conditionally render immersive layouts using the .snippet-panel-immersive CSS class defined in index.scss.
  • Exit Mechanism: Pressing Esc or re-toggling the menu sends the same IPC message to set the state back to 'OFF'.

Frequently Asked Questions

How does Lepton's immersive mode handle keyboard shortcuts?

Lepton registers the immersive mode shortcut in configs/defaultConfig.js using Electron's accelerator format (CommandOrControl+I). The main process reads this configuration at startup and binds it to the menu item in main.js. When pressed, the shortcut triggers the same 'immersive-mode' IPC message as the menu or Touch Bar buttons.

What prevents immersive mode from activating when dialogs are open?

In app/index.js, the IPC listener checks the allDialogsClosed() function before dispatching the Redux action. This validation ensures that modal dialogs (such as settings or confirmation dialogs) block the immersive mode toggle, preventing UI conflicts and maintaining focus on the active modal.

Can I customize the immersive mode styling?

Yes, the immersive appearance is controlled by SCSS in app/containers/snippetPanel/index.scss. The .snippet-panel-immersive class extends the base panel styles and sets width: 100% while hiding sidebars. You can modify these styles or the conditional class logic in app/containers/snippetPanel/index.js to adjust the full-screen layout.

How does the application exit immersive mode?

Exiting immersive mode uses the same mechanism as entering it. Pressing Esc (registered as 'back-to-normal-mode' in main.js) or selecting the immersive mode menu item again sends the 'immersive-mode' IPC channel. The toggle logic in app/index.js then flips the Redux state from 'ON' back to 'OFF', causing components to revert to their standard layouts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →