mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-24 23:05:49 +00:00
Fix HUD layout and overlay sizing
This commit is contained in:
Vendored
+1
@@ -27,6 +27,7 @@ interface Window {
|
||||
hudOverlayHide: () => void;
|
||||
hudOverlayClose: () => void;
|
||||
setHudOverlayExpanded: (expanded: boolean) => void;
|
||||
setHudOverlayCompactWidth: (width: number) => void;
|
||||
getHudOverlayCaptureProtection: () => Promise<{ success: boolean; enabled: boolean }>;
|
||||
setHudOverlayCaptureProtection: (
|
||||
enabled: boolean,
|
||||
|
||||
@@ -10,6 +10,9 @@ contextBridge.exposeInMainWorld("electronAPI", {
|
||||
setHudOverlayExpanded: (expanded: boolean) => {
|
||||
ipcRenderer.send("set-hud-overlay-expanded", expanded);
|
||||
},
|
||||
setHudOverlayCompactWidth: (width: number) => {
|
||||
ipcRenderer.send("set-hud-overlay-compact-width", width);
|
||||
},
|
||||
getHudOverlayCaptureProtection: () => {
|
||||
return ipcRenderer.invoke("get-hud-overlay-capture-protection");
|
||||
},
|
||||
|
||||
+33
-5
@@ -26,11 +26,14 @@ const HUD_BOTTOM_CLEARANCE_CM = 3.5;
|
||||
const DIP_PER_INCH = 96;
|
||||
const CM_PER_INCH = 2.54;
|
||||
const HUD_EDGE_MARGIN_DIP = 16;
|
||||
const HUD_SHADOW_BLEED_DIP = 20;
|
||||
const HUD_WINDOW_WIDTH = 560;
|
||||
const HUD_SHADOW_BLEED_DIP = 36;
|
||||
const HUD_MIN_WINDOW_WIDTH = 560;
|
||||
const HUD_COMPACT_HEIGHT = 96;
|
||||
const HUD_EXPANDED_HEIGHT = 520 + HUD_SHADOW_BLEED_DIP;
|
||||
|
||||
let hudOverlayExpanded = false;
|
||||
let hudOverlayCompactWidth = HUD_MIN_WINDOW_WIDTH;
|
||||
|
||||
function isHudOverlayCaptureProtectionSupported(): boolean {
|
||||
return process.platform !== "linux";
|
||||
}
|
||||
@@ -78,7 +81,11 @@ function getScreen() {
|
||||
function getHudOverlayBounds(expanded: boolean) {
|
||||
const primaryDisplay = getScreen().getPrimaryDisplay();
|
||||
const { bounds, workArea } = primaryDisplay;
|
||||
const windowWidth = HUD_WINDOW_WIDTH;
|
||||
const maxWindowWidth = Math.max(HUD_MIN_WINDOW_WIDTH, workArea.width - HUD_EDGE_MARGIN_DIP * 2);
|
||||
const windowWidth = Math.min(
|
||||
maxWindowWidth,
|
||||
Math.max(HUD_MIN_WINDOW_WIDTH, Math.round(hudOverlayCompactWidth)),
|
||||
);
|
||||
const windowHeight = expanded ? HUD_EXPANDED_HEIGHT : HUD_COMPACT_HEIGHT;
|
||||
const bottomClearanceDip = Math.round((HUD_BOTTOM_CLEARANCE_CM / CM_PER_INCH) * DIP_PER_INCH);
|
||||
const screenBottom = bounds.y + bounds.height;
|
||||
@@ -106,6 +113,8 @@ function applyHudOverlayBounds(expanded: boolean) {
|
||||
return;
|
||||
}
|
||||
|
||||
hudOverlayExpanded = expanded;
|
||||
|
||||
hudOverlayWindow.setBounds(getHudOverlayBounds(expanded), false);
|
||||
if (!hudOverlayWindow.isVisible()) {
|
||||
return;
|
||||
@@ -123,6 +132,26 @@ ipcMain.on("set-hud-overlay-expanded", (_event, expanded: boolean) => {
|
||||
applyHudOverlayBounds(Boolean(expanded));
|
||||
});
|
||||
|
||||
ipcMain.on("set-hud-overlay-compact-width", (_event, width: number) => {
|
||||
if (!Number.isFinite(width)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const primaryDisplay = getScreen().getPrimaryDisplay();
|
||||
const maxWindowWidth = Math.max(
|
||||
HUD_MIN_WINDOW_WIDTH,
|
||||
primaryDisplay.workArea.width - HUD_EDGE_MARGIN_DIP * 2,
|
||||
);
|
||||
const nextWidth = Math.min(maxWindowWidth, Math.max(HUD_MIN_WINDOW_WIDTH, Math.round(width)));
|
||||
|
||||
if (nextWidth === hudOverlayCompactWidth) {
|
||||
return;
|
||||
}
|
||||
|
||||
hudOverlayCompactWidth = nextWidth;
|
||||
applyHudOverlayBounds(hudOverlayExpanded);
|
||||
});
|
||||
|
||||
ipcMain.handle("get-hud-overlay-capture-protection", () => {
|
||||
const enabled = loadHudOverlayCaptureProtectionSetting();
|
||||
|
||||
@@ -158,8 +187,7 @@ export function createHudOverlayWindow(): BrowserWindow {
|
||||
const win = new BrowserWindow({
|
||||
width: initialBounds.width,
|
||||
height: initialBounds.height,
|
||||
minWidth: HUD_WINDOW_WIDTH,
|
||||
maxWidth: HUD_WINDOW_WIDTH,
|
||||
minWidth: HUD_MIN_WINDOW_WIDTH,
|
||||
minHeight: HUD_COMPACT_HEIGHT,
|
||||
maxHeight: HUD_EXPANDED_HEIGHT,
|
||||
x: initialBounds.x,
|
||||
|
||||
@@ -9,13 +9,16 @@
|
||||
.bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
gap: 10px;
|
||||
width: max-content;
|
||||
max-width: 1200px;
|
||||
background: rgba(18, 18, 24, 0.97);
|
||||
border: 1px solid rgba(255, 255, 255, 0.07);
|
||||
border-radius: 18px;
|
||||
padding: 10px 14px;
|
||||
border-radius: 20px;
|
||||
padding: 11px 20px 11px 18px;
|
||||
box-shadow:
|
||||
0 8px 40px rgba(0, 0, 0, 0.45),
|
||||
0 10px 30px rgba(0, 0, 0, 0.24),
|
||||
0 2px 10px rgba(0, 0, 0, 0.12),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.04);
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
@@ -26,16 +29,19 @@
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 0 1 auto;
|
||||
min-height: 46px;
|
||||
min-width: 0;
|
||||
min-width: fit-content;
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.barState {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex: 0 1 auto;
|
||||
white-space: nowrap;
|
||||
min-width: 0;
|
||||
min-width: fit-content;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
@@ -43,7 +49,7 @@
|
||||
width: 1px;
|
||||
height: 26px;
|
||||
background: #2a2a34;
|
||||
margin: 0 4px;
|
||||
margin: 0 6px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -98,6 +104,8 @@
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
height: 40px;
|
||||
min-width: 0;
|
||||
max-width: 640px;
|
||||
padding: 0 14px 0 12px;
|
||||
border-radius: 11px;
|
||||
border: 1px solid #2a2a34;
|
||||
@@ -107,7 +115,7 @@
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
flex-shrink: 0;
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
|
||||
.screenSel:hover {
|
||||
@@ -115,13 +123,22 @@
|
||||
background: #20202a;
|
||||
}
|
||||
|
||||
.sourceLabel {
|
||||
display: inline-block;
|
||||
min-width: 0;
|
||||
max-width: 520px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.menuArea {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
overflow: visible;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
@@ -135,7 +152,9 @@
|
||||
padding: 8px;
|
||||
margin-top: auto;
|
||||
margin-bottom: 8px;
|
||||
box-shadow: 0 12px 48px rgba(0, 0, 0, 0.5);
|
||||
box-shadow:
|
||||
0 12px 32px rgba(0, 0, 0, 0.22),
|
||||
0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
pointer-events: auto;
|
||||
animation: menuCardIn 0.18s ease;
|
||||
}
|
||||
|
||||
@@ -175,6 +175,7 @@ export function LaunchWindow() {
|
||||
const [hideHudFromCapture, setHideHudFromCapture] = useState(true);
|
||||
const [platform, setPlatform] = useState<string | null>(null);
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
const hudBarRef = useRef<HTMLDivElement>(null);
|
||||
const webcamPreviewRef = useRef<HTMLVideoElement | null>(null);
|
||||
|
||||
const micDropdownOpen = activeDropdown === "mic";
|
||||
@@ -358,6 +359,43 @@ export function LaunchWindow() {
|
||||
};
|
||||
}, [activeDropdown]);
|
||||
|
||||
useEffect(() => {
|
||||
const hudBar = hudBarRef.current;
|
||||
if (!hudBar || typeof ResizeObserver === "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
let frameId = 0;
|
||||
const reportWidth = () => {
|
||||
frameId = 0;
|
||||
const measuredWidth = Math.ceil(
|
||||
Math.max(hudBar.getBoundingClientRect().width, hudBar.scrollWidth) + 24,
|
||||
);
|
||||
window.electronAPI.setHudOverlayCompactWidth(measuredWidth);
|
||||
};
|
||||
|
||||
const scheduleWidthReport = () => {
|
||||
if (frameId !== 0) {
|
||||
cancelAnimationFrame(frameId);
|
||||
}
|
||||
frameId = requestAnimationFrame(reportWidth);
|
||||
};
|
||||
|
||||
scheduleWidthReport();
|
||||
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
scheduleWidthReport();
|
||||
});
|
||||
resizeObserver.observe(hudBar);
|
||||
|
||||
return () => {
|
||||
resizeObserver.disconnect();
|
||||
if (frameId !== 0) {
|
||||
cancelAnimationFrame(frameId);
|
||||
}
|
||||
};
|
||||
}, [selectedSource, recording, paused, activeDropdown]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClick = (e: MouseEvent) => {
|
||||
if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
|
||||
@@ -528,7 +566,9 @@ export function LaunchWindow() {
|
||||
title={selectedSource}
|
||||
>
|
||||
<Monitor size={16} />
|
||||
<ContentClamp truncateLength={10}>{selectedSource}</ContentClamp>
|
||||
<ContentClamp className={styles.sourceLabel} truncateLength={36}>
|
||||
{selectedSource}
|
||||
</ContentClamp>
|
||||
<ChevronUp size={10} className={`text-[#6b6b78] ml-0.5 transition-transform duration-200 ${activeDropdown === "sources" ? "" : "rotate-180"}`} />
|
||||
</button>
|
||||
|
||||
@@ -805,6 +845,7 @@ export function LaunchWindow() {
|
||||
|
||||
<div className="flex flex-col items-center pointer-events-auto">
|
||||
<motion.div
|
||||
ref={hudBarRef}
|
||||
layout
|
||||
transition={hudStateTransition}
|
||||
className={`${styles.bar} ${styles.electronDrag} mb-2`}
|
||||
|
||||
Reference in New Issue
Block a user