BrowsePage-CCSAcHrC.js 31 KB

1
  1. import{r as a,j as e,h as Et}from"./react-DnK_pEU5.js";import{R as At,P as Rt,C as Ye,c as ae,a as It,X as Ft,b as Tt,T as Je,O as Ge,D as et,u as $t,i as Dt,d as j,g as ye,t as m,s as Mt,f as Ue,B as w,I as Ot,e as Bt,h as zt}from"./index-LUV-rsLw.js";import{u as Lt}from"./useBackendConnection-DKvfYjhQ.js";import{u as qt,C as Ht}from"./ConnectionBanner-BwHQi9kR.js";import{L as ve}from"./label-CyGXWE6X.js";import{S as Ve}from"./slider-C6r01RE5.js";import{S as Ke,a as We,b as Xe,c as Ze,d as O}from"./select-B_ypzzK2.js";import{p as Qe}from"./types-DxHZB39T.js";import"./dnd-Bgdma8TD.js";import"./motion-8nKk6_RD.js";import"./index-CLGWHFf2.js";const Ut=At,Vt=Rt,tt=a.forwardRef(({className:c,...h},b)=>e.jsx(Ge,{className:ae("fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",c),...h,ref:b}));tt.displayName=Ge.displayName;const Kt=Tt("fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",{variants:{side:{top:"inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",bottom:"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",left:"inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",right:"inset-y-0 right-0 h-full w-full border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-md"}},defaultVariants:{side:"right"}}),st=a.forwardRef(({side:c="right",className:h,children:b,...y},k)=>e.jsxs(Vt,{children:[e.jsx(tt,{}),e.jsxs(Ye,{ref:k,className:ae(Kt({side:c}),h),...y,children:[b,e.jsxs(It,{className:"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary",children:[e.jsx(Ft,{className:"h-4 w-4"}),e.jsx("span",{className:"sr-only",children:"Close"})]})]})]}));st.displayName=Ye.displayName;const at=({className:c,...h})=>e.jsx("div",{className:ae("flex flex-col space-y-2 text-center sm:text-left",c),...h});at.displayName="SheetHeader";const rt=a.forwardRef(({className:c,...h},b)=>e.jsx(Je,{ref:b,className:ae("text-lg font-semibold text-foreground",c),...h}));rt.displayName=Je.displayName;const Wt=a.forwardRef(({className:c,...h},b)=>e.jsx(et,{ref:b,className:ae("text-sm text-muted-foreground",c),...h}));Wt.displayName=et.displayName;const nt=a.createContext(null),je=60;function ls(){const{isPlayOnlyActive:c}=Et()||{},[h,b]=a.useState([]),[y,k]=a.useState({}),[B,re]=a.useState(!0),[N,W]=a.useState(""),[P,ne]=a.useState("all"),[I,z]=a.useState("name"),[_,u]=a.useState(!0),[f,E]=a.useState(je),le=a.useRef(null),[o,X]=a.useState(null),[lt,F]=a.useState(!1),[L,it]=a.useState(()=>localStorage.getItem("preExecution")||"adaptive"),[be,we]=a.useState(!1),[Z,me]=a.useState(null),Ne=qt(),ie=$t(t=>!!t.status?.playlist),[ot,Ce]=a.useState(!1),[v,ke]=a.useState([]),[ct,Pe]=a.useState(!1),[q,$]=a.useState(!1),[oe,dt]=a.useState(1),[Se,H]=a.useState(0),[ce,_e]=a.useState(null),[T,ut]=a.useState({}),A=a.useRef(null),U=a.useRef(null),C=a.useRef(0),Q=a.useRef(new Set),Y=a.useRef(null),de=a.useRef(null),Ee=a.useRef(y);a.useEffect(()=>{Ee.current=y},[y]);const[he,Ae]=a.useState(!1),[mt,fe]=a.useState(0),[ht,Re]=a.useState(!1),[R,pe]=a.useState(new Set),J=a.useRef(null),[xe,Ie]=a.useState(!1),G=a.useRef(null),ft=t=>{G.current={x:t.touches[0].clientX,y:t.touches[0].clientY}},pt=t=>{if(!G.current)return;const s=t.changedTouches[0].clientX-G.current.x,r=t.changedTouches[0].clientY-G.current.y;(s>80&&s>Math.abs(r)||r>80&&r>Math.abs(s))&&F(!1),G.current=null};a.useEffect(()=>{const t=()=>{F(!1)};return window.addEventListener("playback-started",t),()=>window.removeEventListener("playback-started",t)},[]),a.useEffect(()=>{localStorage.setItem("preExecution",L)},[L]),a.useEffect(()=>(Dt().then(()=>{ee()}).catch(()=>{ee()}),Fe(),()=>{Y.current&&clearTimeout(Y.current),de.current&&de.current.abort(),Q.current.clear()}),[]),Lt(()=>{ee(),Fe()});const Fe=async()=>{try{const t=await j.get("/get_playlist?name=Favorites");pe(new Set(t.files||[]))}catch{}},ge=async(t,s)=>{s.stopPropagation();const r=R.has(t),n=new Set(R);try{if(r)n.delete(t),await j.post("/modify_playlist",{playlist_name:"Favorites",files:Array.from(n)}),pe(n),m.success("Removed from favorites");else{n.add(t);try{await j.get("/get_playlist?name=Favorites"),await j.post("/add_to_playlist",{playlist_name:"Favorites",pattern:t})}catch{await j.post("/create_playlist",{playlist_name:"Favorites",files:[t]})}pe(n),m.success("Added to favorites")}}catch{m.error("Failed to update favorites")}},ee=async()=>{re(!0);try{const[t,s]=await Promise.all([j.get("/list_theta_rho_files_with_metadata"),j.get("/api/pattern_history_all")]);if(b(t),ut(s),t.length>0){const r=[...t].sort((x,g)=>x.name.localeCompare(g.name)),n=t.map(x=>x.path),l=r.slice(0,30).map(x=>x.path),i=await ye(l);if(i.size>0){const x={};i.forEach((g,M)=>{x[M]=g}),k(x)}const d=l.filter(x=>!i.has(x));d.length>0&&Te(d);const p=await ye(n);Re(p.size===n.length)}}catch(t){console.error("Error fetching patterns:",t),m.error("Failed to load patterns")}finally{re(!1)}},Te=async t=>{de.current=new AbortController;const r=de.current.signal;try{const n=await ye(t);if(n.size>0){const i={};n.forEach((d,p)=>{i[p]=d}),k(d=>({...d,...i}))}const l=t.filter(i=>!n.has(i));if(l.length>0)for(let i=0;i<l.length&&!r.aborted;i+=10){const d=l.slice(i,i+10);try{const p=await j.post("/preview_thr_batch",{file_names:d},r);for(const[x,g]of Object.entries(p))g&&!g.error&&Mt(x,g);k(x=>({...x,...p}))}catch(p){if(p instanceof Error&&p.name==="AbortError")break}i+10<l.length&&await new Promise(p=>setTimeout(p,100))}}catch(n){if(n instanceof Error&&n.name==="AbortError")return;console.error("Error fetching previews:",n)}},xt=async t=>{Pe(!0);try{const s=await j.post("/get_theta_rho_coordinates",{file_name:t});ke(s.coordinates||[])}catch(s){console.error("Error fetching coordinates:",s),m.error("Failed to load pattern coordinates")}finally{Pe(!1)}},gt=a.useMemo(()=>{const t=new Set(h.map(s=>s.category));return["all",...Array.from(t).sort()]},[h]),S=a.useMemo(()=>{let t=h;return P!=="all"&&(t=t.filter(s=>s.category===P)),N&&(t=t.filter(s=>Ue(s.name,N)||Ue(s.category,N))),t=[...t].sort((s,r)=>{let n=0;switch(I){case"name":n=s.name.localeCompare(r.name);break;case"date":n=s.date_modified-r.date_modified;break;case"size":n=s.coordinates_count-r.coordinates_count;break;case"favorites":{const l=R.has(s.path)?1:0;n=(R.has(r.path)?1:0)-l,n===0&&(n=s.name.localeCompare(r.name));break}case"plays":{const l=s.path.split("/").pop()||"",i=r.path.split("/").pop()||"",d=T[l]?.play_count??0,p=T[i]?.play_count??0;n=d-p,n===0&&(n=s.name.localeCompare(r.name));break}case"last_played":{const l=s.path.split("/").pop()||"",i=r.path.split("/").pop()||"",d=T[l]?.last_played||"",p=T[i]?.last_played||"";n=d.localeCompare(p),n===0&&(n=s.name.localeCompare(r.name));break}default:return 0}return _?n:-n}),t},[h,P,N,I,_,R,T]);a.useEffect(()=>{E(je)},[N,P,I,_]);const $e=a.useMemo(()=>S.slice(0,f),[S,f]);a.useEffect(()=>{if(B||f>=S.length)return;const t=le.current;if(!t)return;const s=new IntersectionObserver(r=>{r.forEach(n=>{n.isIntersecting&&E(l=>l<S.length?Math.min(l+je,S.length):l)})},{rootMargin:"600px"});return s.observe(t),()=>s.disconnect()},[f,S.length,B]);const yt=a.useCallback(t=>{Ee.current[t]||Q.current.has(t)||(Q.current.add(t),Y.current&&clearTimeout(Y.current),Y.current=setTimeout(()=>{const s=Array.from(Q.current);s.length>0&&(Q.current.clear(),Te(s))},50))},[]),V=a.useCallback((t,s,r)=>{const n=r/2,l=r/2,i=r/2*.9*s,d=n+i*Math.cos(t),p=l+i*Math.sin(t);return{x:d,y:p}},[]),te=a.useRef(null),D=a.useRef(-1),De=a.useRef(null),ue=a.useCallback(()=>{const t=document.documentElement.classList.contains("dark");return{isDark:t,bgOuter:t?"#1a1a1a":"#f5f5f5",bgInner:t?"#262626":"#ffffff",borderColor:t?"rgba(255, 255, 255, 0.2)":"rgba(128, 128, 128, 0.3)",lineColor:t?"#e5e5e5":"#333333",markerBorder:t?"#333333":"#ffffff"}},[]),Me=a.useCallback((t,s)=>{const r=ue();te.current||(te.current=document.createElement("canvas"));const n=te.current;n.width=t,n.height=t;const l=n.getContext("2d");if(l){if(l.fillStyle=r.bgOuter,l.fillRect(0,0,t,t),l.beginPath(),l.arc(t/2,t/2,t/2*.95,0,Math.PI*2),l.fillStyle=r.bgInner,l.fill(),l.strokeStyle=r.borderColor,l.lineWidth=1,l.stroke(),l.strokeStyle=r.lineColor,l.lineWidth=1,l.lineCap="round",l.lineJoin="round",s.length>0){const i=V(s[0][0],s[0][1],t);l.beginPath(),l.moveTo(i.x,i.y),l.stroke()}D.current=0,De.current=r.isDark}},[ue,V]),K=a.useCallback((t,s,r,n=!1)=>{const i=t.canvas.width,d=ue();(n||!te.current||De.current!==d.isDark||r<D.current)&&Me(i,s);const x=te.current;if(!x)return;const g=x.getContext("2d");if(g){if(s.length>0&&r>D.current){g.strokeStyle=d.lineColor,g.lineWidth=1,g.lineCap="round",g.lineJoin="round",g.beginPath();const M=V(s[D.current][0],s[D.current][1],i);g.moveTo(M.x,M.y);for(let se=D.current+1;se<=r&&se<s.length;se++){const He=V(s[se][0],s[se][1],i);g.lineTo(He.x,He.y)}g.stroke(),D.current=r}if(t.drawImage(x,0,0),r<s.length&&s.length>0){const M=V(s[r][0],s[r][1],i);t.beginPath(),t.arc(M.x,M.y,5,0,Math.PI*2),t.fillStyle="#0b80ee",t.fill(),t.strokeStyle=d.markerBorder,t.lineWidth=1,t.stroke()}}},[ue,Me,V]);a.useEffect(()=>{if(!q||v.length===0||!A.current)return;const t=A.current.getContext("2d");if(!t)return;let s=performance.now();const r=100*oe,n=l=>{const i=(l-s)/1e3;s=l;const d=Math.floor(i*r);C.current=Math.min(C.current+Math.max(1,d),v.length-1),K(t,v,C.current),H(C.current/(v.length-1)*100),C.current<v.length-1?U.current=requestAnimationFrame(n):$(!1)};return U.current=requestAnimationFrame(n),()=>{U.current&&cancelAnimationFrame(U.current)}},[q,v,oe,K]),a.useEffect(()=>{if(v.length>0&&A.current){const t=A.current.getContext("2d");t&&(C.current=0,H(0),K(t,v,0,!0))}},[v,K]);const vt=async t=>{X(t),F(!0),_e(null);try{const s=await j.get(`/api/pattern_history/${encodeURIComponent(t.path)}`);_e(s)}catch{}},jt=async()=>{o&&(F(!1),Ce(!0),$(!1),H(0),C.current=0,await xt(o.path),$(!0))},Oe=()=>{Ce(!1),$(!1),U.current&&cancelAnimationFrame(U.current),ke([])},Be=()=>{q?$(!1):(C.current>=v.length-1&&(C.current=0,H(0)),$(!0))},bt=()=>{if($(!1),C.current=0,H(0),A.current&&v.length>0){const t=A.current.getContext("2d");t&&K(t,v,0,!0)}},wt=t=>{const s=t[0];if(H(s),C.current=Math.floor(s/100*(v.length-1)),A.current&&v.length>0){const r=A.current.getContext("2d");r&&K(r,v,C.current)}},Nt=async()=>{if(o){we(!0);try{await j.post("/run_theta_rho",{file_name:o.path,pre_execution:L}),m.success(`Running ${o.name}`),F(!1),window.dispatchEvent(new CustomEvent("playback-started"))}catch(t){const s=t instanceof Error?t.message:"Failed to run pattern";s.includes("409")||s.includes("already running")?m.error("Another pattern is already running"):m.error(s)}finally{we(!1)}}},Ct=()=>{if(o){if(!o.path.startsWith("custom_patterns/")){m.error("Only custom patterns can be deleted");return}me(o)}},kt=async()=>{if(Z)try{await j.post("/delete_theta_rho_file",{file_name:Z.path}),m.success(`Deleted ${Z.name}`),F(!1),X(null),ee()}catch{m.error("Failed to delete pattern")}finally{me(null)}},ze=async t=>{if(o)try{await j.post("/add_to_queue",{pattern:o.path,position:t}),m.success(t==="next"?"Playing next":"Added to queue")}catch(s){const r=s instanceof Error?s.message:"Failed to add to queue";r.includes("400")||r.includes("No playlist")?m.error("No playlist is currently running"):m.error(r)}},Le=t=>y[t]?.image_data||null,qe=t=>{if(!t)return null;const s=Math.hypot(t.x,t.y);return s<.1?"center":s>=.9?"edge":"mid-table"},Pt=o?.path.startsWith("custom_patterns/"),St=async()=>{if(he)return;Ae(!0),fe(0);const t=await zt(s=>{const r=s.total>0?Math.round(s.completed/s.total*100):0;fe(r)});t.success?(Re(!0),t.cached===0?m.success("All patterns are already cached!"):m.success("All pattern previews have been cached!")):m.error("Failed to cache previews"),Ae(!1),fe(0)},_t=async t=>{const s=t.target.files;if(!s||s.length===0)return;const r=Array.from(s).filter(i=>!i.name.endsWith(".thr"));if(r.length>0){m.error(`Invalid file${r.length>1?"s":""}: ${r.map(i=>i.name).join(", ")}. Only .thr files are accepted.`);return}Ie(!0);let n=0,l=0;for(const i of Array.from(s))try{await j.uploadFile("/upload_theta_rho",i),n++}catch(d){console.error(`Upload error for ${i.name}:`,d),l++}l===0?m.success(`${n} pattern${n>1?"s":""} uploaded`):n>0?m.warning(`${n} uploaded, ${l} failed`):m.error(`${l} upload${l>1?"s":""} failed`),n>0&&await ee(),Ie(!1),J.current&&(J.current.value="")};return B?e.jsx("div",{className:"flex items-center justify-center min-h-[60vh]",children:e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined animate-spin text-4xl text-muted-foreground",children:"sync"})}):e.jsxs("div",{className:"flex flex-col w-full max-w-5xl mx-auto gap-3 sm:gap-6 py-3 sm:py-6 px-0 sm:px-4",children:[!c&&e.jsx("input",{ref:J,type:"file",accept:".thr",multiple:!0,onChange:_t,className:"hidden"}),e.jsxs("div",{className:"flex items-start justify-between gap-4 pl-1",children:[e.jsxs("div",{className:"space-y-0.5",children:[e.jsx("h1",{className:"text-xl font-semibold tracking-tight",children:"Browse Patterns"}),e.jsxs("p",{className:"text-xs text-muted-foreground",children:[h.length," patterns available"]})]}),!c&&e.jsxs(w,{variant:"ghost",onClick:()=>J.current?.click(),disabled:xe,className:"gap-2 shrink-0 h-9 w-9 sm:h-11 sm:w-auto rounded-full px-0 sm:px-4 justify-center bg-card border border-border shadow-sm hover:bg-accent",children:[xe?e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined animate-spin text-lg",children:"sync"}):e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined text-lg",children:"add"}),e.jsx("span",{className:"hidden sm:inline",children:"Add Pattern"})]})]}),e.jsx(Ht,{}),e.jsx("div",{className:"sticky z-30 py-3 -mx-0 sm:-mx-4 px-0 sm:px-4 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60",style:{top:"calc(4.5rem + env(safe-area-inset-top, 0px))"},children:e.jsxs("div",{className:"flex items-center gap-2 sm:gap-3",children:[e.jsxs("div",{className:"relative flex-1 min-w-0",children:[e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined absolute left-3 sm:left-4 top-1/2 -translate-y-1/2 text-muted-foreground text-lg sm:text-xl",children:"search"}),e.jsx(Ot,{value:N,onChange:t=>W(t.target.value),placeholder:"Search...",className:"pl-9 sm:pl-11 pr-10 h-9 sm:h-11 rounded-full bg-card border-border shadow-sm text-xs sm:text-sm focus:ring-2 focus:ring-ring"}),N&&e.jsx(w,{variant:"ghost",size:"icon",onClick:()=>W(""),"aria-label":"Clear search",className:"absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground h-7 w-7 rounded-full",children:e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined text-lg",children:"close"})})]}),e.jsxs(Ke,{value:P,onValueChange:ne,children:[e.jsxs(We,{className:"h-9 w-9 sm:h-11 sm:w-auto rounded-full bg-card border-border shadow-sm text-xs sm:text-sm shrink-0 [&>svg]:hidden sm:[&>svg]:block px-0 sm:px-3 justify-center sm:justify-between [&>span:last-of-type]:hidden sm:[&>span:last-of-type]:inline gap-2",children:[e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined text-lg shrink-0 sm:hidden",children:"folder"}),e.jsx(Xe,{placeholder:"All"})]}),e.jsx(Ze,{children:gt.map(t=>e.jsx(O,{value:t,children:t==="all"?"All":t==="root"?"Default":t},t))})]}),e.jsxs(Ke,{value:I,onValueChange:t=>{const s=t;z(s),u(s!=="plays"&&s!=="last_played")},children:[e.jsxs(We,{className:"h-9 w-9 sm:h-11 sm:w-auto rounded-full bg-card border-border shadow-sm text-xs sm:text-sm shrink-0 [&>svg]:hidden sm:[&>svg]:block px-0 sm:px-3 justify-center sm:justify-between [&>span:last-of-type]:hidden sm:[&>span:last-of-type]:inline gap-2",children:[e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined text-lg shrink-0 sm:hidden",children:"sort"}),e.jsx(Xe,{placeholder:"Sort"})]}),e.jsxs(Ze,{children:[e.jsx(O,{value:"favorites",children:"Favorites"}),e.jsx(O,{value:"name",children:"Name"}),e.jsx(O,{value:"date",children:"Modified"}),e.jsx(O,{value:"size",children:"Size"}),e.jsx(O,{value:"plays",children:"Most Played"}),e.jsx(O,{value:"last_played",children:"Last Played"})]})]}),e.jsx(w,{variant:"outline",size:"icon",onClick:()=>u(!_),className:"shrink-0 h-9 w-9 sm:h-11 sm:w-11 rounded-full bg-card shadow-sm",title:_?"Ascending":"Descending",children:e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined text-lg sm:text-xl",children:_?"arrow_upward":"arrow_downward"})}),!ht&&e.jsx(w,{variant:"outline",onClick:St,className:`shrink-0 rounded-full bg-card shadow-sm gap-2 ${he?"h-9 sm:h-11 w-auto px-3 sm:px-4":"h-9 w-9 sm:h-11 sm:w-auto px-0 sm:px-4 justify-center sm:justify-start"}`,title:"Cache All Previews",children:he?e.jsxs(e.Fragment,{children:[e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined animate-spin text-lg",children:"sync"}),e.jsxs("span",{className:"text-sm",children:[mt,"%"]})]}):e.jsxs(e.Fragment,{children:[e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined text-lg",children:"cached"}),e.jsx("span",{className:"hidden sm:inline text-sm",children:"Cache"})]})})]})}),(N||P!=="all")&&e.jsxs("p",{className:"text-sm text-muted-foreground",children:["Showing ",S.length," of ",h.length," patterns"]}),h.length===0?e.jsxs("div",{className:"flex flex-col items-center justify-center min-h-[40vh] gap-4 text-center",children:[e.jsx("div",{className:"p-4 rounded-full bg-muted",children:e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined text-5xl text-muted-foreground",children:"gesture"})}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("h2",{className:"text-xl font-semibold",children:"No patterns yet"}),e.jsx("p",{className:"text-muted-foreground",children:"Upload a .thr pattern file to get started"})]}),!c&&e.jsxs(w,{onClick:()=>J.current?.click(),disabled:xe,className:"gap-2",children:[e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined text-lg",children:"add"}),"Add Pattern"]})]}):S.length===0?e.jsxs("div",{className:"flex flex-col items-center justify-center min-h-[40vh] gap-4 text-center",children:[e.jsx("div",{className:"p-4 rounded-full bg-muted",children:e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined text-5xl text-muted-foreground",children:"search_off"})}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("h2",{className:"text-xl font-semibold",children:"No patterns found"}),e.jsx("p",{className:"text-muted-foreground",children:"Try adjusting your search or filters"})]}),(N||P!=="all")&&e.jsx(w,{variant:"secondary",onClick:()=>{W(""),ne("all")},children:"Clear Filters"})]}):e.jsxs(nt.Provider,{value:{requestPreview:yt,previews:y},children:[e.jsx("div",{className:"grid grid-cols-3 sm:grid-cols-4 md:grid-cols-5 lg:grid-cols-6 gap-2 sm:gap-4",children:$e.map(t=>e.jsx(Xt,{pattern:t,isSelected:o?.path===t.path,isFavorite:R.has(t.path),playTime:T[t.path.split("/").pop()||""]?.actual_time_formatted||null,playCount:T[t.path.split("/").pop()||""]?.play_count??0,onToggleFavorite:ge,onClick:()=>vt(t)},t.path))}),f<S.length&&e.jsxs(e.Fragment,{children:[e.jsx("div",{ref:le,className:"h-1","aria-hidden":"true"}),e.jsxs("p",{className:"text-xs text-muted-foreground text-center",children:["Showing ",$e.length," of ",S.length," patterns"]})]})]}),e.jsx("div",{className:"h-48"}),e.jsx(Ut,{open:lt,onOpenChange:F,children:e.jsxs(st,{className:"flex flex-col p-0 overflow-hidden pt-safe",onTouchStart:ft,onTouchEnd:pt,children:[e.jsx(at,{className:"px-6 py-4 shrink-0",children:e.jsxs(rt,{className:"flex items-center gap-2 pr-8",children:[o&&e.jsx("span",{role:"button",tabIndex:0,className:`shrink-0 transition-colors cursor-pointer flex items-center ${R.has(o.path)?"text-red-500 hover:text-red-600":"text-muted-foreground hover:text-red-500"}`,onClick:t=>ge(o.path,t),onKeyDown:t=>{(t.key==="Enter"||t.key===" ")&&(t.preventDefault(),ge(o.path,t))},title:R.has(o.path)?"Remove from favorites":"Add to favorites",children:e.jsx("span",{"aria-hidden":"true",className:"material-icons",style:{fontSize:"20px"},children:R.has(o.path)?"favorite":"favorite_border"})}),e.jsx("span",{className:"truncate",children:o?.name||"Pattern Details"})]})}),o&&e.jsxs("div",{className:"p-6 overflow-y-auto flex-1",children:[e.jsxs("div",{className:"mb-6",children:[e.jsxs("button",{type:"button",className:"block aspect-square w-full max-w-[280px] mx-auto overflow-hidden rounded-full border bg-muted relative group cursor-pointer",onClick:jt,children:[Le(o.path)?e.jsx("img",{src:Le(o.path),alt:o.name,className:"w-full h-full object-cover pattern-preview"}):e.jsx("div",{className:"w-full h-full flex items-center justify-center",children:e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined text-4xl text-muted-foreground",children:"image"})}),e.jsx("div",{className:"absolute bottom-2 right-2 bg-background/90 backdrop-blur-sm rounded-full w-10 h-10 flex items-center justify-center shadow-md border group-hover:scale-110 transition-transform",children:e.jsx("span",{"aria-hidden":"true",className:"material-icons text-xl",children:"play_arrow"})}),e.jsx("div",{className:"absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-200 bg-black/20 rounded-full"})]}),e.jsx("p",{className:"text-xs text-muted-foreground text-center mt-2",children:"Tap to preview animation"})]}),(()=>{const t=qe(y[o.path]?.first_coordinate),s=qe(y[o.path]?.last_coordinate);return!t&&!s?null:e.jsxs("p",{className:"mb-4 text-sm text-muted-foreground",children:[t&&`Starts: ${t}`,t&&s&&" · ",s&&`Ends: ${s}`]})})(),(()=>{const t=o.path.split("/").pop()||"",s=T[t]?.play_count??0,r=!ce?.actual_time_formatted&&o.coordinates_count>0?Math.max(1,Math.round(o.coordinates_count/18/60)):null;return e.jsx(e.Fragment,{children:(ce?.actual_time_formatted||r!==null||s>0)&&e.jsxs("div",{className:"mb-4 flex justify-between text-sm",children:[ce?.actual_time_formatted?e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined text-muted-foreground text-base",children:"schedule"}),e.jsx("span",{className:"text-muted-foreground",children:"Last run:"}),e.jsx("span",{className:"font-semibold",children:ce.actual_time_formatted})]}):r!==null&&e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined text-muted-foreground text-base",children:"schedule"}),e.jsxs("span",{className:"text-muted-foreground",children:["Est. ~",r," min"]})]}),s>0&&e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined text-muted-foreground text-base",children:"play_circle"}),e.jsx("span",{className:"text-muted-foreground",children:"Plays:"}),e.jsx("span",{className:"font-semibold",children:s})]})]})})})(),e.jsxs("div",{className:"mb-6",children:[e.jsx(ve,{className:"text-sm font-semibold mb-3 block",children:"Clear"}),e.jsx("div",{className:"grid grid-cols-2 gap-2",children:Qe.map(t=>e.jsxs("label",{className:`relative flex cursor-pointer items-center justify-center rounded-lg border p-2.5 text-center text-sm font-medium transition-all hover:border-primary ${L===t.value?"border-primary bg-primary text-primary-foreground ring-2 ring-primary ring-offset-2 ring-offset-background":"border-border text-muted-foreground hover:text-foreground"}`,children:[t.label,e.jsx("input",{type:"radio",name:"preExecutionAction",value:t.value,checked:L===t.value,onChange:()=>it(t.value),className:"sr-only"})]},t.value))}),e.jsx("p",{className:"text-xs text-muted-foreground mt-2",children:Qe.find(t=>t.value===L)?.description})]}),e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"flex gap-2",children:[e.jsxs(w,{onClick:Nt,disabled:be||!Ne,title:Ne?void 0:"Table not connected",className:"flex-1 gap-2",size:"lg",children:[be?e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined animate-spin text-lg",children:"sync"}):e.jsx("span",{"aria-hidden":"true",className:"material-icons text-lg",children:"play_arrow"}),"Play"]}),Pt&&e.jsx(w,{variant:"outline",onClick:Ct,"aria-label":"Delete pattern",className:"text-destructive hover:bg-destructive/10 hover:border-destructive px-3",size:"lg",children:e.jsx("span",{"aria-hidden":"true",className:"material-icons text-lg",children:"delete"})})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs(w,{variant:"outline",size:"sm",className:"flex-1 gap-1.5",onClick:()=>ze("next"),disabled:!ie,title:ie?void 0:"Available while a playlist is playing",children:[e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined text-base",children:"playlist_play"}),"Play Next"]}),e.jsxs(w,{variant:"outline",size:"sm",className:"flex-1 gap-1.5",onClick:()=>ze("end"),disabled:!ie,title:ie?void 0:"Available while a playlist is playing",children:[e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined text-base",children:"playlist_add"}),"Add to Queue"]})]})]})]})]})}),e.jsx(Bt,{open:Z!==null,onOpenChange:t=>{t||me(null)},title:`Delete "${Z?.name}"?`,description:"This cannot be undone.",confirmLabel:"Delete",destructive:!0,onConfirm:kt}),ot&&e.jsx("div",{className:"fixed inset-0 bg-black/80 z-[60] flex items-center justify-center p-4",onClick:Oe,children:e.jsxs("div",{className:"bg-background rounded-lg shadow-xl max-w-4xl w-full max-h-[95vh] flex flex-col overflow-hidden",onClick:t=>t.stopPropagation(),children:[e.jsxs("div",{className:"flex items-center justify-between p-6 border-b shrink-0",children:[e.jsx("h3",{className:"text-xl font-semibold",children:o?.name||"Animated Preview"}),e.jsx(w,{variant:"ghost",size:"icon",onClick:Oe,"aria-label":"Close preview",className:"rounded-full",children:e.jsx("span",{"aria-hidden":"true",className:"material-icons text-2xl",children:"close"})})]}),e.jsx("div",{className:"p-6 overflow-y-auto flex-1 flex justify-center items-center",children:ct?e.jsx("div",{className:"w-full max-w-[400px] aspect-square flex items-center justify-center rounded-full bg-muted",children:e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined animate-spin text-4xl text-muted-foreground",children:"sync"})}):e.jsxs("div",{className:"relative w-full max-w-[400px] aspect-square",children:[e.jsx("canvas",{ref:A,width:400,height:400,role:"img","aria-label":`Animated preview of ${o?.name||"pattern"}`,className:"rounded-full w-full h-full"}),e.jsx("div",{className:"absolute inset-0 flex items-center justify-center cursor-pointer rounded-full opacity-0 hover:opacity-100 transition-opacity bg-black/10",onClick:Be,children:e.jsx("div",{className:"bg-background rounded-full w-16 h-16 flex items-center justify-center shadow-lg",children:e.jsx("span",{"aria-hidden":"true",className:"material-icons text-3xl",children:q?"pause":"play_arrow"})})})]})}),e.jsxs("div",{className:"p-6 space-y-4 shrink-0 border-t",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"flex justify-between mb-2",children:[e.jsx(ve,{className:"text-sm font-medium",children:"Speed"}),e.jsxs("span",{className:"text-sm text-muted-foreground",children:[oe,"x"]})]}),e.jsx(Ve,{value:[oe],onValueChange:t=>dt(t[0]),min:.1,max:5,step:.1,className:"py-2"})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex justify-between mb-2",children:[e.jsx(ve,{className:"text-sm font-medium",children:"Progress"}),e.jsxs("span",{className:"text-sm text-muted-foreground",children:[Se.toFixed(0),"%"]})]}),e.jsx(Ve,{value:[Se],onValueChange:wt,min:0,max:100,step:.1,className:"py-2"})]}),e.jsxs("div",{className:"flex items-center justify-center gap-4",children:[e.jsxs(w,{onClick:Be,className:"gap-2",children:[e.jsx("span",{"aria-hidden":"true",className:"material-icons",children:q?"pause":"play_arrow"}),q?"Pause":"Play"]}),e.jsxs(w,{variant:"secondary",onClick:bt,className:"gap-2",children:[e.jsx("span",{"aria-hidden":"true",className:"material-icons",children:"replay"}),"Reset"]})]})]})]})})]})}function Xt({pattern:c,isSelected:h,isFavorite:b,playTime:y,playCount:k,onToggleFavorite:B,onClick:re}){const[N,W]=a.useState(!1),[P,ne]=a.useState(!1),I=a.useRef(null),z=a.useContext(nt);a.useEffect(()=>{if(!z||!I.current)return;const u=new IntersectionObserver(f=>{f.forEach(E=>{E.isIntersecting&&(z.requestPreview(c.path),u.disconnect())})},{rootMargin:"100px"});return u.observe(I.current),()=>u.disconnect()},[c.path,z]);const _=z?.previews[c.path]?.image_data||null;return e.jsxs("button",{ref:I,onClick:re,className:`group flex flex-col items-center gap-2 p-2.5 rounded-xl bg-card border border-border transition-all duration-200 ease-out hover:-translate-y-1 hover:shadow-md active:scale-95 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 ${h?"ring-2 ring-primary ring-offset-2 ring-offset-background":""}`,children:[e.jsx("div",{className:"relative w-full aspect-square",children:e.jsx("div",{className:"w-full h-full rounded-full overflow-hidden border border-border bg-muted",children:_&&!P?e.jsxs(e.Fragment,{children:[!N&&e.jsx("div",{className:"absolute inset-0 flex items-center justify-center",children:e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined animate-spin text-xl text-muted-foreground",children:"sync"})}),e.jsx("img",{src:_,alt:c.name,className:`w-full h-full object-cover pattern-preview transition-opacity ${N?"opacity-100":"opacity-0"}`,loading:"lazy",onLoad:()=>W(!0),onError:()=>ne(!0)})]}):e.jsx("div",{className:"w-full h-full flex items-center justify-center",children:e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined text-2xl text-muted-foreground",children:P?"broken_image":"image"})})})}),(k>0||y)&&e.jsxs("div",{className:"flex items-center w-full px-0.5 -mb-1 justify-between",children:[k>0&&e.jsxs("span",{className:"flex items-center gap-0.5 text-xs text-muted-foreground",title:`Played ${k} time${k!==1?"s":""}`,children:[e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined",style:{fontSize:"13px"},children:"play_circle"}),k,"x"]}),y&&e.jsxs("span",{className:"flex items-center gap-0.5 text-xs text-muted-foreground ml-auto",title:`Last run: ${y}`,children:[e.jsx("span",{"aria-hidden":"true",className:"material-icons-outlined",style:{fontSize:"13px"},children:"schedule"}),(()=>{const u=y.match(/^(?:(\d+):)?(\d+):(\d+)$/);if(u){const E=u[1]?parseInt(u[1]):0,le=parseInt(u[2]),o=parseInt(u[3]),X=E*60+le+(o>=30?1:0);return X>0?`${X}m`:"<1m"}const f=y.match(/(\d+)h\s*(\d+)m|(\d+)\s*min|(\d+)m\s*(\d+)s|(\d+)\s*sec/);if(f){if(f[1]&&f[2])return`${parseInt(f[1])*60+parseInt(f[2])}m`;if(f[3])return`${f[3]}m`;if(f[4]&&f[5]){const E=parseInt(f[4]);return E>0?`${E}m`:"<1m"}else if(f[6])return"<1m"}return y})()]})]}),e.jsxs("div",{className:"flex items-center justify-between w-full gap-1 px-0.5",children:[e.jsx("span",{className:"text-xs font-bold text-foreground truncate",title:c.name,children:c.name}),e.jsx("span",{role:"button",tabIndex:0,className:`shrink-0 transition-colors cursor-pointer ${b?"text-red-500 hover:text-red-600":"text-muted-foreground hover:text-red-500"}`,onClick:u=>{u.stopPropagation(),B(c.path,u)},onKeyDown:u=>{(u.key==="Enter"||u.key===" ")&&(u.preventDefault(),u.stopPropagation(),B(c.path,u))},title:b?"Remove from favorites":"Add to favorites",children:e.jsx("span",{"aria-hidden":"true",className:"material-icons",style:{fontSize:"16px"},children:b?"favorite":"favorite_border"})})]})]})}export{ls as BrowsePage};