This commit is contained in:
legop3
2026-09-12 16:16:59 -04:00
parent 99d2a7689f
commit fb31ff52bd
28 changed files with 545 additions and 88 deletions
@@ -11,6 +11,7 @@ import useDefaultNickname from '../../hooks/useDefaultNickname.js';
import useUserIdentitySync from '../../hooks/useUserIdentitySync.js';
import PtzLiveVideo from '../../components/PtzLiveVideo/index.jsx';
import RoverMediaPlayer from '../../components/RoverMediaPlayer/index.jsx';
import RoverHelpOverlay from '../../components/RoverHelpOverlay/index.jsx';
import FitViewportFrame from './components/FitViewportFrame.jsx';
import InfoColumn from './components/InfoColumn.jsx';
import { ROTATE_MS } from './constants.js';
@@ -352,6 +353,7 @@ export default function MiniSummaryContent() {
/>
)}
</FitViewportFrame>
<RoverHelpOverlay active={Boolean(rover.needsHelp)} />
</div>
);
})}
@@ -1,65 +0,0 @@
// Auto Fit Text
// Purpose: Defines the Auto Fit Text module and the local helpers/components used in this file.
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
import { useLayoutEffect, useRef, useState } from 'react';
export default function AutoFitText({ children, className = '', maxSize = 1000, minSize = 14, style = undefined }) {
const containerRef = useRef(null);
const textRef = useRef(null);
const [fontSize, setFontSize] = useState(maxSize);
useLayoutEffect(() => {
const container = containerRef.current;
const textEl = textRef.current;
if (!container || !textEl) return undefined;
let raf = null;
const fit = () => {
const width = container.clientWidth;
if (!width) {
scheduleFit();
return;
}
let low = minSize;
let high = maxSize;
let best = minSize;
while (low <= high) {
const mid = Math.floor((low + high) / 2);
textEl.style.fontSize = `${mid}px`;
const fits = textEl.scrollWidth <= width;
if (fits) {
best = mid;
low = mid + 1;
} else {
high = mid - 1;
}
}
setFontSize(best);
};
const scheduleFit = () => {
if (raf) cancelAnimationFrame(raf);
raf = requestAnimationFrame(fit);
};
scheduleFit();
const ro = new ResizeObserver(scheduleFit);
ro.observe(container);
return () => {
if (raf) cancelAnimationFrame(raf);
ro.disconnect();
};
}, [children, maxSize, minSize]);
return (
<div ref={containerRef} className="w-full min-w-0">
<div
ref={textRef}
className={`whitespace-nowrap ${className}`}
style={{ fontSize: `${fontSize}px`, lineHeight: 1.1, ...(style || {}) }}
>
{children}
</div>
</div>
);
}
@@ -4,8 +4,9 @@
import RoverMediaPlayer from '../../../components/RoverMediaPlayer/index.jsx';
import BatteryBar from '../../../components/BatteryBar/index.jsx';
import RoverLabel from '../../../components/RoverLabel/index.jsx';
import RoverHelpOverlay from '../../../components/RoverHelpOverlay/index.jsx';
import AutoFitText from '../../../components/AutoFitText/index.jsx';
import { getBatteryVisual } from '../utils.js';
import AutoFitText from './AutoFitText.jsx';
export default function InfoColumn({
rover,
@@ -32,6 +33,10 @@ export default function InfoColumn({
orientation={isActiveView ? 'vertical' : 'horizontal'}
variant="background"
/>
{/* In the side-by-side roster this column is the rover's complete tile.
The active carousel already overlays its video pane, so suppressing a
second copy here avoids flashing HELP twice for the same rover. */}
<RoverHelpOverlay active={!isActiveView && Boolean(rover?.needsHelp)} />
{isActiveView ? (
<div className="relative z-10 flex min-w-0 flex-1 flex-col justify-between text-center">
<div className="min-w-0 bg-transparent px-0 py-0 leading-none">