feat: expand admin streaming pills, add high-end resource warning

Replaces hardcoded 3-resolution/3-framerate arrays with ALL_RESOLUTIONS
and STANDARD_FRAMERATES from shared constants (540p–Native, 30–120 fps).
Adds amber warning banner when high-end options (≥1440p or ≥75 fps) are
enabled, and fixes toggleResolution to handle 'native' with correct sort.
This commit is contained in:
Jannis Braun
2026-03-21 23:30:46 +01:00
parent 5ac0917d81
commit a9a676bc70
@@ -1,9 +1,12 @@
import { useState, useEffect } from 'react';
import { useSettingsStore } from '../../../stores/settingsStore';
import type { InstanceStreamingLimits } from '@backspace/shared';
const VALID_RESOLUTIONS = [540, 720, 1080] as const;
const VALID_FRAMERATES = [30, 45, 60] as const;
import {
STANDARD_FRAMERATES,
ALL_RESOLUTIONS, RESOLUTION_LABELS,
HIGH_END_RESOLUTION_THRESHOLD, HIGH_END_FRAMERATE_THRESHOLD,
type Resolution,
} from '@backspace/shared/src/constants';
function formatKbps(kbps: number): string {
return kbps >= 1000
@@ -48,7 +51,7 @@ export function StreamingPanel() {
setSaveError('');
};
const toggleResolution = (res: number) => {
const toggleResolution = (res: number | 'native') => {
const current = new Set(draft.allowedResolutions);
if (current.has(res)) {
if (current.size <= 1) return;
@@ -56,7 +59,10 @@ export function StreamingPanel() {
} else {
current.add(res);
}
setDraft({ ...draft, allowedResolutions: Array.from(current).sort((a, b) => a - b) });
// Sort: numbers ascending, 'native' always last
const nums: (number | 'native')[] = Array.from(current).filter((r): r is number => r !== 'native').sort((a, b) => a - b);
if (current.has('native')) nums.push('native');
setDraft({ ...draft, allowedResolutions: nums });
};
const toggleFramerate = (fps: number) => {
@@ -160,14 +166,14 @@ export function StreamingPanel() {
<div className="text-xs text-txt-secondary mb-1.5">
Allowed Resolutions
</div>
<div className="flex gap-1.5">
{VALID_RESOLUTIONS.map((res) => (
<div className="flex flex-wrap gap-1.5">
{ALL_RESOLUTIONS.map((res) => (
<button
key={res}
key={String(res)}
onClick={() => toggleResolution(res)}
className={`${pillBase} ${draft.allowedResolutions.includes(res) ? pillOn : pillOff}`}
>
{res}p
{RESOLUTION_LABELS[res]}
</button>
))}
</div>
@@ -178,8 +184,8 @@ export function StreamingPanel() {
<div className="text-xs text-txt-secondary mb-1.5">
Allowed Frame Rates
</div>
<div className="flex gap-1.5">
{VALID_FRAMERATES.map((fps) => (
<div className="flex flex-wrap gap-1.5">
{STANDARD_FRAMERATES.map((fps) => (
<button
key={fps}
onClick={() => toggleFramerate(fps)}
@@ -193,6 +199,29 @@ export function StreamingPanel() {
</div>
</div>
{/* High-end resource warning */}
{(draft.allowedResolutions.some(
(r) => r === 'native' || (typeof r === 'number' && r >= HIGH_END_RESOLUTION_THRESHOLD)
) || draft.allowedFramerates.some(
(f) => f >= HIGH_END_FRAMERATE_THRESHOLD
)) && (
<div className="p-3 bg-accent-amber/10 border border-accent-amber/30 rounded-lg">
<div className="flex items-start gap-2">
<svg className="w-4 h-4 text-accent-amber mt-0.5 shrink-0" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.168 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 6a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 6zm0 9a1 1 0 100-2 1 1 0 000 2z" clipRule="evenodd" />
</svg>
<div className="text-xs text-accent-amber/90 leading-relaxed">
<span className="font-semibold">High-performance settings enabled.</span>{' '}
Streaming above 1080p or 60 fps requires significant client-side CPU/GPU encoding
power and can saturate server bandwidth, especially when streams are routed through
TURN. High-end configurations (e.g., 4K at 120 fps) can require up to 45 Mbps per
active stream. Ensure your infrastructure can handle this load before enabling these
options for all users.
</div>
</div>
</div>
)}
{/* Save / Reset */}
{saveError && (
<div className="p-2 bg-accent-rose/10 border border-accent-rose/30 rounded text-txt-danger text-sm">{saveError}</div>