feat: add "start in fullscreen" window preference

Adds an opt-in preference that launches the main window in fullscreen,
driven by feedBack core's Settings → System "Fullscreen" toggle. Wires
the window.feedBackDesktop.window bridge (getStartFullscreen /
setStartFullscreen) that core's setupWindowOptions() gates on, persists
the flag in the desktop config (DesktopConfig.startFullscreen, alongside
windowBounds), and passes `fullscreen: true` at BrowserWindow creation
when set.

Persistence lives here (not renderer localStorage) because the main
process must read the pref at window-creation time. setStartFullscreen
live-applies via setFullScreen so the toggle is responsive on
Windows/Linux; on macOS the first programmatic fullscreen-enter on a
window created windowed is dropped by AppKit, so there it takes effect on
next launch — the core Settings copy notes this. This intentionally
narrows the earlier "never launch fullscreen" default to an opt-in.

Signed-off-by: gionnibgud <gionnibgud@gmail.com>
This commit is contained in:
gionnibgud 2026-07-13 13:44:33 +02:00
parent 6884800fd8
commit 4f1b64ff73
4 changed files with 49 additions and 3 deletions

View File

@ -33,6 +33,12 @@ export const IPC_MAINTENANCE_RESTART = 'maintenance:restart' as const;
// powerSaveBlocker here instead. See got-feedback/feedback#686.
export const IPC_POWER_SET_SCREEN_AWAKE = 'power:setScreenAwake' as const;
// Main-window fullscreen-at-launch preference. Renderer (Settings → System)
// reads/writes it; main persists it in the desktop config and applies it on
// window creation.
export const IPC_WINDOW_GET_START_FULLSCREEN = 'window:getStartFullscreen' as const;
export const IPC_WINDOW_SET_START_FULLSCREEN = 'window:setStartFullscreen' as const;
// Detachable panes (feedBack core's window.feedBack.panes).
//
// Deliberately tiny. The renderer OPENS its own pane windows with window.open() —

View File

@ -117,6 +117,8 @@ import {
IPC_UPDATE_CHECK_NOW,
IPC_UPDATE_APPLY,
IPC_POWER_SET_SCREEN_AWAKE,
IPC_WINDOW_GET_START_FULLSCREEN,
IPC_WINDOW_SET_START_FULLSCREEN,
} from './ipc-channels';
import { initAudioBridge, shutdownAudio } from './audio-bridge';
import { initDebugLogging, isDebugEnabled } from './debug-log';
@ -469,6 +471,7 @@ function createWindow(port: number): void {
getDesktopConfig().windowBounds,
screen.getAllDisplays().map((d) => d.workArea),
);
const startFullscreen = getDesktopConfig().startFullscreen === true;
mainWindow = new BrowserWindow({
x: restored.x,
y: restored.y,
@ -476,6 +479,7 @@ function createWindow(port: number): void {
height: restored.height,
minWidth: MIN_WIDTH,
minHeight: MIN_HEIGHT,
fullscreen: startFullscreen,
title: 'fee[dB]ack',
backgroundColor: '#0f172a', // slate-900 to match Slopsmith UI
webPreferences: rendererWebPreferences,
@ -484,9 +488,11 @@ function createWindow(port: number): void {
// Persist geometry on close. getNormalBounds() so a maximized session
// saves the underlying windowed size, restored + re-maximized next launch.
// ponytail: fullscreen is deliberately not persisted (launching straight
// into fullscreen is jarring, especially on macOS) and we save on close
// only — a crash loses the last session's geometry; add debounced
// Fullscreen is NOT auto-persisted from the live window state (launching
// straight into fullscreen is jarring, especially on macOS); it launches
// fullscreen ONLY when the user opts in via Settings → System → "Start in
// fullscreen" (config.startFullscreen, applied above). We save geometry on
// close only — a crash loses the last session's geometry; add debounced
// resize/move saving if that ever matters.
mainWindow.on('close', () => {
if (!mainWindow || mainWindow.isDestroyed()) return;
@ -1272,6 +1278,23 @@ async function startup(): Promise<void> {
return { success: true, enabled: on, urls: getLanUrls() };
});
ipcMain.handle(IPC_WINDOW_GET_START_FULLSCREEN, () => getDesktopConfig().startFullscreen === true);
ipcMain.handle(IPC_WINDOW_SET_START_FULLSCREEN, (_event, on: unknown) => {
const value = on === true;
setDesktopConfig({ startFullscreen: value });
// Live-apply so the toggle is responsive, not silent-until-relaunch.
// Works on the first toggle on Windows/Linux. On macOS the FIRST enter on
// a window created windowed is dropped by AppKit (its native-fullscreen
// state machine isn't engaged until the window has been fullscreen once —
// creating with `fullscreen: true` engages it), so there it takes effect
// on next launch instead; the core Settings note tells macOS users that.
// Reliable live both ways once the window has entered fullscreen once.
if (mainWindow && !mainWindow.isDestroyed() && mainWindow.isFullScreen() !== value) {
mainWindow.setFullScreen(value);
}
return value;
});
// Auto-update (Velopack). The renderer Settings panel reads the persisted
// channel from localStorage and calls setChannel() on boot — we default
// to 'stable' here so the first check runs against the safest feed even

View File

@ -30,6 +30,8 @@ import {
IPC_UPDATE_EVENT_AVAILABLE,
IPC_UPDATE_EVENT_DOWNLOADED,
IPC_POWER_SET_SCREEN_AWAKE,
IPC_WINDOW_GET_START_FULLSCREEN,
IPC_WINDOW_SET_START_FULLSCREEN,
IPC_MAINTENANCE_GET_PATHS,
IPC_MAINTENANCE_RESET,
IPC_MAINTENANCE_RESTART,
@ -580,6 +582,14 @@ const feedBackDesktopApi = {
power: {
setScreenAwake: (keep: boolean) => ipcRenderer.invoke(IPC_POWER_SET_SCREEN_AWAKE, keep),
},
// Main-window preferences the renderer can't own because the main process
// must read them at window-creation time. Today: start-in-fullscreen, wired
// by feedBack core's Settings → System "Start in fullscreen" toggle
// (setupWindowOptions()). Both methods return the resulting boolean.
window: {
getStartFullscreen: (): Promise<boolean> => ipcRenderer.invoke(IPC_WINDOW_GET_START_FULLSCREEN),
setStartFullscreen: (on: boolean): Promise<boolean> => ipcRenderer.invoke(IPC_WINDOW_SET_START_FULLSCREEN, on),
},
// Detachable panes. The renderer opens its own pane windows with window.open()
// — it moves a live DOM node into them and needs a handle on the new document
// to do it — and Electron turns that into a real BrowserWindow, which main

View File

@ -38,6 +38,13 @@ interface DesktopConfig {
// Last main-window geometry, restored (after sanitization against the
// current display layout) on next launch — see window-bounds.ts.
windowBounds?: SavedWindowBounds;
// When true, the main window launches in fullscreen and the System-settings
// toggle that drives it is shown. Opt-in (default off) — this deliberately
// reverses createWindow()'s historical "never launch fullscreen" default,
// but only for users who ask for it. Persisted here (not renderer
// localStorage) because the main process must read it at window creation,
// before the renderer exists.
startFullscreen?: boolean;
// Per-pane pop-out window geometry, keyed by pane id, plus its always-on-top
// flag. Sanitized against the display layout on restore, exactly like
// windowBounds — see pane-hosts.ts. Lives in the desktop config rather than