How Fluxer Manages Window State Persistence Across Display Configuration Changes
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 located inside Electron's standard user data folder. The function getWindowStateFile() in fluxer_desktop/src/main/Window.tsx constructs this path lazily the first time it is needed.
// 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 leastVISIBILITY_MARGINpixels (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.
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:
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:
// 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:
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.jsoninside Electron'suserDatadirectory, constructed viagetWindowStateFile(). - Validation occurs at startup through
loadWindowBounds(), which usesboundsIntersect()andfindVisibleDisplay()to verify the saved rectangle is visible on a connected monitor. - Persistence uses debounced listeners on
resizeandmoveevents, plus explicit saves on maximize and close, implemented insaveWindowBounds(). - Runtime safety is enforced by
ensureWindowOnScreen(), which relocates off-screen windows to the primary display before showing them viashowWindow(). - Defaults from
fluxer_desktop/src/common/Constants.tsx(such asDEFAULT_WINDOW_WIDTHandDEFAULT_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 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 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 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, centering the window on the primary display.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →