# How Fluxer Manages Window State Persistence Across Display Configuration Changes

> Discover how Fluxer ensures window state persistence across display changes. It saves bounds, validates coordinates, and repositions off-screen windows for seamless visibility.

- Repository: [Fluxer/fluxer](https://github.com/fluxerapp/fluxer)
- Tags: internals
- Published: 2026-03-17

---

**Fluxer persists window bounds to a JSON file in Electron's userData directory, validates saved coordinates against current displays on startup, and automatically repositions off-screen windows onto the primary display to ensure visibility.**

The Fluxer desktop application is built on Electron and faces the common challenge of maintaining window visibility when users disconnect monitors or change display arrangements. To solve this, the application implements a robust **window state persistence** system that saves geometry to disk while validating positions against the current screen configuration. This approach ensures that the main window never becomes stranded on a disconnected display, even after hardware changes.

## State Storage in Electron's User Data Directory

Fluxer stores window geometry in a file named [`window-state.json`](https://github.com/fluxerapp/fluxer/blob/main/window-state.json) located inside Electron's standard user data folder. The function `getWindowStateFile()` in [`fluxer_desktop/src/main/Window.tsx`](https://github.com/fluxerapp/fluxer/blob/main/fluxer_desktop/src/main/Window.tsx) constructs this path lazily the first time it is needed.

```typescript
// Lines 39-44 in src/main/Window.tsx
const getWindowStateFile = () => {
  const userData = app.getPath('userData');
  return path.join(userData, 'window-state.json');
};

```

This placement ensures the state file persists across application updates while remaining user-specific and writable.

## Loading and Validating Window Bounds Against Current Displays

When the application launches, `loadWindowBounds()` reads the JSON state and validates it against the current display configuration. The function uses `findVisibleDisplay()` to verify that the saved rectangle intersects at least one connected monitor.

The validation relies on two key utilities:

- **`boundsIntersect`** (lines 54-62): Calculates rectangle overlap between the saved window bounds and each available display.
- **`findVisibleDisplay`** (lines 66-75): Iterates over all displays and returns the first that overlaps the window rectangle by at least `VISIBILITY_MARGIN` pixels (default: 32).

If the saved coordinates do not intersect any display—such as when a monitor has been unplugged—the function returns `null`, triggering fallback logic that centers the window or places it on the primary screen.

## Persisting State with Debounced Events

To minimize disk I/O while capturing user adjustments, Fluxer wires debounced event listeners to the window's `resize` and `move` events. The `saveWindowBounds()` function (lines 19-33) writes the current bounds and maximized flag to [`window-state.json`](https://github.com/fluxerapp/fluxer/blob/main/window-state.json).

The event wiring in `createWindow()` (lines 31-35) ensures state is captured:
- During resize and move operations (debounced)
- Immediately upon maximize or unmaximize
- On application close

This strategy balances real-time accuracy with performance, preventing excessive writes during rapid window adjustments.

## Runtime Correction for Display Changes

Even after initial validation, display configurations can change while the application is running. The `ensureWindowOnScreen()` function (lines 78-94) guards against this by checking window visibility each time `showWindow()` is invoked.

If the window is found to be off-screen—detected by `findVisibleDisplay()` returning `null`—the function automatically repositions the window onto the primary display and clamps its size to fit within the primary's bounds. This check runs before the window is actually revealed, ensuring users never encounter a hidden or inaccessible window after display changes.

## Practical Implementation Examples

### Resetting Window State for Debugging

To force Fluxer to start with default window dimensions, delete the persisted state file before launching:

```typescript
import { app } from 'electron';
import path from 'node:path';
import fs from 'node:fs';

const userData = app.getPath('userData');
const stateFile = path.join(userData, 'window-state.json');

if (fs.existsSync(stateFile)) {
  fs.unlinkSync(stateFile);
  console.log('Fluxer window state cleared');
}

```

### Adjusting the Visibility Margin

To require a larger safety zone between the window edge and screen boundaries, modify the constant in [`src/main/Window.tsx`](https://github.com/fluxerapp/fluxer/blob/main/src/main/Window.tsx):

```typescript
// Default value
const VISIBILITY_MARGIN = 32;

// Increase for stricter positioning requirements
const VISIBILITY_MARGIN = 64;

```

The `findVisibleDisplay()` logic automatically respects this new margin when validating window positions.

### Handling Display Hot-Plug Events

To programmatically reposition the window when the system detects a display change:

```typescript
import { screen } from 'electron';
import { getMainWindow, ensureWindowOnScreen } from './Window';

screen.on('display-metrics-changed', () => {
  const win = getMainWindow();
  if (win) {
    ensureWindowOnScreen(win);
  }
});

```

This ensures the window remains accessible immediately after monitor additions or removals.

## Summary

- **Fluxer** stores window state in [`window-state.json`](https://github.com/fluxerapp/fluxer/blob/main/window-state.json) inside Electron's `userData` directory, constructed via `getWindowStateFile()`.
- **Validation** occurs at startup through `loadWindowBounds()`, which uses `boundsIntersect()` and `findVisibleDisplay()` to verify the saved rectangle is visible on a connected monitor.
- **Persistence** uses debounced listeners on `resize` and `move` events, plus explicit saves on maximize and close, implemented in `saveWindowBounds()`.
- **Runtime safety** is enforced by `ensureWindowOnScreen()`, which relocates off-screen windows to the primary display before showing them via `showWindow()`.
- **Defaults** from [`fluxer_desktop/src/common/Constants.tsx`](https://github.com/fluxerapp/fluxer/blob/main/fluxer_desktop/src/common/Constants.tsx) (such as `DEFAULT_WINDOW_WIDTH` and `DEFAULT_WINDOW_HEIGHT`) are applied when no valid saved state exists.

## Frequently Asked Questions

### Where does Fluxer store the window state file?

Fluxer stores window state in a JSON file named [`window-state.json`](https://github.com/fluxerapp/fluxer/blob/main/window-state.json) located in Electron's user data directory, which varies by operating system (typically `%APPDATA%/Fluxer` on Windows, `~/Library/Application Support/Fluxer` on macOS, or `~/.config/Fluxer` on Linux). The `getWindowStateFile()` function in [`src/main/Window.tsx`](https://github.com/fluxerapp/fluxer/blob/main/src/main/Window.tsx) constructs this path using `app.getPath('userData')`.

### What happens if the saved window position is off-screen?

If `loadWindowBounds()` determines that the saved coordinates do not intersect any currently connected display—using the `findVisibleDisplay()` function with a 32-pixel margin—the application discards the saved state. The window is then created using default dimensions or centered on the primary display. Additionally, `ensureWindowOnScreen()` runs before every `showWindow()` call to catch off-screen positions caused by mid-session display changes.

### How often does Fluxer save window state changes?

Fluxer saves window state using debounced listeners attached to the `resize` and `move` events, meaning it writes to disk only after the user stops adjusting the window for a brief period. The state is also saved immediately upon maximize, unmaximize, and application close events to ensure the final configuration is always persisted.

### How can I reset the window state to default values?

Delete the [`window-state.json`](https://github.com/fluxerapp/fluxer/blob/main/window-state.json) file from Electron's user data directory while the application is closed. The next time Fluxer starts, it will fail to find saved bounds and automatically apply default dimensions defined in [`src/common/Constants.tsx`](https://github.com/fluxerapp/fluxer/blob/main/src/common/Constants.tsx), centering the window on the primary display.