Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 22 additions & 14 deletions electron/hudOverlayBounds.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,26 @@ describe("getHudOverlayWindowBounds", () => {
it("uses a bottom-centered compact fallback when mouse passthrough is unavailable", () => {
expect(getHudOverlayWindowBounds(workArea, false)).toEqual({
x: 650,
y: 920,
y: 520,
width: 860,
height: 160,
height: 560,
});
});

it("reserves enough compact height for a full-height HUD menu", () => {
// The menu card is capped at 400px and sits above roughly 96px of bar and
// padding plus 16px of offsets. If the compact window is shorter than that
// the menu is clipped by the window bounds, which is the bug this guards.
const compact = getHudOverlayWindowBounds(workArea, false);
expect(compact.height).toBeGreaterThanOrEqual(400 + 16 + 96);
});

it("expands the non-passthrough fallback for HUD menus and hover interaction", () => {
expect(getHudOverlayWindowBounds(workArea, false, true)).toEqual({
x: 650,
y: 540,
y: 400,
width: 860,
height: 540,
height: 680,
});
});

Expand All @@ -49,9 +57,9 @@ describe("getHudOverlayWindowBounds", () => {
),
).toEqual({
x: -100,
y: 280,
y: 20,
width: 640,
height: 160,
height: 420,
});
});

Expand Down Expand Up @@ -98,9 +106,9 @@ describe("resizeHudOverlayFallbackBounds", () => {
),
).toEqual({
x: 420,
y: 320,
y: 180,
width: 860,
height: 540,
height: 680,
});
});

Expand All @@ -110,17 +118,17 @@ describe("resizeHudOverlayFallbackBounds", () => {
workArea,
{
x: 420,
y: 320,
y: 180,
width: 860,
height: 540,
height: 680,
},
false,
),
).toEqual({
x: 420,
y: 700,
y: 300,
width: 860,
height: 160,
height: 560,
});
});

Expand All @@ -138,9 +146,9 @@ describe("resizeHudOverlayFallbackBounds", () => {
),
).toEqual({
x: 1060,
y: 520,
y: 380,
width: 860,
height: 540,
height: 680,
});
});
});
Expand Down
14 changes: 12 additions & 2 deletions electron/hudOverlayBounds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,18 @@ export interface HudOverlayWorkArea {
}

const NON_PASSTHROUGH_HUD_WIDTH_DIP = 860;
const NON_PASSTHROUGH_HUD_COMPACT_HEIGHT_DIP = 160;
const NON_PASSTHROUGH_HUD_EXPANDED_HEIGHT_DIP = 540;
// Without mouse passthrough the HUD is an ordinary window, so anything the
// renderer draws outside it is clipped: a menu taller than the window loses its
// top rows. Resizing on demand is not an option either, because Wayland
// compositors ignore client-side moves, so a window that grows keeps its
// top-left pinned and drags the bar down with it.
//
// So reserve the menu headroom once, at creation. The bar renders at the bottom
// edge of the window, which leaves the space above it transparent and free for
// menus to open into without the window ever changing size.
// 400px menu card + 16px offsets + ~96px of bar and padding, rounded up.
const NON_PASSTHROUGH_HUD_COMPACT_HEIGHT_DIP = 560;
const NON_PASSTHROUGH_HUD_EXPANDED_HEIGHT_DIP = 680;

function clamp(value: number, min: number, max: number): number {
return Math.min(Math.max(value, min), max);
Expand Down
41 changes: 5 additions & 36 deletions electron/windows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@ import { fileURLToPath } from "node:url";
import { app, BrowserWindow, ipcMain } from "electron";
import { supportsHudCaptureProtection } from "../src/lib/hudCaptureProtection";
import { USER_DATA_PATH } from "./appPaths";
import {
getHudOverlayWindowBounds,
resizeHudOverlayFallbackBounds,
shouldExpandHudOverlayFallback,
} from "./hudOverlayBounds";
import { getHudOverlayWindowBounds, shouldExpandHudOverlayFallback } from "./hudOverlayBounds";
import { getHudOverlayTaskbarOptions } from "./hudOverlayWindowOptions";
import { getPackagedRendererBaseUrl } from "./rendererServer";

Expand Down Expand Up @@ -265,34 +261,6 @@ function positionUpdateToastWindow() {
updateToastWindow.moveTop();
}

function setHudOverlayFallbackExpanded(expanded: boolean) {
if (hudOverlayRecordingActive) {
hudOverlayFallbackExpanded = false;
return;
}

hudOverlayFallbackExpanded = expanded;
if (
!hudOverlayWindow ||
hudOverlayWindow.isDestroyed() ||
isHudOverlayMousePassthroughSupported()
) {
return;
}

const { workArea } = getHudOverlayDisplay();
const nextBounds = resizeHudOverlayFallbackBounds(
workArea,
hudOverlayWindow.getBounds(),
expanded,
);
hudOverlayWindow.setBounds(nextBounds, false);
positionUpdateToastWindow();
if (hudOverlayWindow.isVisible()) {
hudOverlayWindow.moveTop();
}
}

function setHudOverlayMousePassthrough(ignore: boolean) {
hudOverlayIgnoringMouse =
hudOverlaySourceSelectionActive && !hudOverlayRecordingActive ? true : ignore;
Expand All @@ -312,9 +280,10 @@ function setHudOverlayMousePassthrough(ignore: boolean) {
}

if (!isHudOverlayMousePassthroughSupported()) {
if (process.platform !== "linux") {
setHudOverlayFallbackExpanded(!ignore);
}
// Deliberately no resize here. This branch is Linux-only, and growing the
// window on hover moves the bar out from under the pointer, which fires
// mouseleave and shrinks it back, oscillating. The fallback window instead
// reserves menu headroom up front, so menus need no resize at all.
hudOverlayWindow.setIgnoreMouseEvents(false);
return;
}
Expand Down
16 changes: 2 additions & 14 deletions src/components/launch/popovers/MorePopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,13 @@ import { useI18n } from "@/contexts/I18nContext";
import { useScopedT } from "@/contexts/I18nContext";
import { useTheme } from "@/contexts/ThemeContext";
import type { AppLocale } from "@/i18n/config";
import { SUPPORTED_LOCALES } from "@/i18n/config";
import { LOCALE_LABELS, SUPPORTED_LOCALES } from "@/i18n/config";
import styles from "../LaunchWindow.module.css";
import { useLaunchPopoverCoordinator } from "./LaunchPopoverCoordinator";
import { DropdownItem, HudPopover } from "./PopoverScaffold";

const POPOVER_ID = "more";

const LOCALE_LABELS: Record<string, string> = {
en: "English",
es: "Español",
fr: "Français",
it: "Italiano",
nl: "Nederlands",
ko: "한국어",
"pt-BR": "Português",
"zh-CN": "簡體中文",
"zh-TW": "繁體中文",
};

export function MorePopover({
trigger,
supportsHudCaptureProtection,
Expand Down Expand Up @@ -170,7 +158,7 @@ export function MorePopover({
requestClose(POPOVER_ID);
}}
>
{LOCALE_LABELS[code] ?? code}
{LOCALE_LABELS[code]}
</DropdownItem>
))}
{appVersion && (
Expand Down
17 changes: 2 additions & 15 deletions src/components/video-editor/SettingsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
import { type AspectRatio } from "@/utils/aspectRatioUtils";
import { useI18n, useScopedT } from "../../contexts/I18nContext";
import type { AppLocale } from "../../i18n/config";
import { SUPPORTED_LOCALES } from "../../i18n/config";
import { LOCALE_LABELS, SUPPORTED_LOCALES } from "../../i18n/config";
import { AnnotationSettingsPanel } from "./AnnotationSettingsPanel";
import CaptionListPanel from "./CaptionListPanel";
import type { CaptionRetimeSpan } from "./captionOps";
Expand Down Expand Up @@ -708,19 +708,6 @@ const CAPTION_LANGUAGE_OPTIONS = [
{ value: "ko", label: "Korean" },
] as const;

const APP_LANGUAGE_LABELS: Record<AppLocale, string> = {
en: "English",
es: "Español",
fr: "Français",
de: "Deutsch",
it: "Italiano",
nl: "Nederlands",
ko: "한국어",
"pt-BR": "Português",
"zh-CN": "簡體中文",
"zh-TW": "繁體中文",
};

function loadPreviewImage(url: string) {
return new Promise<HTMLImageElement>((resolve, reject) => {
const image = new Image();
Expand Down Expand Up @@ -2576,7 +2563,7 @@ export function SettingsPanel({
<SelectContent className="border-foreground/10 bg-editor-surface-alt text-foreground">
{SUPPORTED_LOCALES.map((candidateLocale) => (
<SelectItem key={candidateLocale} value={candidateLocale}>
{APP_LANGUAGE_LABELS[candidateLocale]}
{LOCALE_LABELS[candidateLocale]}
</SelectItem>
))}
</SelectContent>
Expand Down
18 changes: 18 additions & 0 deletions src/i18n/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,21 @@ export const I18N_NAMESPACES = [

export type AppLocale = (typeof SUPPORTED_LOCALES)[number];
export type I18nNamespace = (typeof I18N_NAMESPACES)[number];

/**
* Native display name per locale. Typed against AppLocale so adding a locale to
* SUPPORTED_LOCALES without a label is a compile error rather than a menu row
* that renders the raw locale code.
*/
export const LOCALE_LABELS: Record<AppLocale, string> = {
en: "English",
es: "Español",
fr: "Français",
de: "Deutsch",
it: "Italiano",
nl: "Nederlands",
ko: "한국어",
"pt-BR": "Português",
"zh-CN": "简体中文",
"zh-TW": "繁體中文",
};