diff --git a/lang/dev/lobby.json b/lang/dev/lobby.json index dab4fc9f5..d6114175d 100644 --- a/lang/dev/lobby.json +++ b/lang/dev/lobby.json @@ -296,20 +296,22 @@ }, "settings": { "title": "seinttgs", - "fullscreen": "Fecrslluen", - "windowSize": "Wdoniw Size", + "sectionDisplay": "Daspliy", + "sectionSound": "Suond", + "sectionGeneral": "Geranel", + "resolution": "Rsueiloton", + "fullscreenOption": "Fecrslluen", + "maximizedOption": "Maimeixzd", "display": "Daspliy", "skipIntro": "Skip Inrto", "loginAutomatically": "Lgoin Aluaclatitomy", + "uiScale": "Iartfnece slace", "sfxVolume": "Sfx Vmolue", "musicVolume": "Msuic Vmolue", "devMode": "Dev Mode", "uploadLogs": "Uolpad logs", "logUrlCopied": "Log URL was cpeiod to clpbriaod.", "couldNotUploadLog": "Culod not upolad log.", - "labelSm": "Samll", - "labelMd": "Meuidm", - "labelLg": "Lgare", "labelDisplay": "Daspliy {id}", "language": "Lguganae", "storage": "Sraogte", diff --git a/lang/en/lobby.json b/lang/en/lobby.json index 2b7ffb9d4..202e1d35c 100644 --- a/lang/en/lobby.json +++ b/lang/en/lobby.json @@ -296,20 +296,22 @@ }, "settings": { "title": "settings", - "fullscreen": "Fullscreen", - "windowSize": "Window Size", + "sectionDisplay": "Display", + "sectionSound": "Sound", + "sectionGeneral": "General", + "resolution": "Resolution", + "fullscreenOption": "Fullscreen", + "maximizedOption": "Maximized", "display": "Display", "skipIntro": "Skip Intro", "loginAutomatically": "Login Automatically", + "uiScale": "Interface scale", "sfxVolume": "Sfx Volume", "musicVolume": "Music Volume", "devMode": "Dev Mode", "uploadLogs": "Upload logs", "logUrlCopied": "Log URL was copied to clipboard.", "couldNotUploadLog": "Could not upload log.", - "labelSm": "Small", - "labelMd": "Medium", - "labelLg": "Large", "labelDisplay": "Display {id}", "language": "Language", "storage": "Storage", diff --git a/src/main/config/window.ts b/src/main/config/window.ts new file mode 100644 index 000000000..8fccfee34 --- /dev/null +++ b/src/main/config/window.ts @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2026 The BAR Lobby Authors +// +// SPDX-License-Identifier: MIT + +// Device independent pixels throughout. Chromium and the OS translate to physical pixels, +// so a size given here is the same physical size on any display and needs no conversion. + +// Smallest and largest window the layout is expected to work in. +export const MIN_WINDOW_SIZE = { width: 1280, height: 720 }; + +// Windowed sizes are offered across this range of shapes; fullscreen uses the display's own. +export const SUPPORTED_ASPECT_RATIOS = [ + { label: "4:3", ratio: 4 / 3 }, + { label: "16:10", ratio: 16 / 10 }, + { label: "16:9", ratio: 16 / 9 }, + { label: "21:9", ratio: 21 / 9 }, +]; + +export const WINDOW_HEIGHT_STEPS = [720, 900, 1080, 1200, 1440]; + +// Absolute interface scale, in the same units the OS calls its scaling percentage. +export const UI_SCALE_MIN = 0.75; +export const UI_SCALE_MAX = 2.5; +export const UI_SCALE_STEP = 0.05; + +export const clampUiScale = (scale: number) => Math.min(UI_SCALE_MAX, Math.max(UI_SCALE_MIN, scale)); diff --git a/src/main/json/model/settings.ts b/src/main/json/model/settings.ts index e1ea67b4b..0f197435c 100644 --- a/src/main/json/model/settings.ts +++ b/src/main/json/model/settings.ts @@ -4,10 +4,15 @@ import { Type } from "@sinclair/typebox"; +import { UI_SCALE_MAX, UI_SCALE_MIN } from "@main/config/window"; + export const settingsSchema = Type.Object({ fullscreen: Type.Boolean({ default: true }), - size: Type.Number({ default: 900 }), + maximized: Type.Boolean({ default: false }), + windowWidth: Type.Number({ default: 1280 }), + windowHeight: Type.Number({ default: 720 }), displayIndex: Type.Number({ default: 0 }), + uiScale: Type.Union([Type.Number({ minimum: UI_SCALE_MIN, maximum: UI_SCALE_MAX }), Type.Null()], { default: null }), skipIntro: Type.Boolean({ default: false }), sfxVolume: Type.Number({ default: 5, minimum: 0, maximum: 100 }), musicVolume: Type.Number({ default: 5, minimum: 0, maximum: 100 }), diff --git a/src/main/main-window.ts b/src/main/main-window.ts index ab25d4387..3cc2964a6 100644 --- a/src/main/main-window.ts +++ b/src/main/main-window.ts @@ -2,29 +2,25 @@ // // SPDX-License-Identifier: MIT -import { app, BrowserWindow, nativeImage } from "electron"; +import { app, BrowserWindow, nativeImage, screen } from "electron"; import path from "path"; import { settingsService } from "./services/settings.service"; import { logger } from "./utils/logger"; import icon from "@main/resources/icon.png"; +import { MIN_WINDOW_SIZE, UI_SCALE_MAX, UI_SCALE_MIN, UI_SCALE_STEP } from "@main/config/window"; import { purgeLogFiles } from "@main/services/log.service"; import { typedWebContents, ipcMain } from "@main/typed-ipc"; import { gameAPI } from "@main/game/game"; import contentService from "@main/services/content.service"; -const ZOOM_FACTOR_BASELINE_HEIGHT = 1080; - const log = logger("main-window"); export function createWindow() { const settings = settingsService.getSettings(); log.info("Creating main window with settings: ", settings); - function getWindowSize(windowedHeight: number) { - return { - width: (windowedHeight * 16) / 9, - height: windowedHeight, - }; + function scaleRange() { + return { min: UI_SCALE_MIN, max: UI_SCALE_MAX, os: osScale() }; } const mainWindow = new BrowserWindow({ @@ -35,9 +31,10 @@ export function createWindow() { frame: false, show: false, autoHideMenuBar: true, - ...getWindowSize(settings.size), - minWidth: 640, - minHeight: 360, + width: settings.windowWidth, + height: settings.windowHeight, + minWidth: MIN_WINDOW_SIZE.width, + minHeight: MIN_WINDOW_SIZE.height, backgroundColor: "#000000", webPreferences: { preload: path.join(__dirname, "../build/preload.js"), @@ -51,26 +48,67 @@ export function createWindow() { // Disable zoom shortcuts webContents.on("before-input-event", (event, input) => { - // Block Ctrl/Cmd + '+', '-', '0' (zoom shortcuts) - if (((input.control || input.meta) && (input.key === "+" || input.key === "-" || input.key === "=")) || (input.key === "0" && (input.control || input.meta))) { - event.preventDefault(); - } + // Chromium's own zoom is bypassed so the scale stays a persisted setting. + if (input.type !== "keyDown" || !(input.control || input.meta)) return; + const delta = input.key === "+" || input.key === "=" ? UI_SCALE_STEP : input.key === "-" ? -UI_SCALE_STEP : input.key === "0" ? 0 : null; + if (delta === null) return; + + event.preventDefault(); + nudgeUiScale(delta); }); + // The setting is an absolute interface scale, matching what the OS calls its + // scaling percentage, so Chromium's own OS-derived scaling has to be divided out. + function osScale() { + return screen.getDisplayMatching(mainWindow.getBounds()).scaleFactor || 1; + } + + function applyScale(uiScale: number | null) { + const { min, max, os } = scaleRange(); + + webContents.setZoomFactor(Math.min(max, Math.max(min, uiScale ?? os)) / os); + } + function updateZoom() { - if (mainWindow.getContentSize()[1] > 0) { - const zoomFactor = mainWindow.getContentSize()[1] / ZOOM_FACTOR_BASELINE_HEIGHT; - webContents.setZoomFactor(zoomFactor); - } + applyScale(settingsService.getSettings().uiScale); } - // We handle direct window `resize` event, not only `mainWindow:resized` from renderer as - // that offers much lower latency and offers more fluid experience when resizing. We can't - // use only `resize` event as looks like under some platforms not all window shape changes - // trigger this event. - mainWindow.on("resize", () => { - updateZoom(); - }); + // Stored by the renderer, which writes the whole settings object and would otherwise + // overwrite a value set behind its back. + function nudgeUiScale(delta: number) { + const { min, max } = scaleRange(); + const current = settingsService.getSettings().uiScale ?? osScale(); + const next = delta === 0 ? null : Math.min(max, Math.max(min, Math.round((current + delta) * 100) / 100)); + + webContents.send("mainWindow:uiScaleNudged", next); + } + + // Resize fires continuously while dragging, hence the trailing timer. + let zoomUpdate: NodeJS.Timeout | undefined; + function scheduleZoomUpdate() { + clearTimeout(zoomUpdate); + zoomUpdate = setTimeout(() => { + updateZoom(); + webContents.send("mainWindow:scaleRangeChanged", scaleRange()); + reportWindowState(); + }, 100); + } + + // No size while fullscreen or maximised, since the remembered one is where to return to. + function reportWindowState() { + const maximized = mainWindow.isMaximized(); + const [width, height] = mainWindow.getSize(); + const settled = !maximized && !mainWindow.isFullScreen(); + + webContents.send("mainWindow:windowStateChanged", { maximized, size: settled ? { width, height } : null }); + } + + mainWindow.on("resize", scheduleZoomUpdate); + mainWindow.on("maximize", scheduleZoomUpdate); + mainWindow.on("unmaximize", scheduleZoomUpdate); + mainWindow.on("enter-full-screen", scheduleZoomUpdate); + mainWindow.on("leave-full-screen", scheduleZoomUpdate); + mainWindow.on("closed", () => clearTimeout(zoomUpdate)); process.env.MAIN_WINDOW_ID = mainWindow.id.toString(); @@ -85,6 +123,8 @@ export function createWindow() { // Note: `fullscreen: true` conflicts with `show: false`, so we apply fullscreen here. if (settings.fullscreen) { mainWindow.setFullScreen(true); + } else if (settings.maximized) { + mainWindow.maximize(); } updateZoom(); mainWindow.show(); @@ -111,30 +151,82 @@ export function createWindow() { app.on("browser-window-focus", () => mainWindow.flashFrame(false)); - //TODO add an IPC handler for changing display via the settings - // Register IPC handlers for the main window ipcMain.handle("mainWindow:setFullscreen", (_event, flag: boolean) => { mainWindow.setFullScreen(flag); - updateZoom(); }); - ipcMain.handle("mainWindow:setSize", (_event, size: number) => { - if (!mainWindow.isFullScreen() && !mainWindow.isMaximized()) { - const { width, height } = getWindowSize(size); - mainWindow.setSize(width, height); + ipcMain.handle("mainWindow:setMaximized", (_event, flag: boolean) => { + if (flag === mainWindow.isMaximized()) return; + if (!flag) { + mainWindow.unmaximize(); + return; } + + if (mainWindow.isFullScreen()) mainWindow.setFullScreen(false); + mainWindow.maximize(); + }); + ipcMain.handle("mainWindow:setSize", (_event, width: number, height: number) => { + // Their own settings are written alongside the size and applied first; leaving those + // modes here as well undid a revert back to fullscreen. + if (mainWindow.isFullScreen() || mainWindow.isMaximized()) return; + + const target = { width: Math.max(width, MIN_WINDOW_SIZE.width), height: Math.max(height, MIN_WINDOW_SIZE.height) }; + const [currentWidth, currentHeight] = mainWindow.getSize(); + // Arrives again for a size the window already has, and re-centring would yank a drag. + if (target.width === currentWidth && target.height === currentHeight) return; + + mainWindow.setSize(target.width, target.height); + mainWindow.center(); + updateZoom(); + }); + ipcMain.handle("mainWindow:setDisplay", (_event, index: number) => { + const display = screen.getAllDisplays()[index]; + if (!display) return; + + // Nothing but the display changes. The size comes from the setting rather than the + // window, so it survives the trip out of fullscreen or maximised, and it is not + // trimmed to the new display: a size that no longer fits is the window's business, + // not a reason to rewrite what the user chose. + const { windowWidth, windowHeight } = settingsService.getSettings(); + const width = Math.max(windowWidth, MIN_WINDOW_SIZE.width); + const height = Math.max(windowHeight, MIN_WINDOW_SIZE.height); + const { workArea } = display; + + // Fullscreen and maximised are tied to the display they were entered on. + const wasFullScreen = mainWindow.isFullScreen(); + const wasMaximized = mainWindow.isMaximized(); + if (wasFullScreen) mainWindow.setFullScreen(false); + if (wasMaximized) mainWindow.unmaximize(); + + mainWindow.setBounds({ + width, + height, + x: Math.round(workArea.x + (workArea.width - width) / 2), + y: Math.round(workArea.y + (workArea.height - height) / 2), + }); + + if (wasFullScreen) mainWindow.setFullScreen(true); + else if (wasMaximized) mainWindow.maximize(); + updateZoom(); }); ipcMain.handle("mainWindow:flashFrame", (_event, flag: boolean) => { mainWindow.flashFrame(flag); }); + ipcMain.handle("mainWindow:setUiScale", (_event, scale: number | null) => applyScale(scale)); + ipcMain.handle("mainWindow:getScaleRange", () => scaleRange()); + ipcMain.handle("mainWindow:getDisplays", () => + screen.getAllDisplays().map((display, index) => ({ + index, + scaleFactor: display.scaleFactor || 1, + workArea: { width: display.workAreaSize.width, height: display.workAreaSize.height }, + // workArea has the taskbar removed, so it is the wrong shape for an aspect ratio. + size: { width: display.size.width, height: display.size.height }, + })) + ); ipcMain.handle("mainWindow:minimize", () => mainWindow.minimize()); ipcMain.handle("mainWindow:isFullscreen", () => mainWindow.isFullScreen()); - ipcMain.handle("mainWindow:resized", () => { - updateZoom(); - }); - // Get download progress updates to update the dock/taskbar contentService.registerProgressHandler(mainWindow); diff --git a/src/main/typed-ipc.ts b/src/main/typed-ipc.ts index ac2e96815..32320ef37 100644 --- a/src/main/typed-ipc.ts +++ b/src/main/typed-ipc.ts @@ -5,7 +5,7 @@ import type { AuthState } from "@main/services/auth.service"; import type { StoredIdentity } from "@main/model/user"; import type { BattleWithMetadata } from "@main/game/battle/battle-types"; -import type { BattleStartRequestData, BattleEndedEventData } from "tachyon-protocol/types"; +import type { BattleStartRequestData } from "tachyon-protocol/types"; import type { ContentRef } from "@main/content/content-ref"; import type { ContentPresence, ContentState } from "@main/content/content-state"; import type { DownloadInfo } from "@main/content/downloads"; @@ -40,6 +40,9 @@ export type IPCEvents = { "game:launched": () => void; "maps:mapAdded": (filename: string) => void; "maps:mapDeleted": (filename: string) => void; + "mainWindow:scaleRangeChanged": (range: { min: number; max: number; os: number }) => void; + "mainWindow:windowStateChanged": (state: { maximized: boolean; size: { width: number; height: number } | null }) => void; + "mainWindow:uiScaleNudged": (scale: number | null) => void; "navigation:navigateTo": (target: string) => void; "notifications:showAlert": (alertConfig: { text: string; severity?: "info" | "warning" | "error"; timeoutMs?: number }) => void; "paths:copyProgress": (progress: { copied: number; total: number }) => void; @@ -48,7 +51,6 @@ export type IPCEvents = { "replays:replayDeleted": (filename: string) => void; "replays:highlightOpened": (fileNames: string[]) => void; "tachyon:battleStart": (data: BattleStartRequestData) => void; - "tachyon:battleEnded": (data: BattleEndedEventData) => void; "tachyon:connected": () => void; "tachyon:disconnected": () => void; "tachyon:event": (event: TachyonEvent) => void; @@ -90,8 +92,12 @@ export type IPCCommands = { "log:upload": () => string; "mainWindow:flashFrame": (flag: boolean) => void; "mainWindow:setFullscreen": (flag: boolean) => void; - "mainWindow:resized": () => void; - "mainWindow:setSize": (size: number) => void; + "mainWindow:setMaximized": (flag: boolean) => void; + "mainWindow:setSize": (width: number, height: number) => void; + "mainWindow:setUiScale": (scale: number | null) => void; + "mainWindow:getScaleRange": () => { min: number; max: number; os: number }; + "mainWindow:getDisplays": () => Array<{ index: number; scaleFactor: number; workArea: { width: number; height: number }; size: { width: number; height: number } }>; + "mainWindow:setDisplay": (index: number) => void; "mainWindow:minimize": () => void; "mainWindow:isFullscreen": () => boolean; "maps:downloadMap": (springName: string) => void; diff --git a/src/preload/preload.ts b/src/preload/preload.ts index dd74211ef..564b0f9f4 100644 --- a/src/preload/preload.ts +++ b/src/preload/preload.ts @@ -16,7 +16,7 @@ import { DownloadInfo } from "@main/content/downloads"; import { Info } from "@main/services/info.service"; import { BattleWithMetadata } from "@main/game/battle/battle-types"; import { GetCommandData, GetCommandIds, GetCommands } from "tachyon-protocol"; -import type { BattleStartRequestData, BattleEndedEventData } from "tachyon-protocol/types"; +import type { BattleStartRequestData } from "tachyon-protocol/types"; import { MultiplayerLaunchSettings } from "@main/game/game"; import { logLevels } from "@main/services/log.service"; import { Config } from "@main/services/config.service"; @@ -40,11 +40,20 @@ contextBridge.exposeInMainWorld("info", infoApi); const mainWindowApi = { setFullscreen: (flag: boolean): Promise => ipcRenderer.invoke("mainWindow:setFullscreen", flag), - setSize: (size: number): Promise => ipcRenderer.invoke("mainWindow:setSize", size), + setMaximized: (flag: boolean): Promise => ipcRenderer.invoke("mainWindow:setMaximized", flag), + setSize: (width: number, height: number): Promise => ipcRenderer.invoke("mainWindow:setSize", width, height), + setUiScale: (scale: number | null): Promise => ipcRenderer.invoke("mainWindow:setUiScale", scale), + getScaleRange: (): Promise<{ min: number; max: number; os: number }> => ipcRenderer.invoke("mainWindow:getScaleRange"), + onScaleRangeChanged: (callback: (range: { min: number; max: number; os: number }) => void) => ipcRenderer.on("mainWindow:scaleRangeChanged", (_event, range) => callback(range)), + getDisplays: (): Promise> => + ipcRenderer.invoke("mainWindow:getDisplays"), + setDisplay: (index: number): Promise => ipcRenderer.invoke("mainWindow:setDisplay", index), + onWindowStateChanged: (callback: (state: { maximized: boolean; size: { width: number; height: number } | null }) => void) => + ipcRenderer.on("mainWindow:windowStateChanged", (_event, state) => callback(state)), + onUiScaleNudged: (callback: (scale: number | null) => void) => ipcRenderer.on("mainWindow:uiScaleNudged", (_event, scale) => callback(scale)), flashFrame: (flag: boolean): Promise => ipcRenderer.invoke("mainWindow:flashFrame", flag), minimize: (): Promise => ipcRenderer.invoke("mainWindow:minimize"), isFullscreen: (): Promise => ipcRenderer.invoke("mainWindow:isFullscreen"), - resized: (): Promise => ipcRenderer.invoke("mainWindow:resized"), }; export type MainWindowApi = typeof mainWindowApi; contextBridge.exposeInMainWorld("mainWindow", mainWindowApi); @@ -249,7 +258,6 @@ const tachyonApi = { onDisconnected: (callback: () => void) => ipcRenderer.on("tachyon:disconnected", callback), onEvent, onBattleStart: (callback: (data: BattleStartRequestData) => void) => ipcRenderer.on("tachyon:battleStart", (_event, data) => callback(data)), - onBattleEnded: (callback: (data: BattleEndedEventData) => void) => ipcRenderer.on("tachyon:battleEnded", (_event, data) => callback(data)), }; export type TachyonApi = typeof tachyonApi; contextBridge.exposeInMainWorld("tachyon", tachyonApi); diff --git a/src/renderer/assets/languages/cs.json b/src/renderer/assets/languages/cs.json index dec9d0cef..5cd84524f 100644 --- a/src/renderer/assets/languages/cs.json +++ b/src/renderer/assets/languages/cs.json @@ -2279,20 +2279,22 @@ }, "settings": { "title": null, - "fullscreen": null, - "windowSize": null, + "sectionDisplay": null, + "sectionSound": null, + "sectionGeneral": null, + "resolution": null, + "fullscreenOption": null, + "maximizedOption": null, "display": null, "skipIntro": null, "loginAutomatically": null, + "uiScale": null, "sfxVolume": null, "musicVolume": null, "devMode": null, "uploadLogs": null, "logUrlCopied": null, "couldNotUploadLog": null, - "labelSm": null, - "labelMd": null, - "labelLg": null, "labelDisplay": null, "language": null, "storage": null, diff --git a/src/renderer/assets/languages/de.json b/src/renderer/assets/languages/de.json index fb56c39db..154b115bd 100644 --- a/src/renderer/assets/languages/de.json +++ b/src/renderer/assets/languages/de.json @@ -2150,20 +2150,22 @@ }, "settings": { "title": null, - "fullscreen": null, - "windowSize": null, + "sectionDisplay": null, + "sectionSound": null, + "sectionGeneral": null, + "resolution": null, + "fullscreenOption": null, + "maximizedOption": null, "display": null, "skipIntro": null, "loginAutomatically": null, + "uiScale": null, "sfxVolume": null, "musicVolume": null, "devMode": null, "uploadLogs": null, "logUrlCopied": null, "couldNotUploadLog": null, - "labelSm": null, - "labelMd": null, - "labelLg": null, "labelDisplay": null, "language": null, "storage": null, diff --git a/src/renderer/assets/languages/dev.json b/src/renderer/assets/languages/dev.json index 8e05cba77..e4102426a 100644 --- a/src/renderer/assets/languages/dev.json +++ b/src/renderer/assets/languages/dev.json @@ -296,20 +296,22 @@ }, "settings": { "title": "seinttgs", - "fullscreen": "Fecrslluen", - "windowSize": "Wdoniw Size", + "sectionDisplay": "Daspliy", + "sectionSound": "Suond", + "sectionGeneral": "Geranel", + "resolution": "Rsueiloton", + "fullscreenOption": "Fecrslluen", + "maximizedOption": "Maimeixzd", "display": "Daspliy", "skipIntro": "Skip Inrto", "loginAutomatically": "Lgoin Aluaclatitomy", + "uiScale": "Iartfnece slace", "sfxVolume": "Sfx Vmolue", "musicVolume": "Msuic Vmolue", "devMode": "Dev Mode", "uploadLogs": "Uolpad logs", "logUrlCopied": "Log URL was cpeiod to clpbriaod.", "couldNotUploadLog": "Culod not upolad log.", - "labelSm": "Samll", - "labelMd": "Meuidm", - "labelLg": "Lgare", "labelDisplay": "Daspliy {id}", "language": "Lguganae", "storage": "Sraogte", diff --git a/src/renderer/assets/languages/en.json b/src/renderer/assets/languages/en.json index e4201df59..91ac4832d 100644 --- a/src/renderer/assets/languages/en.json +++ b/src/renderer/assets/languages/en.json @@ -2127,20 +2127,22 @@ }, "settings": { "title": "settings", - "fullscreen": "Fullscreen", - "windowSize": "Window Size", + "sectionDisplay": "Display", + "sectionSound": "Sound", + "sectionGeneral": "General", + "resolution": "Resolution", + "fullscreenOption": "Fullscreen", + "maximizedOption": "Maximized", "display": "Display", "skipIntro": "Skip Intro", "loginAutomatically": "Login Automatically", + "uiScale": "Interface scale", "sfxVolume": "Sfx Volume", "musicVolume": "Music Volume", "devMode": "Dev Mode", "uploadLogs": "Upload logs", "logUrlCopied": "Log URL was copied to clipboard.", "couldNotUploadLog": "Could not upload log.", - "labelSm": "Small", - "labelMd": "Medium", - "labelLg": "Large", "labelDisplay": "Display {id}", "language": "Language", "storage": "Storage", diff --git a/src/renderer/assets/languages/fr.json b/src/renderer/assets/languages/fr.json index 633305c71..518283d3b 100644 --- a/src/renderer/assets/languages/fr.json +++ b/src/renderer/assets/languages/fr.json @@ -4132,20 +4132,22 @@ }, "settings": { "title": null, - "fullscreen": null, - "windowSize": null, + "sectionDisplay": null, + "sectionSound": null, + "sectionGeneral": null, + "resolution": null, + "fullscreenOption": null, + "maximizedOption": null, "display": null, "skipIntro": null, "loginAutomatically": null, + "uiScale": null, "sfxVolume": null, "musicVolume": null, "devMode": null, "uploadLogs": null, "logUrlCopied": null, "couldNotUploadLog": null, - "labelSm": null, - "labelMd": null, - "labelLg": null, "labelDisplay": null, "language": null, "storage": null, diff --git a/src/renderer/assets/languages/ru.json b/src/renderer/assets/languages/ru.json index d0ea54b26..5f1699f96 100644 --- a/src/renderer/assets/languages/ru.json +++ b/src/renderer/assets/languages/ru.json @@ -4101,20 +4101,22 @@ }, "settings": { "title": null, - "fullscreen": null, - "windowSize": null, + "sectionDisplay": null, + "sectionSound": null, + "sectionGeneral": null, + "resolution": null, + "fullscreenOption": null, + "maximizedOption": null, "display": null, "skipIntro": null, "loginAutomatically": null, + "uiScale": null, "sfxVolume": null, "musicVolume": null, "devMode": null, "uploadLogs": null, "logUrlCopied": null, "couldNotUploadLog": null, - "labelSm": null, - "labelMd": null, - "labelLg": null, "labelDisplay": null, "language": null, "storage": null, diff --git a/src/renderer/assets/languages/zh.json b/src/renderer/assets/languages/zh.json index 717eb0e5c..9e400f7ad 100644 --- a/src/renderer/assets/languages/zh.json +++ b/src/renderer/assets/languages/zh.json @@ -4243,20 +4243,22 @@ }, "settings": { "title": null, - "fullscreen": null, - "windowSize": null, + "sectionDisplay": null, + "sectionSound": null, + "sectionGeneral": null, + "resolution": null, + "fullscreenOption": null, + "maximizedOption": null, "display": null, "skipIntro": null, "loginAutomatically": null, + "uiScale": null, "sfxVolume": null, "musicVolume": null, "devMode": null, "uploadLogs": null, "logUrlCopied": null, "couldNotUploadLog": null, - "labelSm": null, - "labelMd": null, - "labelLg": null, "labelDisplay": null, "language": null, "storage": null, diff --git a/src/renderer/components/common/Modal.vue b/src/renderer/components/common/Modal.vue index bb6c5775d..f56fff7f5 100644 --- a/src/renderer/components/common/Modal.vue +++ b/src/renderer/components/common/Modal.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: MIT