({ toolSelector })
| 127 | BackgroundRenderer.displayName = 'BackgroundRenderer'; |
| 128 | |
| 129 | export default function BackgroundStudio({ toolSelector }) { |
| 130 | const [searchParams, setSearchParams] = useSearchParams(); |
| 131 | const [mobileControlsOpen, setMobileControlsOpen] = useState(false); |
| 132 | const isMobile = useBreakpointValue({ base: true, lg: false }); |
| 133 | |
| 134 | const [localProps, setLocalProps] = useState({}); |
| 135 | const [renderKey, setRenderKey] = useState(0); |
| 136 | const [isRecording, setIsRecording] = useState(false); |
| 137 | const [recordingProgress, setRecordingProgress] = useState(0); |
| 138 | const [canvasBg, setCanvasBg] = useState('#120F17'); |
| 139 | const debounceTimer = useRef(null); |
| 140 | const previewRef = useRef(null); |
| 141 | const mediaRecorderRef = useRef(null); |
| 142 | const recordingAnimationRef = useRef(null); |
| 143 | const recordingIntervalRef = useRef(null); |
| 144 | const recordingTimeoutRef = useRef(null); |
| 145 | |
| 146 | const backgroundId = searchParams.get('bg') || 'silk'; |
| 147 | |
| 148 | const background = useMemo(() => { |
| 149 | return getBackgroundById(backgroundId) || BACKGROUNDS[0]; |
| 150 | }, [backgroundId]); |
| 151 | |
| 152 | const customProps = useMemo(() => { |
| 153 | if (!background?.props) return {}; |
| 154 | const parsed = {}; |
| 155 | for (const propDef of background.props) { |
| 156 | const value = searchParams.get(propDef.name); |
| 157 | if (value !== null) { |
| 158 | if (propDef.type === 'number') { |
| 159 | parsed[propDef.name] = Number(value); |
| 160 | } else if (propDef.type === 'boolean') { |
| 161 | parsed[propDef.name] = value === 'true'; |
| 162 | } else if (propDef.type === 'colorArray') { |
| 163 | parsed[propDef.name] = value.split(',').map(c => (c.startsWith('#') ? c : `#${c}`)); |
| 164 | } else if (propDef.type === 'color') { |
| 165 | parsed[propDef.name] = value.startsWith('#') ? value : `#${value}`; |
| 166 | } else if (propDef.type === 'rgbArray') { |
| 167 | parsed[propDef.name] = value.split(',').map(Number); |
| 168 | } else { |
| 169 | parsed[propDef.name] = value; |
| 170 | } |
| 171 | } |
| 172 | } |
| 173 | return parsed; |
| 174 | }, [background, searchParams]); |
| 175 | |
| 176 | const baseProps = useMemo(() => { |
| 177 | const defaults = getDefaultProps(background); |
| 178 | return { ...defaults, ...customProps }; |
| 179 | }, [background, customProps]); |
| 180 | |
| 181 | const props = useMemo(() => { |
| 182 | return { ...baseProps, ...localProps }; |
| 183 | }, [baseProps, localProps]); |
| 184 | |
| 185 | useEffect(() => { |
| 186 | setLocalProps({}); |
nothing calls this directly
no test coverage detected