import {TimedLyrics,MidiPreview} from './TimedLyrics';
import {ReferenceUpload,Reference} from './References';
import {l,setUILanguage} from './locale';
import React, { useState, useEffect, useRef } from 'react';
import { createRoot } from 'react-dom/client';
import { AppContext } from './context';
import { Icon, Cover, TrackCards, TrackRows, Dialog, Field, Empty } from './components';
import { seedTracks, Track, Playlist, tools, asset, audioSrc, uid, fmt, styles } from './data';
import { dictionaries, Language } from './i18n';
import { api, read, write, server, getBlob, saveBlob, clearBlobs } from './storage';
import { Library, Toolbox, Voices, Billing, Settings, Admin, Landing, ToolForm } from './pages';
import './styles.css';
function App() {
    const [lang, setLang] = useState<Language>(read('language', 'it'));
    setUILanguage(lang);const t = dictionaries[lang];
    const [route, setRoute] = useState(location.hash.slice(2) || 'home');
    const [search, setSearch] = useState('');
    const [tracks, setTracks] = useState<Track[]>(server ? [] : read('tracks', seedTracks));
    const [playlists, setPlaylists] = useState<Playlist[]>(server ? [] : read('playlists', [{ id: 'collection-1', name: l("Il mio spazio sonoro"), tracks: ['demo-1', 'demo-4'] }]));
    const [user, setUser] = useState<any>(server ? null : { name: read<any>('profile', {}).name || 'Creator', role: 'demo', credits: 0 });
    const [config, setConfig] = useState<any>({ provider: false, payments: false, brand: 'GenMiu', demo: !server });
    const [references,setReferences]=useState<Reference[]>([]);const [identities,setIdentities]=useState<any>({personas:[],voices:[]});
    const [jobs, setJobs] = useState<any[]>([]);
    const [modal, setModal] = useState<any>(null);
    const [toast, setToast] = useState('');
    const [current, setCurrent] = useState<Track | null>(server ? null : tracks[0] || null);
    const [playing, setPlaying] = useState(false);
    const [time, setTime] = useState(0);
    const [duration, setDuration] = useState(0);
    const [volume, setVolume] = useState(.7);
    const [repeat, setRepeat] = useState(0);
    const [shuffle, setShuffle] = useState(false);
    const [queue, setQueue] = useState<Track[]>([]);
    const [panel, setPanel] = useState<string | null>(null);
    const [full, setFull] = useState(false);
    const [buffering, setBuffering] = useState(false);
    const [profile, setProfile] = useState(false);
    const [playerError, setPlayerError] = useState('');
    const [prompt, setPrompt] = useState(read('draft', ''));
    const [style, setStyle] = useState(read('style', ''));
    const [instrumental, setInstrumental] = useState(true);
    const [advanced, setAdvanced] = useState(false);
    const [busy, setBusy] = useState(false);
    const [model, setModel] = useState('V6');
    const [trackDuration, setTrackDuration] = useState(180);
    const [installEvent, setInstallEvent] = useState<any>(null);
    const audio = useRef<HTMLAudioElement>(null);const segment=useRef<{id:string;start:number;end:number}|null>(null);
    const wantPlay = useRef(false);
    const inputFile = useRef<HTMLInputElement>(null);
    const submitting = useRef(false);
    const notify = (msg: string) => { setToast(msg); window.setTimeout(() => setToast(''), 4000); };
    function navigate(s: string) { location.hash = '/' + s; setSearch(''); setProfile(false); setPanel(null); window.scrollTo({ top: 0, behavior: 'instant' }); }
    useEffect(() => { const f = () => setRoute(location.hash.slice(2) || 'home'); window.addEventListener('hashchange', f); const install = (e: Event) => { e.preventDefault(); setInstallEvent(e); }; window.addEventListener('beforeinstallprompt', install); return () => { window.removeEventListener('hashchange', f); window.removeEventListener('beforeinstallprompt', install); }; }, []);
    async function boot() { try {
        const d = await api('bootstrap');
        setConfig(d.config);setIdentities({personas:d.personas||[],voices:d.voices||[]});write('server-projects',d.projects||[]);if(d.projects)write('projects',d.projects.map((x:any)=>x.name));
        setUser(d.user);
        setTracks(d.tracks || []);
        setPlaylists(d.playlists || []);
        setJobs(d.jobs || []);
        if (d.user) {
            write('last-account', d.user.id);
            write('snapshot-' + d.user.id, { user: d.user, tracks: d.tracks, playlists: d.playlists, jobs: d.jobs, config: d.config });
        }
    }
    catch (e) {
        if (!navigator.onLine) {
            const id = read('last-account', '');
            const cached = read<any>('snapshot-' + id, null);
            if (cached) {
                setUser(cached.user);
                setTracks(cached.tracks || []);
                setPlaylists(cached.playlists || []);
                setConfig(cached.config);
                notify(l("Offline \u00B7 catalogo dell\u2019ultima sincronizzazione."));
            }
        }
        else
            notify((e as Error).message);
    } }
    useEffect(() => { if (server)
        void boot();
    else
        tracks.filter(v => v.audio.startsWith('blob:')).forEach(async (v) => { const b = await getBlob(v.id); if (b)
            setTracks(ts => ts.map(x => x.id === v.id ? { ...x, audio: URL.createObjectURL(b) } : x)); }); }, []);
    useEffect(() => { if ('serviceWorker' in navigator && location.port !== '5173') {
        navigator.serviceWorker.register('./sw.js' + (server ? '?server=1' : ''), { scope: './' }).catch(() => { });
    } const online = () => { if (server)
        void boot(); }; window.addEventListener('online', online); return () => window.removeEventListener('online', online); }, []);
    useEffect(()=>{if(config.accent&&/^#[a-fA-F0-9]{6}$/.test(config.accent))document.documentElement.style.setProperty('--accent',config.accent);document.title=(config.brand||'GenMiu')+' — Music Studio'},[config.brand,config.accent]);
    useEffect(() => { write('language', lang); document.documentElement.lang = lang; }, [lang]);
    useEffect(() => { if (!server)
        write('tracks', tracks); }, [tracks]);
    useEffect(() => { if (!server)
        write('playlists', playlists); }, [playlists]);
    useEffect(() => { write('draft', prompt); }, [prompt]);
    useEffect(() => { write('style', style); }, [style]);
    useEffect(() => { if (!server || !user)
        return; const timer = setInterval(async () => { try {
        const d = await api('jobs');
        setJobs(d.jobs);
        if (d.jobs.some((j: any) => j.status === 'ready')) {
            const b = await api('bootstrap');
            setTracks(b.tracks);
            setUser(b.user);
        }
    }
    catch { } }, 12000); return () => clearInterval(timer); }, [user?.id]);
    async function play(track: Track) {segment.current=null; if (!navigator.onLine) {
        const offline = await getBlob('offline-' + track.id) || await getBlob(track.id);
        if (offline) {
            track = { ...track, audio: URL.createObjectURL(offline) };
            if (current?.id === track.id && audio.current && !audio.current.src.startsWith('blob:')) {
                audio.current.src = track.audio;
                audio.current.load();
            }
        }
        else {
            notify(l("Questo brano non \u00E8 stato salvato sul dispositivo."));
            return;
        }
    } if (current?.id === track.id) {
        if (audio.current?.paused)
            void audio.current.play().catch(() => notify(l("Tocca di nuovo per avviare l\u2019audio.")));
        else
            audio.current?.pause();
    }
    else {
        wantPlay.current = true;
        setCurrent(track);
        setPlayerError('');
    } }
    useEffect(() => { if (!audio.current || !current)
        return; audio.current.src = audioSrc(current); audio.current.load(); setTime(0); if (wantPlay.current) {
        void audio.current.play().catch(() => { setPlaying(false); notify(l("La riproduzione richiede un tocco sul player.")); });
        wantPlay.current = false;
    } if ('mediaSession' in navigator) {
        navigator.mediaSession.metadata = new MediaMetadata({ title: current.title, artist: current.artist, album: 'GenMiu', artwork: [{ src: new URL(asset(current.cover), location.href).href, sizes: '640x900', type: 'image/webp' }] });
    } }, [current?.id]);
    useEffect(() => { if (audio.current)
        audio.current.volume = volume; }, [volume]);
    function next(dir = 1) { if (queue.length && dir === 1) {
        const n = queue[0];
        setQueue(queue.slice(1));
        wantPlay.current = true;
        setCurrent(n);
        return;
    } const list = tracks.filter(x => !x.archived && !x.deleted); if (!list.length)
        return; const i = list.findIndex(x => x.id === current?.id); const n = shuffle ? Math.floor(Math.random() * list.length) : (i + dir + list.length) % list.length; wantPlay.current = true; setCurrent(list[n]); }
    function playSegment(track:Track,start:number,end:number){segment.current={id:track.id,start,end};if(current?.id===track.id){seek(start);void audio.current?.play().catch(()=>notify('Audio unavailable'))}else{wantPlay.current=true;setCurrent(track)}}
    const seek = (n: number) => { if (audio.current && Number.isFinite(n)) {
        audio.current.currentTime = n;
        setTime(n);
    } };
    useEffect(() => { if (!('mediaSession' in navigator))
        return; navigator.mediaSession.setActionHandler('play', () => audio.current?.play()); navigator.mediaSession.setActionHandler('pause', () => audio.current?.pause()); navigator.mediaSession.setActionHandler('nexttrack', () => next()); navigator.mediaSession.setActionHandler('previoustrack', () => next(-1)); navigator.mediaSession.setActionHandler('seekto', d => seek(d.seekTime || 0)); }, [tracks, current, queue]);
    useEffect(() => { const f = (e: KeyboardEvent) => { if (['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON'].includes((e.target as HTMLElement).tagName) || modal)
        return; if (e.code === 'Space') {
        e.preventDefault();
        if (current)
            play(current);
    } }; window.addEventListener('keydown', f); return () => window.removeEventListener('keydown', f); }, [current, modal]);
    async function updateTrack(id: string, changes: Partial<Track> & {
        rights?: boolean;
    }) { try {
        if (server)
            await api('track.update', { id, ...changes });
        setTracks(ts => ts.map(v => v.id === id ? { ...v, ...changes } : v));
        if (current?.id === id)
            setCurrent(c => c ? { ...c, ...changes } : c);
    }
    catch (e) {
        notify((e as Error).message);
    } }
    async function importAudio(file: File) { if (!file)
        return; if (!/^(audio\/|video\/mp4)/.test(file.type)) {
        notify(l("Seleziona un file audio MP3, WAV, M4A o OGG."));
        return;
    } if (file.size > 20 * 1024 * 1024) {
        notify(l("Il limite di questa installazione \u00E8 20 MB."));
        return;
    } try {
        if (server) {
            const f = new FormData();
            f.append('file', file);
            const r = await fetch('./api.php?action=upload', { method: 'POST', body: f, headers: { 'X-CSRF-Token': sessionStorage.getItem('genmiu-csrf') || '' } });
            const d = await r.json();
            if (d.error)
                throw new Error(d.error);
            await boot();
            notify(t.saved);
            return;
        }
        const id = uid();
        await saveBlob(id, file);
        const url = URL.createObjectURL(file);
        const probe = new Audio(url);
        probe.onloadedmetadata = () => { setTracks(ts => [{ id, title: file.name.replace(/\.[^.]+$/, ''), artist: l("Il tuo audio"), style: 'Importato', cover: 'portrait-6.webp', audio: url, duration: Number.isFinite(probe.duration) ? probe.duration : 0, liked: false, created: new Date().toISOString(), visibility: 'private' }, ...ts]); notify(t.saved); navigate('library'); };
        probe.onerror = () => notify(l("Il browser non riesce a leggere questo formato."));
    }
    catch (e) {
        notify((e as Error).message);
    } }
    async function download(track: Track) { try {
        const r = await fetch(audioSrc(track));
        if (!r.ok)
            throw new Error();
        const b = await r.blob();
        const u = URL.createObjectURL(b);
        const a = document.createElement('a');
        a.href = u;
        a.download = track.title + '.' + (b.type.includes('wav') ? 'wav' : b.type.includes('mpeg') ? 'mp3' : b.type.includes('mp4') ? 'm4a' : 'audio');
        a.click();
        setTimeout(() => URL.revokeObjectURL(u), 1000);
    }
    catch {
        notify(l("Download non disponibile. Verifica la connessione."));
    } }
    async function offline(track: Track) { try {
        const r = await fetch(audioSrc(track));
        if (!r.ok)
            throw new Error();
        await saveBlob('offline-' + track.id, await r.blob());
        const ids = read<string[]>('offline', []);
        write('offline', [...new Set([...ids, track.id])]);
        notify(l("Brano salvato sul dispositivo."));
    }
    catch {
        notify(l("Salvataggio non riuscito: verifica connessione e spazio."));
    } }
    async function submit(operation: string, payload: any) { if (!server || !config.provider) {
        setModal({ type: 'provider' });
        return;
    } if (!user) {
        setModal({ type: 'auth' });
        return;
    } if (submitting.current)
        return; if (!navigator.onLine) {
        notify(l("La generazione richiede una connessione. La bozza \u00E8 conservata."));
        return;
    } submitting.current = true; setBusy(true); const signature = operation + JSON.stringify(payload); const previous = read<any>('pending-request', null); const key = previous?.signature === signature ? previous.key : uid(); write('pending-request', { signature, key }); try {
        const r = await api('job.create', { operation, payload, idempotencyKey: key });
        write('pending-request', null);
        setJobs(j => [r.job, ...j.filter(x => x.id !== r.job.id)]);
        setModal(null);
        notify(l("Richiesta salvata. Lo stato verr\u00E0 aggiornato dal server."));
    }
    catch (e) {
        notify((e as Error).message);
    }
    finally {
        submitting.current = false;
        setBusy(false);
    } }
    async function generate(e: React.FormEvent<HTMLFormElement>) { e.preventDefault(); if (!style&&!references.length) {
        notify(lang === 'it' ? 'Scegli almeno uno stile musicale.' : 'Choose a music style first.');
        return;
    } const fd = new FormData(e.currentTarget); const p: any = { customMode: advanced, instrumental, model, style, prompt, };if(!advanced){for(const [k,type] of [['imageUrls','image'],['videoUrls','video'],['audioUrls','audio']]){const urls=references.filter(r=>r.kind===type).map(r=>r.url);if(urls.length)p[k]=urls}} if (advanced) {
        p.title = fd.get('title');const identity=String(fd.get('identity')||'');if(identity.startsWith('persona:'))p.personaLocalId=identity.slice(8);if(identity.startsWith('voice:'))p.voiceLocalId=identity.slice(6);
        p.lyrics = instrumental ? '' : fd.get('lyrics');
        p.negativeTags = fd.get('negativeTags');
        p.duration = Number(fd.get('duration'));
        p.styleWeight = Number(fd.get('styleWeight'));
        p.variety = Number(fd.get('variety'));
        p.vocalGender = instrumental ? undefined : fd.get('vocalGender');
    } await submit('generate', p); }
    const value = { t, lang, setLang, route, navigate, tracks, setTracks, playlists, setPlaylists, user, setUser, config, setConfig, jobs, modal, setModal, notify, current, playing,time,seek,playSegment, play, updateTrack, download, offline, submit, busy, inputFile, server, boot, setPrompt, setStyle, installEvent, setInstallEvent };
    const nav = [['home', 'home', t.home], ['explore', 'explore', t.explore], ['library', 'library', t.library]];
    const navCreate = [['studio', 'studio', t.studio], ['tools', 'tools', t.tools], ['voices', 'voices', t.voices]];
    const live = tracks.filter(v => !v.deleted && !v.archived);
    const result = live.filter(x => (x.title + ' ' + x.style + ' ' + x.artist).toLowerCase().includes(search.toLowerCase()));
    function composer(compact = false) {
        return <form className={`composer ${advanced && !compact ? 'composer-expanded' : ''}`} onSubmit={generate}>
        <div className="composer-modes"><button type="button" className={!advanced ? 'active' : ''} onClick={() => setAdvanced(false)}>{t.simple}</button><button type="button" className={advanced ? 'active' : ''} onClick={() => { setAdvanced(true); if (compact)
            navigate('studio'); }}>{t.advanced}<Icon name="sparkles" size={12}/></button><span>{model}<span className="green-dot"/></span></div>
        <div className="prompt-wrap"><textarea aria-label={t.prompt} placeholder={t.placeholder} maxLength={advanced ? 5000 : 3000} value={prompt} onChange={e => setPrompt(e.target.value)} rows={compact ? 2 : 3}/><button type="button" className="inspire-button" onClick={() => { setPrompt(['Un viaggio notturno tra sintetizzatori analogici, un basso caldo e luci lontane. Atmosfera nostalgica, ritmo lento.', 'Un pianoforte intimo, texture ambient e pioggia sul vetro. Una melodia che sa di casa.', 'Un beat lo-fi morbido con accordi jazz, vinile e la calma di una domenica mattina.'][Math.floor(Math.random() * 3)]); setStyle('Ambient'); }} aria-label={t.surprise} title={t.surprise}><Icon name="sparkles" size={19}/></button></div>
        {!compact && advanced && <div className="advanced-fields"><Field label={lang==='en'?'Persona or verified voice':'Persona o voce verificata'}><select name="identity"><option value="">{lang==='en'?'No reference identity':'Nessuna identità di riferimento'}</option><optgroup label="Personae">{identities.personas.map((p:any)=><option key={p.id} value={'persona:'+p.id}>{p.name}</option>)}</optgroup><optgroup label={t.voices}>{identities.voices.map((v:any)=><option key={v.id} value={'voice:'+v.id}>{v.name}</option>)}</optgroup></select></Field><div className="form-grid"><Field label={t.title}><input name="title" maxLength={80} placeholder={l("Il nome della tua idea")}/></Field><Field label={t.model}><select value={model} onChange={e => setModel(e.target.value)}>{['V6', 'V6_WILD', 'V6_MINI', 'V5_5', 'V5', 'V4_5'].map(m => <option key={m}>{m}</option>)}</select></Field></div>{!instrumental && <Field label={t.lyrics}><textarea name="lyrics" maxLength={5000} rows={5} placeholder="[Verse]\nLe parole del tuo brano…"/></Field>}<Field label={lang === 'it' ? l("Elementi da escludere") : 'Exclude elements'}><input name="negativeTags" maxLength={1000} placeholder="Distortion, heavy drums…"/></Field><div className="form-grid"><Field label={`${t.duration}: ${trackDuration}s`}><input type="range" name="duration" min="10" max="360" value={trackDuration} onChange={e => setTrackDuration(+e.target.value)}/></Field><Field label="Style weight"><input type="range" name="styleWeight" min="0" max="1" step=".05" defaultValue=".65"/></Field><Field label="Variety"><select name="variety">{[0, 1, 2, 3, 4].map(v => <option key={v}>{v}</option>)}</select></Field>{!instrumental && <Field label="Vocal preference"><select name="vocalGender"><option value="">Automatic</option><option value="f">Female</option><option value="m">Male</option></select></Field>}</div></div>}
        {!advanced&&references.length>0&&<div className="reference-chips">{references.map(r=><span key={r.id}><Icon name={r.kind==='image'?'image':r.kind==='video'?'video':'audio'} size={13}/>{r.name}<button type="button" aria-label={t.delete} onClick={()=>setReferences(rs=>rs.filter(x=>x.id!==r.id))}><Icon name="close" size={12}/></button></span>)}</div>}<div className="composer-bottom"><div className="composer-options"><button type="button" className="chip icon-chip" onClick={() => setModal({type:'references'})} aria-label={lang==='en'?'Add references':'Aggiungi riferimenti'}><Icon name="plus" size={19}/></button><label className="style-select"><Icon name="sliders" size={14}/><select aria-label={t.style} value={style} onChange={e => setStyle(e.target.value)}><option value="">{t.style}</option>{styles.map(s => <option key={s}>{s}</option>)}</select><Icon name="chevronDown" size={12}/></label><label className="instrumental"><span>{t.instrumental}</span><input type="checkbox" checked={instrumental} onChange={e => setInstrumental(e.target.checked)}/><span className="switch"/></label></div><button className="primary create-button" disabled={busy || !navigator.onLine} type="submit"><Icon name="sparkles" size={16}/>{busy ? t.generating : t.generate}</button></div><div className="composer-footnote"><span><Icon name="lock" size={11}/>{t.private} {lang === 'it' ? l("per impostazione iniziale") : 'by default'}</span><span>{config.provider ? (config.operationPrices?.find((p:any)=>p.operation==='generate'&&p.model===model&&p.enabled)?`${config.operationPrices.find((p:any)=>p.operation==='generate'&&p.model===model).cost} ${t.credits} · ${model}`:t.coming) : lang === 'it' ? l("Provider da configurare") : 'Provider setup required'}</span></div></form>;
    }
    return <AppContext.Provider value={value}><div className={`app-shell ${route === 'landing' ? 'landing-shell' : ''}`}>
    <aside className="sidebar"><button className="brand" onClick={() => navigate('home')} aria-label="GenMiu Home"><img src={config.logo||asset('logo.svg')} alt=""/><span>{config.brand||'GenMiu'}<span className="brand-dot">.</span></span></button><div className="workspace-label"><span className="workspace-avatar"><Icon name="audio" size={15}/></span><span>{l("Il tuo spazio creativo")}<small>PERSONAL WORKSPACE</small></span><Icon name="chevronDown" size={14}/></div><nav aria-label="Main navigation"><div className="nav-label">{t.discover}</div>{nav.map(([r, ic, n]) => <button key={r} onClick={() => navigate(r)} className={route === r ? 'active' : ''}><Icon name={ic} size={19}/><span>{n}</span>{route === r && <i />}</button>)}<div className="nav-label create-label">{t.create}</div>{navCreate.map(([r, ic, n]) => <button key={r} onClick={() => navigate(r)} className={route === r ? 'active' : ''}><Icon name={ic} size={19}/><span>{n}</span>{r === 'studio' && <small>AI</small>}</button>)}</nav><div className="sidebar-bottom"><button className="sidebar-link" onClick={() => navigate('settings')}><Icon name="app" size={18}/>{t.install}<Icon name="arrow" size={13}/></button><button className="sidebar-link" onClick={() => setModal({ type: 'help' })}><Icon name="help" size={18}/>{t.docs}</button><div className="upgrade-card"><span className="upgrade-spark"><Icon name="sparkles" size={21}/></span><h4>{l("Ogni idea merita")}<br />{l("di farsi sentire.")}</h4><p>{l("Il tuo universo musicale,")}<br />{l("senza confini creativi.")}</p><button onClick={() => navigate('landing')}>{l("Scopri GenMiu")}<Icon name="arrow" size={14}/></button></div><button className="sidebar-link settings-link" onClick={() => navigate('settings')}><Icon name="settings" size={18}/>{t.settings}<span>⌘ ,</span></button></div><div className="sidebar-version">GENMIU STUDIO <span>v0.9</span></div></aside>
    <div className="main-shell"><header className="topbar"><div className="mobile-brand"><img src={config.logo||asset('logo.svg')} alt=""/>{config.brand||'GenMiu'}<span>.</span></div><label className="global-search"><Icon name="search" size={17}/><input value={search} onChange={e => setSearch(e.target.value)} placeholder={t.search} aria-label={t.search}/>{search ? <button className="icon-button" onClick={() => setSearch('')} aria-label={t.clear}><Icon name="close" size={14}/></button> : <kbd>/</kbd>}</label><div className="topbar-right"><button className="demo-badge" onClick={() => setModal({ type: 'provider' })}><span />{config.demo ? t.demo : config.provider ? 'Studio online' : t.coming}</button><button className="credit-button" onClick={() => navigate('billing')}><Icon name="credits" size={16}/><strong>{user?.credits || 0}</strong><span>{t.credits}</span><Icon name="plus" size={13}/></button><div className="profile-anchor"><button className="avatar" onClick={() => setProfile(!profile)} aria-label={t.account} aria-expanded={profile}><span>{user?.name?.slice(0, 1) || 'G'}</span></button>{profile && <div className="profile-menu"><strong>{user?.name || 'GenMiu'}</strong><small>{user?.email || t.demo}</small><button onClick={() => navigate('settings')}><Icon name="settings" size={16}/>{t.settings}</button><button onClick={() => navigate('billing')}><Icon name="credits" size={16}/>{t.plans}</button>{(!server || user?.role === 'admin') && <button onClick={() => navigate('admin')}><Icon name="admin" size={16}/>{t.admin}</button>}<button onClick={() => { setProfile(false); setModal({ type: server && user ? 'logout' : 'auth' }); }}><Icon name="user" size={16}/>{server && user ? t.logout : t.login}</button></div>}</div></div></header>
    <main id="main-content" className={['home', 'studio'].includes(route) && !search ? 'main-content hero-page' : 'main-content'}>
        {search ? <div className="page standard-page"><div className="page-heading"><div><span className="eyebrow">SEARCH</span><h1>“{search}”</h1><p>{result.length} {t.tracks.toLowerCase()}</p></div></div>{result.length ? <TrackRows tracks={result}/> : <Empty title={t.noResults} sub={l("Prova un altro titolo o stile.")}/>}</div> : <>
            {route === 'home' && <><section className="hero"><div className="hero-orb"><img src={asset('orb.svg')} alt=""/><span /></div><div className="hero-kicker"><span />YOUR NEXT SOUND STARTS HERE</div><h1>{t.hero}<span>.</span></h1><p>{t.subhero}</p>{composer(true)}<div className="suggested-styles"><span>{l("Un po\u2019 di ispirazione")}</span>{['Dreamy lo-fi', 'Cinematic piano', 'Midnight house'].map((s, i) => <button key={s} onClick={() => { setPrompt(s); setStyle(['Lo-fi', 'Cinematic', 'House'][i]); }}>{s}<Icon name="arrow" size={11}/></button>)}</div></section>
            <section className="section tool-section"><div className="section-heading"><h2>{t.startTool}</h2><button onClick={() => navigate('tools')}>{t.allTools}<Icon name="chevron" size={15}/></button></div><div className="tool-carousel">{tools.slice(0, 5).map((v, i) => <button className={`tool-card ${v.color}`} key={v.id} onClick={() => setModal({ type: 'tool', tool: v })}><img src={asset(v.cover!)} alt=""/><div className="tool-overlay"/><span className="tool-tag"><Icon name={v.icon} size={11}/>{l(v.tag)}</span><span className="tool-text"><strong>{l(v.title)}</strong><small>{l(v.description)}</small></span><span className="tool-arrow"><Icon name="arrow" size={17}/></span></button>)}</div></section>
            <section className="section recent-section"><div className="section-heading"><div><h2>{t.recent}</h2><p>{l("La tua creativit\u00E0 prende forma.")}</p></div><button onClick={() => navigate('library')}>{t.seeAll}<Icon name="chevron" size={15}/></button></div>{live.length ? <TrackRows tracks={live.slice(0, 3)}/> : <Empty action/>}</section>
            <section className="section discovery-strip"><div><span className="eyebrow">MADE FOR YOUR NEXT IDEA</span><h2>{l("C\u2019\u00E8 un universo")}<br />{l("di suoni da scoprire.")}</h2><button onClick={() => navigate('explore')}>{t.explore}<Icon name="arrow" size={16}/></button></div><img src={asset('portrait-6.webp')} alt={l("Paesaggio notturno viola")}/></section></>}
        {route === 'studio' && <div className="page studio-page"><div className="page-heading"><div><span className="eyebrow">YOUR PERSONAL MUSIC LAB</span><h1>{t.studio}<span>.</span></h1><p>{t.made}</p></div><button className="secondary" onClick={() => inputFile.current?.click()}><Icon name="upload" size={16}/>{t.import}</button></div>{composer()}<div className="style-palette"><h3>{t.style}</h3><div className="chips">{styles.map(s => <button key={s} className={`chip ${s === style ? 'selected' : ''}`} onClick={() => setStyle(s)}>{s}</button>)}</div></div><div className="section-heading"><h2>{t.jobs}</h2><span className="subtle">{jobs.length}</span></div>{jobs.length ? jobs.map(j => <div className="job-row" key={j.id}><span className={`job-state ${j.status}`}><Icon name={j.status === 'ready' ? 'check' : 'clock'} size={20}/></span><div><strong>{j.title || j.operation}</strong><small>{j.status} · {j.created_at}</small></div><button className="secondary" onClick={() => setModal({ type: 'job', job: j })}>{t.view}</button></div>) : <div className="quiet-empty"><Icon name="audio" size={22}/>{t.noJobs}</div>}</div>}
        {route === 'library' && <Library />}{route === 'tools' && <Toolbox />}{route === 'voices' && <Voices />}{route === 'billing' && <Billing />}{route === 'settings' && <Settings />}{route === 'admin' && <Admin />}{route === 'landing' && <Landing />}
        {route === 'explore' && <div className="page explore-page"><div className="explore-banner"><div><span className="eyebrow">GENMIU ORIGINALS</span><h1>{l("Segui il suono.")}<br />{l("Trova l\u2019ispirazione")}<span>.</span></h1><p>{l("Una selezione originale di paesaggi sonori.")}<br />{l("Ascolta, immagina, crea qualcosa di tuo.")}</p><button className="primary" onClick={() => { const tr = tracks.find(x => x.visibility === 'public'); if (tr)
            play(tr); }}><Icon name="play" size={16} fill="currentColor"/>{l("Ascolta la selezione")}</button></div><div className="explore-art"><img src={asset('portrait-1.webp')} alt={l("Silhouette immersa in luce viola")}/><img src={asset('portrait-2.webp')} alt={l("Silhouette nella luce blu")}/></div></div><div className="section-heading"><div><h2>{t.inspiration}</h2><p>{t.picked}</p></div><span className="pill">Original demo recordings</span></div><TrackCards tracks={live.filter(v => v.visibility === 'public')}/><div className="editorial-note"><Icon name="info" size={18}/><p>{l("Questi esempi strumentali sono composizioni sintetizzate originali incluse nella demo. Non sono risultati live di Suno API.")}</p></div></div>}
        {route.startsWith('listen/')&&(()=>{const tr=tracks.find(x=>x.id===route.slice(7));return tr?<div className="page listen-page"><Cover track={tr}/><h1>{tr.title}</h1><p>{tr.artist} · {tr.style}</p><button className="primary" onClick={()=>play(tr)}><Icon name="play" size={18}/>{t.play}</button></div>:<Empty title="404"/>})()}
            {!route.startsWith('listen/')&&!['home', 'studio', 'library', 'tools', 'voices', 'billing', 'settings', 'admin', 'explore', 'landing'].includes(route) && <Empty title="404" sub={l("Questa pagina non esiste.")} action/>}</>}
    <footer className="content-footer"><span>© 2026 GenMiu. Made for the music in you.</span><div><button onClick={() => setModal({ type: 'legal', name: l("Termini") })}>{l("Termini")}</button><button onClick={() => setModal({ type: 'legal', name: 'Privacy' })}>Privacy</button><button onClick={() => setLang(lang === 'it' ? 'en' : 'it')}><Icon name="globe" size={12}/>{lang.toUpperCase()}</button></div></footer></main></div>
    {panel && <aside className="queue-panel"><div className="section-heading"><h2>{panel === 'queue' ? t.queue : t.lyrics}</h2><button className="icon-button" onClick={() => setPanel(null)} aria-label={t.close}><Icon name="close"/></button></div>{panel === 'queue' ? <><p className="subtle">{l("In riproduzione")}</p>{current && <TrackRows tracks={[current]}/>}<p className="subtle">{l("A seguire \u00B7")}{queue.length || live.length}</p><TrackRows tracks={queue.length ? queue : live}/></> : <TimedLyrics/>}</aside>}
    {current && <div className={`player ${full ? 'player-full' : ''}`}><div className="full-top"><button className="icon-button" onClick={() => setFull(false)} aria-label={t.close}><Icon name="collapse"/></button><span>NOW PLAYING</span><button className="icon-button" onClick={() => setModal({ type: 'track', track: current })} aria-label={t.view}><Icon name="more"/></button></div><div className="player-track"><button className="player-cover" onClick={() => setFull(!full)} aria-label="Expand player"><Cover track={current}/></button><div><strong onClick={() => setFull(true)}>{current.title}</strong><small>{playerError || current.artist}</small></div><button className={`icon-button ${current.liked ? 'liked' : ''}`} aria-label={t.liked} aria-pressed={current.liked} onClick={() => updateTrack(current.id, { liked: !current.liked })}><Icon name="heart" size={18} fill={current.liked ? 'currentColor' : 'none'}/></button></div><div className="player-center"><div className="play-controls"><button className={`icon-button shuffle ${shuffle ? 'on' : ''}`} onClick={() => setShuffle(!shuffle)} aria-label="Shuffle" aria-pressed={shuffle}><Icon name="shuffle" size={16}/></button><button className="icon-button prev" onClick={() => next(-1)} aria-label="Previous"><Icon name="prev" size={18} fill="currentColor"/></button><button className="main-play" onClick={() => play(current)} aria-label={playing ? t.pause : t.play}><Icon name={buffering ? 'refresh' : playing ? 'pause' : 'play'} size={19} fill={buffering ? 'none' : 'currentColor'} className={buffering ? 'spinning' : ''}/></button><button className="icon-button next" onClick={() => next()} aria-label="Next"><Icon name="next" size={18} fill="currentColor"/></button><button className={`icon-button repeat ${repeat ? 'on' : ''}`} onClick={() => setRepeat((repeat + 1) % 3)} aria-label={`Repeat ${['off', 'track', 'queue'][repeat]}`}><Icon name={repeat === 1 ? 'repeat1' : 'repeat'} size={16}/></button></div><div className="timeline"><span>{fmt(time)}</span><input aria-label="Playback position" type="range" min="0" max={duration || current.duration || 1} value={Math.min(time, duration || current.duration || 1)} step=".1" style={{ '--progress': `${time / (duration || 1) * 100}%` } as React.CSSProperties} onChange={e => seek(+e.target.value)}/><span>{fmt(duration || current.duration)}</span></div></div><div className="player-extras"><button className={`icon-button ${panel === 'lyrics' ? 'on' : ''}`} onClick={() => setPanel(panel === 'lyrics' ? null : 'lyrics')} aria-label={t.lyrics}><Icon name="mic" size={17}/></button><button className={`icon-button ${panel === 'queue' ? 'on' : ''}`} onClick={() => setPanel(panel === 'queue' ? null : 'queue')} aria-label={t.queue}><Icon name="queue" size={18}/></button><span className="volume-control"><button className="icon-button" onClick={() => setVolume(volume ? 0 : .7)} aria-label="Mute"><Icon name={volume ? 'volume' : 'mute'} size={18}/></button><input aria-label="Volume" type="range" min="0" max="1" step=".01" value={volume} onChange={e => setVolume(+e.target.value)}/></span><button className="icon-button" onClick={() => setFull(!full)} aria-label="Expand player"><Icon name="expand" size={16}/></button></div><div className="full-lyrics"><button onClick={() => setPanel(panel === 'lyrics' ? null : 'lyrics')}><Icon name="text" size={18}/>{t.lyrics}</button><button onClick={() => setPanel(panel === 'queue' ? null : 'queue')}><Icon name="queue" size={18}/>{t.queue}</button></div></div>}
    <nav className="bottom-nav" aria-label="Mobile navigation">{[['home', 'home', t.home], ['explore', 'explore', t.explore], ['studio', 'sparkles', t.create], ['library', 'library', t.library]].map(([r, ic, n]) => <button key={r} className={route === r ? 'active' : ''} onClick={() => navigate(r)}><Icon name={ic} size={21}/><span>{n}</span></button>)}</nav>
    <audio ref={audio} preload="metadata" onPlay={() => setPlaying(true)} onPause={() => setPlaying(false)} onLoadedMetadata={()=>{if(segment.current?.id===current?.id&&audio.current)audio.current.currentTime=segment.current!.start}} onTimeUpdate={() => {const n=audio.current?.currentTime||0;setTime(n);if(segment.current&&n>=segment.current.end){audio.current?.pause();segment.current=null}}} onDurationChange={() => setDuration(Number.isFinite(audio.current?.duration) ? audio.current!.duration : 0)} onWaiting={() => setBuffering(true)} onCanPlay={() => setBuffering(false)} onError={() => { setBuffering(false); setPlayerError(l("Audio non disponibile")); setPlaying(false); }} onEnded={() => { if (repeat === 1) {
        seek(0);
        void audio.current?.play();
    }
    else if (repeat === 2 || queue.length || live.findIndex(x => x.id === current?.id) < live.length - 1)
        next();
    else
        setPlaying(false); }}/>
    <input ref={inputFile} type="file" accept="audio/*" hidden onChange={e => { if (e.target.files?.[0])
        void importAudio(e.target.files[0]); e.target.value = ''; }}/>
    {toast && <div className="toast" role="status"><Icon name="check" size={17}/>{toast}<button className="icon-button" onClick={() => setToast('')} aria-label={t.close}><Icon name="close" size={14}/></button></div>}
        {modal && <Dialog title={modal.type === 'references'?(lang==='en'?'Add references':'Aggiungi riferimenti'):modal.type === 'tool' ? l(modal.tool.title) : modal.type === 'track' ? modal.track.title : modal.type === 'playlist' ? t.newPlaylist : modal.type === 'auth' ? t.login : modal.type === 'provider' ? l("Il tuo studio, pronto a connettersi") : modal.type === 'legal' ? modal.name : modal.type === 'help' ? t.docs : modal.type === 'job' ? t.generating : 'GenMiu'} onClose={() => setModal(null)} wide={modal.type === 'tool'}>
        {modal.type==='references'&&<ReferenceUpload onAdd={rs=>setReferences(prev=>[...prev,...rs])}/>}
        {modal.type === 'tool' && <ToolForm tool={modal.tool}/>}
        {modal.type === 'provider' && <div className="provider-dialog"><img src={asset('orb.svg')} alt=""/><p>{t.demoText}</p><div className="notice"><Icon name="info" size={19}/><span>{server ? l("Inserisci la chiave API nell\u2019amministrazione e attiva il worker. I crediti sono gestiti sul server.") : l("Questa anteprima usa il browser per libreria e playlist. Il pacchetto PHP include il collegamento server al provider; le chiavi non vanno inserite qui.")}</span></div><button className="primary full-width" onClick={() => { setModal(null); navigate('admin'); }}><Icon name="settings" size={17}/>{t.configure}</button><button className="text-button" onClick={() => { setModal({ type: 'help' }); }}>{l("Apri la guida di installazione")}<Icon name="arrow" size={14}/></button></div>}
        {modal.type === 'track' && <div className="track-actions"><div className="track-dialog-summary"><Cover track={modal.track}/><div><strong>{modal.track.title}</strong><p>{modal.track.style}</p><small>{modal.track.demo ? 'Original demo audio' : t[modal.track.visibility as 'private']} · {fmt(modal.track.duration)}</small></div></div><Field label={t.title}><input defaultValue={modal.track.title} onBlur={e => { if (e.target.value.trim())
            void updateTrack(modal.track.id, { title: e.target.value.trim() }); }}/></Field><div className="action-grid"><button onClick={() => { play(modal.track); setModal(null); }}><Icon name="play"/>{t.play}</button><button onClick={() => { setQueue(q => [...q, modal.track]); notify(l("Aggiunto alla coda")); }}><Icon name="queue"/>{l("Aggiungi alla coda")}</button><button onClick={() => download(modal.track)}><Icon name="download"/>{t.download}</button><button onClick={() => offline(modal.track)}><Icon name="offline"/>{t.offline}</button><button onClick={async()=>{if(modal.track.visibility==='private'){notify(lang==='en'?'Publish the track or use an unlisted link first.':'Pubblica il brano o scegli un link non indicizzato prima di condividerlo.');return}if(!server&&!modal.track.demo){notify(lang==='en'?'Local imports can be shared through the PHP installation.':'Le importazioni locali si condividono tramite l’installazione PHP.');return}const url=server?config.baseUrl+'/track/'+modal.track.slug:location.origin+location.pathname+'#/listen/'+modal.track.id;try{await navigator.clipboard.writeText(url);notify(t.copied)}catch{notify(url)}}}><Icon name="external"/>{t.share}</button></div><Field label={l("Elabora questo brano")}><select defaultValue="" onChange={e => { const tool = tools.find(x => x.id === e.target.value); if (tool)
            setModal({ type: 'tool', tool, sourceId: modal.track.id }); }}><option value="">{l("Scegli uno strumento\u2026")}</option>{tools.filter(x => x.fields.includes('track')).map(v => <option key={v.id} value={v.id}>{l(v.title)}</option>)}</select></Field><Field label={l("Progetto")}><select defaultValue={modal.track.project || ''} onChange={e => { if (server) {
            const pr = read<any[]>('server-projects', []).find(v => v.name === e.target.value);
            if (pr)
                void updateTrack(modal.track.id, { project: pr.name, ...({ project_id: pr.id } as any) });
        }
        else
            void updateTrack(modal.track.id, { project: e.target.value }); }}><option value="">{l("Nessun progetto")}</option>{[...new Set<string>([...read<string[]>('projects', []), ...tracks.map(x => x.project || '').filter(Boolean)])].map(p => <option key={p}>{p}</option>)}</select></Field><Field label={t.playlists}><select defaultValue="" onChange={async (e) => { const id = e.target.value; if (!id)
            return; const p = playlists.find(x => x.id === id)!; const next = [...new Set([...p.tracks, modal.track.id])]; if (server)
            try {
                await api('playlist.save', { ...p, tracks: next });
            }
            catch (e) {
                notify((e as Error).message);
                return;
            } setPlaylists(ps => ps.map(x => x.id === id ? { ...x, tracks: next } : x)); notify(t.saved); }}><option value="">{t.add}…</option>{playlists.map(p => <option key={p.id} value={p.id}>{p.name}</option>)}</select></Field><Field label={l("Visibilit\u00E0")}><select defaultValue={modal.track.visibility} onChange={e => setModal({ ...modal, visibility: e.target.value })}><option value="private">{t.private}</option><option value="unlisted">{t.unlisted}</option><option value="public">{t.public}</option></select></Field>{modal.visibility && modal.visibility !== 'private' && <label className="checkbox-label"><input type="checkbox" checked={!!modal.rights} onChange={e => setModal({ ...modal, rights: e.target.checked })}/>{t.rights}</label>}{modal.visibility && <button className="secondary full-width" disabled={modal.visibility !== 'private' && !modal.rights} onClick={() => { void updateTrack(modal.track.id, { visibility: modal.visibility, rights: modal.rights }); notify(t.saved); setModal(null); }}>{t.save}</button>}<div className="dialog-bottom"><button className="text-button" onClick={() => { void updateTrack(modal.track.id, { archived: !modal.track.archived }); setModal(null); }}><Icon name="archive" size={16}/>{modal.track.archived ? t.restore : t.archive}</button><button className="text-button danger" onClick={() => setModal({ ...modal, confirmDelete: true })}><Icon name="trash" size={16}/>{t.delete}</button></div>{modal.confirmDelete && <div className="notice"><span>{l("Il brano verr\u00E0 spostato nel cestino e potr\u00E0 essere ripristinato.")}</span><button className="danger-button" onClick={() => { void updateTrack(modal.track.id, { deleted: true }); setModal(null); }}>{l("Conferma")}</button></div>}</div>}
        {modal.type === 'playlist' && <form className="dialog-form" onSubmit={async (e) => { e.preventDefault(); const f = new FormData(e.currentTarget); const p = { id: uid(), name: String(f.get('name')), tracks: [] }; try {
            if (server)
                await api('playlist.save', p);
            setPlaylists(ps => [...ps, p]);
            setModal(null);
            notify(t.saved);
        }
        catch (e) {
            notify((e as Error).message);
        } }}><Field label={l("Nome")}><input name="name" required maxLength={80} placeholder={l("La tua colonna sonora")}/></Field><button className="primary full-width">{t.save}</button></form>}
        {modal.type === 'auth' && <form className="dialog-form" onSubmit={async (e) => { e.preventDefault(); if (!server) {
            notify(l("Accesso disponibile nell\u2019installazione PHP."));
            return;
        } try {
            const f = Object.fromEntries(new FormData(e.currentTarget));
            const r = await api(modal.register ? 'register' : modal.reset ? 'password.request' : 'login', f);
            if (modal.reset) {
                notify(r.message);
                setModal(null);
            }
            else {
                await boot();
                setModal(null);
            }
        }
        catch (e) {
            notify((e as Error).message);
        } }}>{modal.register && <Field label={l("Nome")}><input name="name" required autoComplete="name"/></Field>}<Field label="Email"><input type="email" name="email" required autoComplete="email"/></Field>{!modal.reset && <Field label="Password"><input type="password" name="password" required minLength={10} autoComplete={modal.register ? 'new-password' : 'current-password'}/></Field>}<button className="primary full-width">{modal.register ? l("Crea account") : modal.reset ? l("Invia link") : t.login}</button><button type="button" className="text-button" onClick={() => setModal({ type: 'auth', register: !modal.register })}>{modal.register ? l("Hai gi\u00E0 un account? Accedi") : l("Crea il tuo account")}</button><button type="button" className="text-button" onClick={() => setModal({ type: 'auth', reset: true })}>{l("Password dimenticata?")}</button>{!server && <p className="subtle">{l("Accesso e registrazione richiedono il backend PHP.")}</p>}</form>}
        {modal.type === 'logout' && <div className="dialog-form"><p>{l("Uscire e rimuovere i dati offline da questo dispositivo?")}</p><button className="primary" onClick={async () => { await api('logout', {}); await clearBlobs();sessionStorage.clear(); await boot();setReferences([]);setIdentities({personas:[],voices:[]});setPrompt('');setQueue([]);setPanel(null); audio.current?.pause(); setCurrent(null); setModal(null); Object.keys(localStorage).filter(k => k.startsWith('genmiu-')&&k!=='genmiu-language').forEach(k => localStorage.removeItem(k)); navigator.serviceWorker?.controller?.postMessage('CLEAR_PRIVATE'); write('offline', []); }}>{t.logout}</button></div>}
        {modal.type === 'help' && <div className="help-dialog"><p>{l("Tutto quello che serve per dare vita al tuo studio.")}</p>{[[l("Installazione"), l("Database, account amministratore e configurazione iniziale.")], [l("Provider e strumenti"), l("Chiavi, crediti, modelli e lavorazioni asincrone.")], [l("Libreria e ascolto"), l("Importa audio, crea playlist e salva brani offline.")], [l("PWA e dispositivi"), l("Installa GenMiu dal menu del browser, quando supportato.")]].map(([a, b]) => <div key={a}><Icon name="check" size={18}/><span><strong>{a}</strong><small>{b}</small></span></div>)}<a className="primary full-width" href="./docs/index.html" target="_blank" rel="noreferrer">English documentation<Icon name="external" size={16}/></a></div>}
        {modal.type === 'legal' && <div className="legal-copy"><p>{l("GenMiu \u00E8 uno studio musicale indipendente che pu\u00F2 utilizzare il provider esterno sunoapi.org. Non \u00E8 un prodotto ufficiale Suno.")}</p><h3>{modal.name === 'Privacy' ? l("I tuoi dati") : l("Uso dei contenuti")}</h3><p>{l("Bozze e sorgenti sono private per impostazione iniziale. Per elaborare una sorgente, il backend dell\u2019installazione invia i dati necessari al provider configurato. Condividi soltanto contenuti per cui disponi dei diritti necessari.")}</p><p>{l("In questa demo, preferiti, playlist e importazioni restano nel tuo browser. Puoi rimuoverli dalle impostazioni. Nessun pagamento o invio al provider viene eseguito.")}</p><p>{l("Il proprietario dell\u2019installazione deve completare contatti, tempi di conservazione, condizioni commerciali e informativa applicabile prima di aprire il servizio al pubblico.")}</p></div>}
        {modal.type === 'job' && <div className="dialog-form"><div className="notice"><Icon name="clock"/><span>{modal.job.status}</span></div><p>{modal.job.error_message || l("Il server conserva la richiesta. Non ripeterla in caso di attesa.")}</p><small>ID: {modal.job.id}</small>{modal.job.midiData&&<MidiPreview data={modal.job.midiData}/>}{(modal.job.outputs || []).filter((o: any) => o.url).map((o: any) => <a className="secondary" href={o.url + '&download=1'} key={o.id} download><Icon name="download" size={16}/>{o.kind} · {o.mime}</a>)}{(modal.job.lyrics || []).map((lyric: any) => <div className="job-lyrics" key={lyric.id}><h3>{lyric.title}</h3><textarea defaultValue={lyric.body} onChange={e=>setModal({...modal,job:{...modal.job,lyrics:modal.job.lyrics.map((v:any)=>v.id===lyric.id?{...v,body:e.target.value}:v)}})} rows={6} onBlur={async (e) => { await api('lyrics.save', { id: lyric.id, body: e.target.value }); notify(t.saved); }}/><button className="secondary" onClick={() => { setPrompt(lyric.body); setModal(null); navigate('studio'); }}>{l("Porta al composer")}</button></div>)}{modal.job.status === 'queued' && <button className="secondary" onClick={async () => { try {
            await api('job.cancel', { id: modal.job.id });
            setModal(null);
            await boot();
        }
        catch (e) {
            notify((e as Error).message);
        } }}>{l("Annulla prima dell\u2019invio")}</button>}<button className="secondary" onClick={async () => { try {
            const d = await api('job.reconcile', { id: modal.job.id });
            setModal({ ...modal, job: d.job });
            await boot();
        }
        catch (e) {
            notify((e as Error).message);
        } }}>{l("Verifica lo stato")}</button></div>}
        </Dialog>}
    </div></AppContext.Provider>;
}
createRoot(document.getElementById('root')!).render(<React.StrictMode><App /></React.StrictMode>);
