diff --git a/src/components/OnboardingTour.tsx b/src/components/OnboardingTour.tsx index 4a9fa0e6..d5c163bc 100644 --- a/src/components/OnboardingTour.tsx +++ b/src/components/OnboardingTour.tsx @@ -251,7 +251,7 @@ export default function OnboardingTour() { useEffect(() => { if (localStorage.getItem(TOUR_KEY)) return; const t = setTimeout(async () => { - const rect = await measureTarget(TOUR_STEPS[0].targetId); + const rect = await measureTarget(TOUR_STEPS[0]?.targetId ?? ""); if (rect) { setTargetRect(rect); setVisible(true); @@ -267,7 +267,7 @@ useEffect(() => { isFirstRender.current = false; return; } - measureTarget(TOUR_STEPS[stepIndex].targetId).then((rect) => { + measureTarget(TOUR_STEPS[stepIndex]?.targetId ?? "").then((rect) => { if (rect) { setTargetRect(rect); setTimeout(() => tooltipRef.current?.focus(), 50); @@ -285,7 +285,7 @@ useEffect(() => { useEffect(() => { if (!visible) return; const onResize = () => { - measureTarget(TOUR_STEPS[stepIndex].targetId).then(setTargetRect); + measureTarget(TOUR_STEPS[stepIndex]?.targetId ?? "").then(setTargetRect); }; window.addEventListener("resize", onResize); return () => window.removeEventListener("resize", onResize); @@ -318,7 +318,7 @@ useEffect(() => { /> - + ), }, @@ -66,7 +66,7 @@ const QUICK_ACTIONS = [ platform: "TikTok", icon: ( - + ), }, @@ -76,7 +76,7 @@ const QUICK_ACTIONS = [ platform: "YouTube", icon: ( - + ), }, @@ -86,7 +86,7 @@ const QUICK_ACTIONS = [ platform: "YouTube", icon: ( - + ), }, @@ -96,7 +96,7 @@ const QUICK_ACTIONS = [ platform: "Twitter", icon: ( - + ), }, @@ -122,14 +122,18 @@ export default function PresetSelector({ recipe, onChange }: Props) { const handleWidthChange = useCallback( (width: number) => { - onChange({ customWidth: width }); + if (!isNaN(width) && width >= 16 && width <= 7680) { + onChange({ customWidth: width }); + } }, [onChange], ); const handleHeightChange = useCallback( (height: number) => { - onChange({ customHeight: height }); + if (!isNaN(height) && height >= 16 && height <= 7680) { + onChange({ customHeight: height }); + } }, [onChange], ); @@ -280,6 +284,7 @@ export default function PresetSelector({ recipe, onChange }: Props) { ((resolve) => { const onSeeked = () => { video.removeEventListener("seeked", onSeeked); @@ -117,7 +117,7 @@ export default function ThumbnailStrip({ const activeIndex = thumbnails.findIndex( (t, i) => currentTime >= t.time && - (i === thumbnails.length - 1 || currentTime < thumbnails[i + 1].time) + (i === thumbnails.length - 1 || currentTime < (thumbnails[i + 1]?.time ?? Infinity)) ); if (!videoSrc) return null; diff --git a/src/components/TipCarousel.tsx b/src/components/TipCarousel.tsx index 0500dd19..c16fde92 100644 --- a/src/components/TipCarousel.tsx +++ b/src/components/TipCarousel.tsx @@ -80,7 +80,8 @@ export default function TipCarousel() { }; const activeTip = TIPS[activeIdx]; - const IconComponent = activeTip.icon; + if (!activeTip) return null; + const IconComponent = activeTip.icon; return (

- We detected a {recommendedPreset.label.replace(/\s/g, "")} video → Recommended: {recommendedPreset.platform.split("·")[0].trim()} ({recommendedPreset.label.replace(/\s/g, "")}) + We detected a {recommendedPreset.label.replace(/\s/g, "")} video → Recommended: {(recommendedPreset.platform.split("·")[0] ?? "").trim()} ({recommendedPreset.label.replace(/\s/g, "")})

)} diff --git a/src/components/WaveformCanvas.tsx b/src/components/WaveformCanvas.tsx index a170a0e7..cff6a6bc 100644 --- a/src/components/WaveformCanvas.tsx +++ b/src/components/WaveformCanvas.tsx @@ -54,7 +54,7 @@ export default function WaveformCanvas({ samples, loading, hasAudio }: Props) { ctx.globalAlpha = 0.7; for (let i = 0; i < samples.length; i++) { - const amplitude = samples[i]; + const amplitude = samples[i] ?? 0; const barHeight = Math.max(amplitude * (height * 0.92), 1.5); const x = i * barWidth; const y = midY - barHeight / 2; diff --git a/tsconfig.json b/tsconfig.json index 5310d2e1..5fe25c1c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "allowJs": true, "skipLibCheck": true, "strict": true, + "noUncheckedIndexedAccess": true, "noEmit": true, "esModuleInterop": true, "module": "esnext",