feat: dynamic resolution/framerate pills from instance limits

This commit is contained in:
Jannis Braun
2026-03-21 23:30:14 +01:00
parent 57b192533d
commit 5ac0917d81
@@ -7,6 +7,7 @@ import { buildScreenShareOptions } from '../../utils/screenShare';
import { useFloatingPosition } from '../../hooks/useFloatingPosition'; import { useFloatingPosition } from '../../hooks/useFloatingPosition';
import { Toggle } from '../ui/Toggle'; import { Toggle } from '../ui/Toggle';
import { isElectron } from '../../platform/platform'; import { isElectron } from '../../platform/platform';
import { RESOLUTION_LABELS } from '@backspace/shared/src/constants';
interface ScreenShareSettingsPopoverProps { interface ScreenShareSettingsPopoverProps {
open: boolean; open: boolean;
@@ -14,18 +15,6 @@ interface ScreenShareSettingsPopoverProps {
anchorRef: React.RefObject<HTMLElement | null>; anchorRef: React.RefObject<HTMLElement | null>;
} }
const ALL_RESOLUTIONS: { value: ScreenShareConfig['height']; label: string }[] = [
{ value: 540, label: '540p' },
{ value: 720, label: '720p' },
{ value: 1080, label: '1080p' },
];
const ALL_FRAME_RATES: { value: ScreenShareConfig['fps']; label: string }[] = [
{ value: 30, label: '30' },
{ value: 45, label: '45' },
{ value: 60, label: '60' },
];
const MODES: { value: ScreenShareConfig['mode']; label: string }[] = [ const MODES: { value: ScreenShareConfig['mode']; label: string }[] = [
{ value: 'gaming', label: 'Gaming' }, { value: 'gaming', label: 'Gaming' },
{ value: 'text', label: 'Text' }, { value: 'text', label: 'Text' },
@@ -66,12 +55,14 @@ export function ScreenShareSettingsPopover({ open, onClose, anchorRef }: ScreenS
const BITRATE_MAX = limits?.maxBitrateKbps ?? 20000; const BITRATE_MAX = limits?.maxBitrateKbps ?? 20000;
const BITRATE_STEP = limits?.bitrateStepKbps ?? 500; const BITRATE_STEP = limits?.bitrateStepKbps ?? 500;
const RESOLUTIONS = ALL_RESOLUTIONS.filter((r) => const RESOLUTIONS = (limits?.allowedResolutions ?? [540, 720, 1080]).map((r) => ({
(limits?.allowedResolutions ?? [540, 720, 1080]).includes(r.value) value: r as ScreenShareConfig['height'],
); label: RESOLUTION_LABELS[r as keyof typeof RESOLUTION_LABELS] ?? `${r}p`,
const FRAME_RATES = ALL_FRAME_RATES.filter((f) => }));
(limits?.allowedFramerates ?? [30, 45, 60]).includes(f.value) const FRAME_RATES = (limits?.allowedFramerates ?? [30, 45, 60]).map((f) => ({
); value: f as ScreenShareConfig['fps'],
label: `${f}`,
}));
useEffect(() => { useEffect(() => {
if (!open) return; if (!open) return;
@@ -89,15 +80,22 @@ export function ScreenShareSettingsPopover({ open, onClose, anchorRef }: ScreenS
if (!limits) return; if (!limits) return;
const patch: Partial<ScreenShareConfig> = {}; const patch: Partial<ScreenShareConfig> = {};
if (!limits.allowedResolutions.includes(config.height)) { if (!limits.allowedResolutions.includes(config.height)) {
const closest = limits.allowedResolutions.reduce((a, b) => const numericRes = limits.allowedResolutions.filter((r): r is number => r !== 'native');
Math.abs(b - config.height) < Math.abs(a - config.height) ? b : a if (typeof config.height === 'number' && numericRes.length > 0) {
) as ScreenShareConfig['height']; const h = config.height;
patch.height = closest; patch.height = numericRes.reduce((a, b) =>
Math.abs(b - h) < Math.abs(a - h) ? b : a
);
} else {
// 'native' was disabled or no numeric options — fall back to highest numeric
patch.height = numericRes.length > 0 ? Math.max(...numericRes) : 1080;
}
} }
if (!limits.allowedFramerates.includes(config.fps)) { if (!limits.allowedFramerates.includes(config.fps)) {
const f = config.fps;
const closest = limits.allowedFramerates.reduce((a, b) => const closest = limits.allowedFramerates.reduce((a, b) =>
Math.abs(b - config.fps) < Math.abs(a - config.fps) ? b : a Math.abs(b - f) < Math.abs(a - f) ? b : a
) as ScreenShareConfig['fps']; );
patch.fps = closest; patch.fps = closest;
} }
if (config.customBitrateKbps != null) { if (config.customBitrateKbps != null) {
@@ -131,7 +129,7 @@ export function ScreenShareSettingsPopover({ open, onClose, anchorRef }: ScreenS
<div className="text-[11px] text-txt-tertiary font-semibold uppercase tracking-wider mb-1.5"> <div className="text-[11px] text-txt-tertiary font-semibold uppercase tracking-wider mb-1.5">
Resolution Resolution
</div> </div>
<div className="flex gap-1.5"> <div className="flex flex-wrap gap-1.5">
{RESOLUTIONS.map((r) => ( {RESOLUTIONS.map((r) => (
<button <button
key={r.value} key={r.value}
@@ -149,7 +147,7 @@ export function ScreenShareSettingsPopover({ open, onClose, anchorRef }: ScreenS
<div className="text-[11px] text-txt-tertiary font-semibold uppercase tracking-wider mb-1.5"> <div className="text-[11px] text-txt-tertiary font-semibold uppercase tracking-wider mb-1.5">
Frame Rate Frame Rate
</div> </div>
<div className="flex gap-1.5"> <div className="flex flex-wrap gap-1.5">
{FRAME_RATES.map((f) => ( {FRAME_RATES.map((f) => (
<button <button
key={f.value} key={f.value}