PageSourceSearch

https://hypetunes.com/assets/AdminStationManager-RQdEoC67.js

js hypetunes.com collected 2026-10-02 16:11:35 UTC 188,486 bytes, 69 lines download raw bytes

1const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ShowStage3D-BoyT1HXW.js","assets/index-D0ni8z01.js","assets/index-CJ9cxVMq.css","assets/three.module-Dol8ZsoG.js","assets/vanilla-m3Xdu9cz.js","assets/faceDecal-MJv--XPr.js","assets/stageBridge-7twDgoYX.js","assets/facePanel-DJZmsmgm.js","assets/ShowStage3D-DhGXt_Tf.css"])))=>i.map(i=>d[i]);
2import{aR as s,aH as e,a as Ue,I as de,a7 as js,ab as xs,aQ as ys,f as Ns}from"./index-D0ni8z01.js";import ws from"./AdminPlatformKnowledge-BKBVvQMC.js";import ks from"./AdminSoundLibrary-DNlJnnt3.js";import{M as _s}from"./AdminStationManager-DKgA67dL.js";import{s as Ss}from"./stageBridge-7twDgoYX.js";import{u as Cs}from"./useImageGeneration-5MnOKnjx.js";const Ts=()=>localStorage.getItem("token");async function Pe(t,o={}){const{headers:c,body:v,...l}=o;return(await fetch(`${Ue}${t}`,{...l,headers:{Authorization:`Bearer ${Ts()}`,...v?{"Content-Type":"application/json"}:{},...c},...v?{body:JSON.stringify(v)}:{}})).json()}const $s={targetType:"track",targetId:"",title:"",artistName:"",artworkUrl:"",occasion:"standard",placement:"features",set:"",hostAvatarId:"",autoContinue:!1},ts=60,as=t=>t.lane==="wan_fly"?t.framing==="vj_to_wide"||t.framing==="mid_to_wide"?"pull-out (WAN)":"artwork reveal (WAN)":"close-up (Kling)",Ps={awaiting_audio_review:"review narration",awaiting_visual_review:"review imagery",ready:"ready — approve?"};function Rs(){const[t,o]=s.useState([]),[c,v]=s.useState([]),[l,f]=s.useState($s),[R,L]=s.useState(!1),[z,m]=s.useState(null),[x,b]=s.useState({key:"",name:"",setPrompt:"",includeLogo:!1}),[B,d]=s.useState(!1),[D,S]=s.useState(null),[E,I]=s.useState(""),[q,j]=s.useState(null),[k,C]=s.useState(""),[M,V]=s.useState([]),[X,ie]=s.useState(null),[Q,u]=s.useState(!1),[g,_]=s.useState(null),U=s.useCallback(async()=>{const a=await Pe("/api/admin/vj/segments").catch(p=>({error:p.message}));a?.segments?(o(a.segments),_(null)):_(a?.error||"Segments request returned no data.")},[]),te=s.useRef({}),ue=(a,p,se)=>{const n=te.current[a];return n&&n.raw===p&&n.signed?n.signed:(se&&(te.current[a]={raw:p,signed:se}),se)},re=s.useCallback(async()=>{const a=await Pe("/api/admin/vj/sets").catch(()=>null);a?.sets&&v(a.sets)},[]);s.useEffect(()=>{U(),re()},[U,re]);const J=t.some(a=>a.status==="generating"),[je,$]=s.useState(0),[A,he]=s.useState(0),ee=s.useCallback(()=>$(Date.now()+240*1e3),[]);s.useEffect(()=>{if(!(J||Date.now()<je))return;const a=setTimeout(async()=>{await U(),he(p=>p+1)},8e3);return()=>clearTimeout(a)},[J,je,A,U]),s.useEffect(()=>{if(Q||X)return;const a=setTimeout(async()=>{const p=await Pe(`/api/admin/vj/feature-videos?q=${encodeURIComponent(k)}`).catch(()=>null);p?.videos&&V(p.videos)},300);return()=>clearTimeout(a)},[k,Q,X]);const y=a=>{ie(a),f(p=>({...p,targetType:"video",targetId:String(a.video_id),title:a.title,artistName:a.artist_name||"",artworkUrl:a.artwork_url||""}))},W=()=>{ie(null),f(a=>({...a,targetId:"",title:"",artistName:"",artworkUrl:""}))},xe=()=>{Q?(u(!1),ie(null),f(a=>({...a,targetType:"video",targetId:"",title:"",artistName:"",artworkUrl:""}))):u(!0)},be=async(a=null)=>{const p=a||l;if(!p.targetType||!p.targetId||!p.title){m({kind:"err",text:Q?"Target type, target id and title are required.":"Pick a video from the search first."});return}L(!0),m(null);const se={targetType:p.targetType,targetId:Number(p.targetId),title:p.title,...p.artistName?{artistName:p.artistName}:{},...p.artworkUrl?{artworkUrl:p.artworkUrl}:{},...p.occasion?{occasion:p.occasion}:{},...p.placement?{placement:p.placement}:{},...p.set?{set:p.set}:{},...p.hostAvatarId?{hostAvatarId:Number(p.hostAvatarId)}:{},autoContinue:!!p.autoContinue},n=await Pe("/api/admin/vj/segments",{method:"POST",body:se}).catch(P=>({error:P.message}));L(!1),n?.success?(m({kind:"ok",text:p.autoContinue?"Generating straight through — the row below fills in as each step lands (~1-3 min).":"Generating — it will pause below for your narration review."}),ee(),setTimeout(U,1200),U()):m({kind:"err",text:n?.error||"Failed to start generation."})},[Z,ve]=s.useState(""),Te=async a=>{const p=await Pe(`/api/admin/vj/segments/${a.vj_id}/continue`,{method:"POST",body:{}}).catch(se=>({error:se.message}));p?.success||m({kind:"err",text:p?.error||"Failed to continue."}),ee(),U()},$e=async a=>{const p=await Pe(`/api/admin/vj/segments/${a.vj_id}/redo`,{method:"POST",body:Z?{note:Z}:{}}).catch(se=>({error:se.message}));p?.success?ve(""):m({kind:"err",text:p?.error||"Failed to redo."}),ee(),U()},N=async(a,p)=>{const se=await Pe(`/api/admin/vj/segments/${a.vj_id}/keyframes/regen`,{method:"POST",body:{framing:p,...Z?{note:Z}:{}}}).catch(n=>({error:n.message}));se?.success?ve(""):m({kind:"err",text:se?.error||"Failed to regen keyframe."}),ee(),U()},H=async(a,p)=>{const se=await Pe(`/api/admin/vj/segments/${a.vj_id}/decision`,{method:"POST",body:{action:p}}).catch(n=>({error:n.message}));se?.success||m({kind:"err",text:se?.error||`Failed to ${p}.`}),U()},[ce,Re]=s.useState(!1),ge=a=>be({targetType:a.target_type,targetId:a.target_id,title:a.title,artistName:a.artist_name||"",artworkUrl:a.artwork_url||"",occasion:a.occasion,placement:a.placement||"features",set:"",hostAvatarId:a.host_avatar_id,autoContinue:!!a.auto_continue}),pe=async(a,p=!1)=>{j(a.set_id);const se=await Pe(`/api/admin/vj/sets/${a.set_id}/sheet`,{method:"POST",body:{force:p}}).catch(n=>({error:n.message}));j(null),se?.success||m({kind:"err",text:se?.error||"Sheet render failed."}),re()},r=async a=>{await Pe(`/api/admin/vj/sets/${a.set_id}/default`,{method:"POST",body:{}}).catch(()=>null),re()},w=async()=>
2{if(!x.key||!x.name||!x.setPrompt){m({kind:"err",text:"New set needs key, name and prompt."});return}const a=await Pe("/api/admin/vj/sets",{method:"POST",body:x}).catch(p=>({error:p.message}));a?.success?(b({key:"",name:"",setPrompt:"",includeLogo:!1}),d(!1),re()):m({kind:"err",text:a?.error||"Failed to create set."})},ae=async a=>{await Pe(`/api/admin/vj/sets/${a.set_id}`,{method:"PUT",body:{includeLogo:!a.include_logo}}).catch(()=>null),re()},oe=async a=>{await Pe(`/api/admin/vj/sets/${a.set_id}`,{method:"PUT",body:{setPrompt:E}}).catch(()=>null),S(null),re()},Se=(a,p={})=>e.jsx("input",{className:"vjp-input",value:l[a],onChange:se=>f(n=>({...n,[a]:se.target.value})),...p});return e.jsxs("div",{className:"vjp",children:[e.jsxs("section",{className:"vjp-card",children:[e.jsx("h3",{children:"📺 New VJ segment"}),e.jsx("p",{className:"vjp-muted",children:"The host throws to what plays next — 16:9, WAN orbit, ~12s (a single take hard-caps at 15s, so lines stay under ~14s). It pauses below for narration review, then imagery review, before each render spend. Features = videos only, the throw introduces the artist and the song; Listen comes later (adds video-less tracks with slideshows)."}),!Q&&(X?e.jsxs("div",{className:"vjp-picked",children:[X.artwork_url_signed&&e.jsx("img",{className:"vjp-picked-art",src:X.artwork_url_signed,alt:"artwork"}),e.jsxs("div",{className:"vjp-picked-meta",children:[e.jsx("strong",{children:X.title}),e.jsx("span",{className:"vjp-muted",children:X.artist_name}),e.jsxs("span",{className:"vjp-muted",children:["video #",X.video_id," · ",X.video_type==="hypeclip"?"hypeclip":"music video"]})]}),e.jsx("button",{className:"vjp-btn",onClick:W,children:"Change"})]}):e.jsxs("div",{className:"vjp-vsearch",children:[e.jsx("input",{className:"vjp-input",placeholder:"Search the features pool — song or artist…",value:k,onChange:a=>C(a.target.value)}),e.jsxs("div",{className:"vjp-vres",children:[M.map(a=>e.jsxs("button",{type:"button",className:"vjp-vres-item",onClick:()=>y(a),children:[a.artwork_url_signed?e.jsx("img",{src:a.artwork_url_signed,alt:""}):e.jsx("span",{className:"vjp-vres-noart"}),e.jsx("span",{className:"vjp-vres-title",children:a.title}),e.jsx("span",{className:"vjp-muted",children:a.artist_name}),e.jsx("span",{className:"vjp-chip",children:a.video_type==="hypeclip"?"clip":"video"})]},a.video_id)),!M.length&&e.jsx("div",{className:"vjp-muted vjp-vres-empty",children:k?"No featured videos match.":"Loading the features pool…"})]})]})),e.jsxs("div",{className:"vjp-grid",children:[Q&&e.jsxs(e.Fragment,{children:[e.jsxs("label",{children:["Target type",e.jsxs("select",{className:"vjp-input",value:l.targetType,onChange:a=>f(p=>({...p,targetType:a.target.value})),children:[e.jsx("option",{value:"track",children:"track"}),e.jsx("option",{value:"post",children:"post"}),e.jsx("option",{value:"video",children:"video"}),e.jsx("option",{value:"episode",children:"episode"})]})]}),e.jsxs("label",{children:["Target id",Se("targetId",{type:"number",placeholder:"e.g. 2018"})]}),e.jsxs("label",{children:["Title",Se("title",{placeholder:"What the VJ announces"})]}),e.jsxs("label",{children:["Artist",Se("artistName",{placeholder:"optional"})]}),e.jsxs("label",{className:"vjp-wide",children:["Artwork URL (shown on the studio monitor)",Se("artworkUrl",{placeholder:"gs:// or https — optional"})]})]}),e.jsxs("label",{children:["Shows in",e.jsxs("select",{className:"vjp-input",value:l.placement,onChange:a=>f(p=>({...p,placement:a.target.value})),children:[e.jsx("option",{value:"features",children:"features (videos only)"}),e.jsx("option",{value:"listen",children:"listen (later — slideshows)"})]})]}),e.jsxs("label",{children:["Occasion",e.jsxs("select",{className:"vjp-input",value:l.occasion,onChange:a=>f(p=>({...p,occasion:a.target.value})),children:[e.jsx("option",{value:"standard",children:"standard"}),e.jsx("option",{value:"premiere",children:"premiere"}),e.jsx("option",{value:"milestone",children:"milestone"})]})]}),e.jsxs("label",{children:["Studio",e.jsxs("select",{className:"vjp-input",value:l.set,onChange:a=>f(p=>({...p,set:a.target.value})),children:[e.jsx("option",{value:"",children:"(default)"}),c.filter(a=>a.is_active).map(a=>e.jsx("option",{value:a.key,children:a.name},a.set_id))]})]}),e.jsxs("label",{children:["Host avatar id",Se("hostAvatarId",{type:"number",placeholder:"19 (default)"})]})]}),e.jsxs("label",{className:"vjp-check",children:[e.jsx("input",{type:"checkbox",checked:l.autoContinue,onChange:a=>f(p=>({...p,autoContinue:a.target.checked}))}),"Skip reviews (render straight through)"]}),e.jsxs("div",{className:"vjp-row",children:[e.jsx("button",{className:"vjp-btn vjp-btn-primary",onClick:()=>be(),disabled:R,children:R?"Starting…":"Generate segment"}),e.jsx("button",{className:"vjp-linkbtn",onClick:xe,children:Q?"use the video search instead":"manual entry"}),z&&e.jsx("span",{className:`vjp-note vjp-note-${z.kind}`,children:z.text})]})]}),e.jsxs("section",{className:"vjp-card",children:[e.jsxs("h3",{children:["Segments ",J&&e.jsx("span",{className:"vjp-live",children:"● generating…"}),t.some(a=>a.status==="archived")&&e.jsx("button",{className:"vjp-linkbtn vjp-arch-toggle",onClick:()=>Re(a=>!a),children:ce?"hide archived":`show archived (${t.filter(a=>a.status==="archived").length})`})]}),g&&e.jsxs("p",{className:"vjp-fail",children:["Segments list failing: ",g]}),t.length===0&&!g&&e.jsx("p",{className:"vjp-muted",children:"Nothing yet — fire the first one above."}),t.filter(a=>ce||a.status!=="archived").map(a=>e.jsxs("div",{className:`vjp-seg vjp-seg-${a.status}`,children:[e.jsxs("div",{className:"vjp-seg-head",children:[e.jsx("span",{className:`vjp-chip vjp-chip-${a.status}`,children:Ps[a.status]||a.status}),e.jsx("strong",{children:a.title}),a.artist_name&&e.jsxs("span",{className:"vjp-muted",children:["— ",a.artist_name]}),e.jsxs("span",{className:"vjp-muted",children:["· ",a.target_type," ",a.target_id," · host ",a.host_avatar_id," · ",a.occasion," · ",a.placement||"features"]}),a.duration_seconds&&e.jsxs("span",{className:"vjp-muted",children:["· ",Number(a.duration_seconds).toFixed(1),"s"]}),a.status==="ready"&&e.jsxs(e.Fragment,{children:[e.jsx("button",{className:"vjp-btn vjp-btn-primary",onClick:()=>H(a,"approve"),children:"Approve for air"}),e.jsx("button",{className:"vjp-btn",onClick:()=>H(a,"archive"),children:"Archive"})]}),a.status==="approved"&&e.jsx("button",{className:"vjp-btn",onClick:()=>H(a,"archive"),children:"Archive"}),a.status==="archived"&&e.jsx("button",{className:"vjp-btn",onClick:()=>H(a,"restore"),children:"Restore"}),a.status==="failed"&&e.jsxs(e.Fragment,{children:[e.jsx("button",{className:"vjp-btn",onClick:()=>ge(a),children:"Retry"}),e.jsx("button",{className:"vjp-btn",onClick:()=>H(a,"archive"),children:"Archive"})]})]}),a.script&&e.jsxs("div",{className:"vjp-script",children:["“",a.script,"”"]}),a.status==="failed"&&a.failure_reason&&e.jsx("div",{className:"vjp-fail",children:a.failure_reason}),e.jsxs("details",{className:"vjp-debug",children:[e.jsx("summary",{children:"Prompts & inputs"}),e.jsxs("div",{className:"vjp-debug-body",children:[e.jsxs("div",{className:"vjp-debug-item",children:[e.jsxs("span",{className:"vjp-debug-label",children:["script (the TTS input)",a.audio_seconds!=null&&` — ${Number(a.audio_seconds).toFixed(2)}s spoken`]}),e.jsx("p",{children:a.script||"—"})]}),e.jsxs("div",{className:"vjp-debug-item",children:[e.jsx("span",{className:"vjp-debug-label",children:"keyframe prompt (image model)"}),e.jsx("p",{children:a.keyframe_prompt||"— not stored for this run (pre-326 row)"})]}),e.jsxs("div",{className:"vjp-debug-item",children:[e.jsxs("span",{className:"vjp-debug-label",children:["shot plan",a.shot_plan?` — ${a.shot_plan}`:""]}),Array.isArray(a.shots)&&a.shots.length>0?a.shots.map(p=>e.jsxs("p",{children:[e.jsxs("strong",{children:["#",p.shot_index+1," ",as(p)]})," (",p.start_sec,"s +",p.duration_sec,"s): ",p.prompt||"—"]},p.shot_id)):e.jsx("p",{children:a.video_prompt||"— no plan yet (or pre-hybrid single-take row)"})]}),e.jsxs("div",{className:"vjp-debug-item",children:[e.jsx("span",{className:"vjp-debug-label",children:"set sheet the keyframe composed against"}),a.sheet_url_signed?e.jsx("img",{className:"vjp-thumb",src:ue(`${a.vj_id}:sheet`,a.sheet_url,a.sheet_url_signed),alt:"set sheet"}):e.jsx("p",{children:"—"})]}),e.jsxs("div",{className:"vjp-debug-item",children:[e.jsx("span",{className:"vjp-debug-label",children:"monitor artwork input"}),e.jsx("p",{className:"vjp-debug-mono",children:a.artwork_url||"— none passed"})]})]})]}),e.jsxs("div",{className:"vjp-seg-media",children:[a.keyframe_url_signed&&e.jsx("img",{className:"vjp-thumb",src:ue(`${a.vj_id}:kf`,a.keyframe_url,a.keyframe_url_signed),alt:"keyframe"}),a.audio_url_signed&&!a.video_url_signed&&e.jsx("audio",{controls:!0,src:ue(`${a.vj_id}:audio`,a.audio_url,a.audio_url_signed)}),a.video_url_signed&&e.jsx("video",{className:"vjp-video",controls:!0,src:ue(`${a.vj_id}:video`,a.video_url,a.video_url_signed)})]}),Array.isArray(a.shots)&&a.shots.length>0&&e.jsx("div",{className:"vjp-shots",children:a.shots.map(p=>e.jsxs("div",{className:"vjp-shot",children:[e.jsxs("div",{className:"vjp-shot-head",children:[e.jsxs("strong",{children:[p.shot_index+1,". ",as(p)]}),e.jsxs("span",{className:"vjp-muted",children:[p.start_sec,"s +",p.duration_sec,"s"]}),e.jsx("span",{className:`vjp-chip vjp-chip-${p.status==="done"?"ready":p.status==="rendering"?"generating":p.status}`,children:p.status})]}),e.jsxs("div",{className:"vjp-shot-frames",children:[p.start_frame_url_signed?e.jsx("img",{src:ue(`s${p.shot_id}:sf`,p.start_frame_url,p.start_frame_url_signed),alt:"start frame"}):e.jsx("span",{className:"vjp-shot-chain",children:"chains from previous shot"}),p.end_frame_url_signed&&e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"vjp-shot-arrow",children:"→"}),e.jsx("img",{src:ue(`s${p.shot_id}:ef`,p.end_frame_url,p.end_frame_url_signed),alt:"end frame"})]}),p.video_url_signed&&e.jsx("video",{controls:!0,src:ue(`s${p.shot_id}:v`,p.video_url,p.video_url_signed)})]}),p.status==="failed"&&p.failure_reason&&e.jsx("div",{className:"vjp-fail",children:p.failure_reason})]},p.shot_id))}),(a.status==="awaiting_audio_review"||a.status==="awaiting_visual_review")&&(()=>{const p=a.status==="awaiting_audio_review",se=a.audio_seconds!=null?Number(a.audio_seconds):null,n=se==null||se<=ts,P=Array.isArray(a.shots)?a.shots.length:0;return e.jsxs("div",{className:"vjp-gate",children:[p&&se!=null&&e.jsxs("span",{className:n?"vjp-fit-ok":"vjp-fit-bad",children:[se.toFixed(1),"s spoken",n?"":` — over the ${ts}s sanity cap`]}),e.jsx("input",{className:"vjp-input vjp-gate-note",placeholder:p?"notes for the rewrite (optional)":'notes for a regen (optional — e.g. "the outfit is a maroon suit")',value:Z,onChange:Y=>ve(Y.target.value)}),!p&&(()=>{const Y=Array.isArray(a.shots)?a.shots:[],we=Y.some(ke=>ke.framing==="artwork_to_vj"),me=Y.some(ke=>ke.framing==="vj_to_wide"||ke.framing==="mid_to_wide");return e.jsxs(e.Fragment,{children:[e.jsxs("button",{className:"vjp-btn",onClick:()=>N(a,"mid"),title:"Rebuilds the VJ base frame; derived frames (artwork opener, wide) rebuild from it automatically",children:["Regen VJ frame",me?" (+wide)":""]}),we&&e.jsx("button",{className:"vjp-btn",onClick:()=>N(a,"artwork"),children:"Regen artwork frame"}),me&&e.jsx("button",{className:"vjp-btn",onClick:()=>N(a,"wide"),children:"Regen wide"})]})})(),e.jsxs("button",{className:"vjp-btn",onClick:()=>$e(a),children:["Redo ",p?"narration":"plan + keyframes"]}),e.jsx("button",{className:"vjp-btn vjp-btn-primary",onClick:()=>Te(a),children:p?"Approve → plan shots + keyframes":`Approve → render ${P||"the"} shots`})]})})()]},a.vj_id))]}),e.jsxs("section",{className:"vjp-card",children:[e.jsx("h3",{children:"🎬 Set designer"}),e.jsx("p",{className:"vjp-muted",children:"The studio library. Each set's sheet renders ONCE and is reused so the show stays familiar; editing a prompt clears its sheet. Every sheet automatically includes the wall monitor — that's the artwork surface, the show doesn't work without it."}),e.jsx("div",{className:"vjp-sets",children:c.map(a=>e.jsxs("div",{className:`vjp-set ${a.is_default?"is-default":""}`,children:[a.sheet_url_signed?e.jsx("img",{className:"vjp-set-sheet",src:a.sheet_url_signed,alt:a.name}):e.jsx("div",{className:"vjp-set-sheet vjp-set-sheet-empty",children:"no sheet yet"}),e.jsxs("div",{className:"vjp-set-info",children:[e.jsx("strong",{children:a.name})," ",e.jsx("code",{children:a.key}),!!a.is_default&&e.jsx("span",{className:"vjp-chip vjp-chip-ready",children:"default"})]}),e.jsxs("label",{className:"vjp-check",title:"Toggling clears the cached sheet — re-render to see the change",children:[e.jsx("input",{type:"checkbox",checked:!!a.include_logo,onChange:()=>ae(a)}),"HypeTunes logo in the set"]}),e.jsxs("div",{className:"vjp-row",children:[e.jsx("button",{className:"vjp-btn",disabled:q===a.set_id,onClick:()=>pe(a,!!a.sheet_url),children:q===a.set_id?"Rendering…":a.sheet_url?"Re-render sheet":"Ren
2der sheet"}),!a.is_default&&e.jsx("button",{className:"vjp-btn",onClick:()=>r(a),children:"Make default"}),e.jsx("button",{className:"vjp-btn",onClick:()=>{S(D===a.set_id?null:a.set_id),I(a.set_prompt||"")},children:D===a.set_id?"Cancel":"Edit prompt"})]}),D===a.set_id&&e.jsxs("div",{className:"vjp-editwrap",children:[e.jsx("textarea",{className:"vjp-input vjp-textarea",rows:4,value:E,onChange:p=>I(p.target.value)}),e.jsx("button",{className:"vjp-btn vjp-btn-primary",onClick:()=>oe(a),children:"Save (clears sheet)"})]})]},a.set_id))}),B?e.jsxs("div",{className:"vjp-newset",children:[e.jsx("input",{className:"vjp-input",placeholder:"key (e.g. neon_loft)",value:x.key,onChange:a=>b(p=>({...p,key:a.target.value}))}),e.jsx("input",{className:"vjp-input",placeholder:"Name",value:x.name,onChange:a=>b(p=>({...p,name:a.target.value}))}),e.jsx("textarea",{className:"vjp-input vjp-textarea",rows:3,placeholder:"Set prompt — describe the studio (the monitor is added automatically)",value:x.setPrompt,onChange:a=>b(p=>({...p,setPrompt:a.target.value}))}),e.jsxs("label",{className:"vjp-check",children:[e.jsx("input",{type:"checkbox",checked:x.includeLogo,onChange:a=>b(p=>({...p,includeLogo:a.target.checked}))}),"HypeTunes logo in the set"]}),e.jsxs("div",{className:"vjp-row",children:[e.jsx("button",{className:"vjp-btn vjp-btn-primary",onClick:w,children:"Create set"}),e.jsx("button",{className:"vjp-btn",onClick:()=>d(!1),children:"Cancel"})]})]}):e.jsx("button",{className:"vjp-btn",onClick:()=>d(!0),children:"+ New set"})]})]})}function ns(t){return{currentTime:0,duration:t,paused:!1,ended:!1,muted:!0,playbackRate:1}}const Os=()=>new Promise(t=>requestAnimationFrame(()=>t())),ms=()=>Object.values(Ss.faceMounts||{}).filter(Boolean),is=t=>{for(const o of ms())try{o.setCapture?.(t)}catch{}},Es=t=>Promise.all(ms().map(o=>{try{return o.captureStep?.(t)??null}catch{return null}}));async function As({bridge:t,audio:o,seconds:c,fps:v=24,startSeconds:l=0,post:f,onProgress:R=null,signal:L=null,quality:z=.92,settleFrames:m=2}){if(!t?.gl||!t?.advance)throw new Error("the stage is not in capture mode — pass capture and onCaptureReady");if(!o)throw new Error("captureTake needs the fake audio object the stage is reading");if(!(c>0))throw new Error("a take needs a length");const x=Math.max(1,Math.round(c*v)),b=t.gl.domElement,B=t.clock?.elapsedTime??0;is(!0);try{for(let d=0;d<x;d+=1){if(L?.aborted)throw new Error("capture cancelled");const D=d/v;o.currentTime=l+D;for(let E=0;E<m;E+=1)await Os();await Es(1/v),t.advance(B+D);const S=b.toDataURL("image/jpeg",z);if(d===0&&S.length<2e3)throw new Error("the first frame came back empty — the Canvas is probably not in capture mode (preserveDrawingBuffer)");await f(d,S),R?.({done:d+1,total:x})}return{frames:x,fps:v,seconds:x/v}}finally{is(!1)}}const Fs=s.lazy(()=>xs(()=>import("./ShowStage3D-BoyT1HXW.js"),__vite__mapDeps([0,1,2,3,4,5,6,7,8]))),Ge=4,Ke=15,Ls=3,Is=3,qe=8,Us=t=>Math.max(Is,Math.min(qe,Math.round((Number(t)||0)/Ls))),Me=t=>{const o=Math.max(0,Math.round(Number(t)||0));return`${Math.floor(o/60)}:${String(o%60).padStart(2,"0")}`},Ds=()=>localStorage.getItem("token"),Le=async(t,o={})=>{const{headers:c,...v}=o;return(await fetch(`${Ue}${t}`,{...v,headers:{Authorization:`Bearer ${Ds()}`,...c}})).json()};let rs=1;function Bs({show:t,episodes:o=[],showMsg:c}){const[v,l]=s.useState(!1),[f,R]=s.useState("capture"),[L,z]=s.useState(""),[m,x]=s.useState(null),[b,B]=s.useState(!1),[d,D]=s.useState(0),[S,E]=s.useState(12e3),[I,q]=s.useState(24),[j,k]=s.useState(!1),[C,M]=s.useState(null),[V,X]=s.useState(null),ie=s.useRef(null),Q=s.useRef(null),u=s.useRef(null),g=s.useRef(ns(0)),[_,U]=s.useState({w:0,h:0}),[te,ue]=s.useState([]),[re,J]=s.useState(null),[je,$]=s.useState(0),A=s.useRef(null),[he,ee]=s.useState([]),[y,W]=s.useState(""),[xe,be]=s.useState(""),[Z,ve]=s.useState(null),[Te,$e]=s.useState(!1),[N,H]=s.useState(!1),[ce,Re]=s.useState(!1),[ge,pe]=s.useState(null),[r,w]=s.useState(null),[ae,oe]=s.useState([]),Se=s.useMemo(()=>(o||[]).filter(h=>h.status==="published"),[o]);s.useEffect(()=>{v&&Le("/api/podcast/scene-restyle/looks").then(h=>{if(!h?.success)return;ee(h.looks||[]);const F=(h.looks||[]).find(ne=>ne.is_default)||(h.looks||[])[0];F&&W(ne=>ne||F.slug)}).catch(()=>{})},[v]);const a=s.useCallback(h=>{h&&Le(`/api/podcast/scene-restyle/runs?episodeId=${h}&limit=10`).then(F=>oe(F?.runs||[])).catch(()=>oe([]))},[]);s.useEffect(()=>{if(!L){x(null);return}B(!0),X(null),w(null),ve(null),ue([]),pe(null),Le(`/api/podcast/episodes/${L}
2`).then(h=>{x(h?.episode||null)}).catch(()=>c?.("Could not load that episode",!0)).finally(()=>B(!1)),a(L)},[L]),s.useEffect(()=>{const h=u.current;if(!h||!v)return;const F=()=>{const Ce=h.getBoundingClientRect();U({w:Math.round(Ce.width),h:Math.round(Ce.height)})};F();const ne=new ResizeObserver(F);return ne.observe(h),()=>ne.disconnect()},[v,f,m]);const p=s.useMemo(()=>!m?.audio_url||!m?.duration_seconds?null:{durationMs:Math.round(Number(m.duration_seconds)*1e3),audioUrl:m.audio_url},[m]),se=s.useMemo(()=>{const h=m?.visual_timeline?.cues;return!Array.isArray(h)||!h.length?null:h.slice(0,200).map((F,ne)=>({startSeconds:Number(F.startTime??F.start??0),endSeconds:Number(F.endTime??F.end??F.startTime??0),label:F.speaker||F.title||`cue ${ne+1}`})).filter(F=>F.endSeconds>F.startSeconds)},[m]),n=Math.round(S/1e3),P=async()=>{if(!ie.current){c?.("The stage has not finished loading yet — give it a moment.",!0);return}if(!_.w||!_.h){c?.("The stage has no size on screen yet.",!0);return}k(!0),M({done:0,total:n*I}),X(null);const h=new AbortController;Q.current=h;let F=null;try{const ne=await Le("/api/podcast/scene-restyle/capture",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({width:_.w,height:_.h,fps:I,label:`ep${L}`})});if(!ne?.success)throw new Error(ne?.error||"could not open a capture");F=ne.captureId;const Ce=ns(Number(m?.duration_seconds)||0);g.current=Ce;const K=await As({bridge:ie.current,audio:Ce,seconds:n,fps:I,startSeconds:d/1e3,signal:h.signal,onProgress:M,post:async(De,ze)=>{const i=await Le(`/api/podcast/scene-restyle/capture/${F}/frame`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({index:De,frame:ze})});if(!i?.success)throw new Error(i?.error||`frame ${De} was refused`)}}),Fe=await Le(`/api/podcast/scene-restyle/capture/${F}/finish`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({fps:I})});if(!Fe?.success)throw new Error(Fe?.error||"the encode failed");F=null,X(Fe);const He=Us(Fe.durationSeconds||n),Ve=Fe.durationSeconds||n;ue(Array.from({length:He},(De,ze)=>({id:`k${rs++}`,t:Number((Ve*((ze+.5)/He)).toFixed(2)),prompt:null}))),R("markers"),c?.(`Captured ${K.frames} frames — ${Fe.durationSeconds}s`)}catch(ne){F&&Le(`/api/podcast/scene-restyle/capture/${F}`,{method:"DELETE"}).catch(()=>{}),c?.(ne.message||"the capture failed",!0)}finally{k(!1),M(null),Q.current=null}},Y=he.find(h=>h.slug===y)||null,we=Y?.frame_prompt||"",me=()=>{if(te.length>=qe){c?.(`${qe} keyframes is the cap — each one is a paid image generation.`,!0);return}const h=Number((A.current?.currentTime??je).toFixed(2)),F={id:`k${rs++}`,t:h,prompt:null};ue(ne=>[...ne,F].sort((Ce,K)=>Ce.t-K.t)),J(F.id)},ke=h=>{ue(F=>F.filter(ne=>ne.id!==h)),J(F=>F===h?null:F)},fe=te.find(h=>h.id===re)||null,Ee=h=>{const F=A.current;F&&(F.currentTime=h),$(h)},Ae=()=>({videoUrl:V?.videoUrl,startSeconds:0,durationSeconds:V?.durationSeconds||n,markers:[...te].sort((h,F)=>h.t-F.t).map(h=>({atSeconds:h.t,prompt:h.prompt||null})),look:y||null,styleNote:xe.trim()||null}),Je=async()=>{$e(!0),ve(null);try{const h=await Le(`/api/podcast/episodes/${L}/scene-restyle?plan=1`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(Ae())});if(!h?.success)throw new Error(h?.error||"the check failed");ve(h.plan),R("film")}catch(h){c?.(h.message,!0)}finally{$e(!1)}},Xe=async()=>{const h=Z?.keyframes??te.length;if(confirm(`Generate ${h} keyframe${h===1?"":"s"}?
3
4This buys the images only — no video. You can look at them and then either film from them or throw them away.`)){Re(!0),pe(null),w(null);try{const F=await Le(`/api/podcast/episodes/${L}/scene-restyle`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...Ae(),stopAfterKeyframes:!0})});if(!F?.success)throw new Error(F?.error||"the keyframes failed");pe(F),a(L);const ne=(F.keyframes||[]).filter(Ce=>Ce.usedRaw).length;c(ne?`Storyboard ready — but ${ne} frame(s) fell back to the raw 3D render.`:"Storyboard ready. Look it over, then film it.",!!ne)}catch(F){c(F.message,!0)}finally{Re(!1)}}},Ze=async()=>{const h=Z?.keyframes??te.length;if(confirm(ge?.runId?`Film ${Z?.clip?.durationSeconds??n}s from the storyboard above?
5
6The keyframes are already made and paid for — this buys ONE video take. It runs for several minutes and this tab has to stay open.`:`Film ${Z?.clip?.durationSeconds??n}s?
7
8This buys ${h} image generation${h===1?"":"s"} AND one video take. It runs for several minutes and this tab has to stay open.`)){H(!0),w(null);try{const F=ge?.runId?{reuseRunId:ge.runId}
8:Ae(),ne=await Le(`/api/podcast/episodes/${L}/scene-restyle`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(F)});if(!ne?.success)throw new Error(ne?.error||"the render failed");w(ne),a(L),c?.(ne.ok?"The take is ready.":`The take failed: ${ne.error||"no video returned"}`,!ne.ok)}catch(F){c?.(F.message,!0)}finally{H(!1)}}};return v?e.jsxs("div",{className:"show-section fep",children:[e.jsxs("div",{className:"fep-head",children:[e.jsx("label",{children:"Film an episode"}),e.jsx("button",{className:"fep-close",onClick:()=>l(!1),title:"Close",children:e.jsx(de,{name:"xmark",size:14})})]}),e.jsx("div",{className:"fep-steps",children:[["capture","1 · Capture in 3D"],["markers","2 · 3D to film"],["film","3 · Film it"]].map(([h,F])=>e.jsx("button",{className:`fep-step ${f===h?"is-on":""}`,disabled:h!=="capture"&&!V,onClick:()=>R(h),children:F},h))}),f==="capture"&&e.jsxs("div",{className:"fep-body",children:[e.jsxs("div",{className:"fep-row",children:[e.jsxs("select",{value:L,onChange:h=>z(h.target.value),disabled:j,children:[e.jsx("option",{value:"",children:"Choose a published episode…"}),Se.map(h=>e.jsxs("option",{value:h.episode_id,children:[h.title," ",h.duration_seconds?`· ${Me(h.duration_seconds)}`:""]},h.episode_id))]}),e.jsxs("label",{className:"fep-fps",children:["fps",e.jsxs("select",{value:I,onChange:h=>q(Number(h.target.value)),disabled:j,children:[e.jsx("option",{value:12,children:"12"}),e.jsx("option",{value:16,children:"16"}),e.jsx("option",{value:24,children:"24"})]})]})]}),b&&e.jsx("p",{className:"hint",children:"Loading the episode…"}),m&&!p&&e.jsx("p",{className:"fep-warn",children:"This episode has no audio or no stored duration, so there is nothing to align a selection against."}),p&&e.jsxs(e.Fragment,{children:[e.jsx(js,{source:p,resizable:!0,value:d,onChange:D,windowMs:S,onWindowChange:E,minWindowMs:Ge*1e3,maxWindowMs:Ke*1e3,bands:se,label:"Which seconds to film",accent:"#f59e0b",accentSoft:"rgba(245, 158, 11, 0.08)",accentEdge:"rgba(245, 158, 11, 0.22)"}),e.jsxs("div",{className:"fep-row fep-precise",children:[e.jsxs("label",{className:"fep-field",children:["Start",e.jsx("input",{type:"number",min:0,step:.5,max:Math.max(0,p.durationMs/1e3-Ge),value:(d/1e3).toFixed(1),onChange:h=>D(Math.max(0,Math.round(Number(h.target.value)*1e3)))})]}),e.jsxs("label",{className:"fep-field",children:["Length",e.jsx("input",{type:"number",min:Ge,max:Ke,step:.5,value:(S/1e3).toFixed(1),onChange:h=>E(Math.max(Ge*1e3,Math.min(Ke*1e3,Math.round(Number(h.target.value)*1e3))))})]}),e.jsxs("span",{className:"fep-precise-end",children:["→ ends at ",Me((d+S)/1e3)]}),se&&se.length>0&&e.jsxs("label",{className:"fep-field fep-grow",children:["Start on a line",e.jsxs("select",{value:"",onChange:h=>{h.target.value!==""&&D(Math.round(Number(h.target.value)*1e3))},children:[e.jsx("option",{value:"",children:"Jump to a cue…"}),se.map((h,F)=>e.jsxs("option",{value:h.startSeconds,children:[Me(h.startSeconds)," · ",h.label]},F))]})]})]}),e.jsxs("p",{className:"hint",children:[Ge,"–",Ke,"s — the model's own ceiling, not ours. The ticks behind the strip are the episode's cues; jumping to one starts the take on a line rather than mid-word."]})]}),m&&e.jsxs("div",{className:"fep-stage-wrap",children:[e.jsx("div",{className:"fep-stage",ref:u,children:e.jsx(s.Suspense,{fallback:e.jsx("div",{className:"fep-stage-loading",children:"Loading the stage…"}),children:e.jsx(Fs,{showFormat:t?.show_format,show:t,episode:m,audioRef:g,playing:!0,capture:!0,onCaptureReady:h=>{ie.current=h}})})}),e.jsxs("div",{className:"fep-stage-meta",children:[_.w?`${_.w}×${_.h}`:"measuring…"," · ",n,"s at ",I,"fps = ",n*I," frames"]})]}),e.jsxs("div",{className:"fep-actions",children:[e.jsx("button",{className:"btn-primary",onClick:P,disabled:!m||!p||j,children:j?`Capturing ${C?.done??0}/${C?.total??0}…`:"🎥 Capture in 3D"}),j&&e.jsx("button",{className:"btn-ghost",onClick:()=>Q.current?.abort(),children:"Stop"}),V&&!j&&e.jsxs("span",{className:"fep-ok",children:[e.jsx(de,{name:"check",size:12})," ",V.frames," frames · ",V.durationSeconds,"s"]})]}),j&&e.jsx("p",{className:"hint",children:"Every frame is rendered at its exact timestamp rather than recorded, so this takes about as long as the clip itself. Leave the tab in front — a background tab stops animating."})]}),f==="markers"&&V&&e.jsxs("div",{className:"fep-body",children:[e.jsx("video",{ref:A,className:"fep-video",src:V.playbackUrl||void 0,controls:!0,onTimeUpdate:h=>$(h.currentTarget.currentTime)}),!V.playbackUrl&&e.jsx("p",{className:"fep-warn",children:"The capture encoded, but no signed URL came back — the footage cannot be previewed here. The markers below still apply to it."}),e.jsx(_s,{className:"fep-rail",duration:V.durationSeconds||n,currentTime:je,markers:te.map((h,F)=>({id:h.id,t:h.t,label:`${F+1}`,tone:"keyframe"})),selectedId:re,onSeek:Ee,onSelect:J,onMove:(h,F)=>ue(ne=>ne.map(Ce=>Ce.id===h?{...Ce,t:Number(F.toFixed(2))}:Ce))}),e.jsxs("div",{className:"fep-row fep-marker-bar",children:[e.jsxs("button",{className:"btn-ghost",onClick:me,disabled:te.length>=qe,children:[e.jsx(de,{name:"plus",size:12})," Keyframe at playhead"]}),e.jsxs("span",{className:"fep-count",children:[te.length,"/",qe," keyframes",te.filter(h=>h.prompt).length>0&&` · ${te.filter(h=>h.prompt).length} edited`]})]}),e.jsxs("div",{className:"fep-row",children:[e.jsxs("label",{className:"fep-field",children:["Look",e.
8jsxs("select",{value:y,onChange:h=>W(h.target.value),children:[he.length===0&&e.jsx("option",{value:"",children:"Built-in photoreal"}),he.map(h=>e.jsxs("option",{value:h.slug,children:[h.name,h.is_default?" · default":""]},h.slug))]})]}),e.jsxs("label",{className:"fep-field fep-grow",children:["This take only",e.jsx("input",{type:"text",value:xe,onChange:h=>be(h.target.value),placeholder:"e.g. heavy rain on the windows"})]})]}),xe.trim()&&Y&&!/\{\{\s*note\s*\}\}/i.test(Y.frame_prompt||"")&&e.jsxs("p",{className:"fep-warn",children:["“",Y.name,"” has no ",e.jsx("code",{children:"{{note}}"})," token, so this note will be dropped rather than appended. Put it in a marker's own prompt, or edit the look."]}),fe?e.jsxs("div",{className:"fep-prompt",children:[e.jsxs("div",{className:"fep-prompt-head",children:[e.jsxs("strong",{children:["Keyframe ",te.findIndex(h=>h.id===fe.id)+1]}),e.jsxs("span",{className:"fep-at",children:["at ",fe.t.toFixed(2),"s"]}),e.jsx("span",{className:`fep-tag ${fe.prompt?"is-edited":""}`,children:fe.prompt?"custom prompt":"follows the look"}),e.jsx("button",{className:"fep-mini",onClick:()=>Ee(fe.t),children:"Go to"}),fe.prompt==null?e.jsx("button",{className:"fep-mini",onClick:()=>ue(h=>h.map(F=>F.id===fe.id?{...F,prompt:we}:F)),children:"Edit from the look"}):e.jsx("button",{className:"fep-mini",onClick:()=>ue(h=>h.map(F=>F.id===fe.id?{...F,prompt:null}:F)),children:"Reset to the look"}),e.jsx("button",{className:"fep-mini fep-danger",onClick:()=>ke(fe.id),children:"Remove"})]}),e.jsx("textarea",{rows:6,value:fe.prompt??"",placeholder:we||"The built-in photoreal instruction applies to this frame.",onChange:h=>ue(F=>F.map(ne=>ne.id===fe.id?{...ne,prompt:h.target.value}:ne))}),e.jsxs("p",{className:"hint",children:[e.jsx("code",{children:"{{cast}}"}),", ",e.jsx("code",{children:"{{setting}}"})," and ",e.jsx("code",{children:"{{note}}"})," are filled in for you, so they can stay. An edited frame replaces the look outright rather than adding to it — say the whole instruction, not just the change."]})]}):e.jsx("p",{className:"hint",children:"Pick a marker on the rail to see or change what that frame is told."}),e.jsxs("div",{className:"fep-actions",children:[e.jsx("button",{className:"btn-primary",onClick:Je,disabled:Te||te.length===0,children:Te?"Checking…":"Check what this costs →"}),e.jsx("button",{className:"btn-ghost",onClick:()=>R("capture"),children:"← Re-capture"})]})]}),f==="film"&&e.jsxs("div",{className:"fep-body",children:[Z&&e.jsxs("div",{className:"fep-plan",children:[e.jsxs("div",{className:"fep-plan-grid",children:[e.jsxs("div",{children:[e.jsx("span",{children:"Clip"}),e.jsxs("strong",{children:[Z.clip?.durationSeconds,"s from ",Z.clip?.startSeconds,"s"]})]}),e.jsxs("div",{children:[e.jsx("span",{children:"Keyframes"}),e.jsx("strong",{children:Z.keyframes})]}),e.jsxs("div",{children:[e.jsx("span",{children:"Look"}),e.jsx("strong",{children:Z.look?.name})]}),e.jsxs("div",{children:[e.jsx("span",{children:"Buys"}),e.jsxs("strong",{children:[Z.spend?.imageGenerations," images + ",Z.spend?.videoTakes," take"]})]})]}),e.jsx("p",{className:"fep-rule",children:Z.keyframeRule}),(Z.cast||[]).length>0&&e.jsx("div",{className:"fep-cast",children:Z.cast.map((h,F)=>e.jsxs("span",{className:"fep-cast-chip",children:[h.image&&e.jsx("img",{src:h.image,alt:""}),h.name]},F))}),(Z.notes||[]).map((h,F)=>e.jsx("p",{className:"fep-warn",children:h},F))]}),e.jsxs("div",{className:"fep-actions",children:[e.jsx("button",{className:"btn-ghost",onClick:Xe,disabled:ce||N||!Z?.ok,children:ce?"Generating keyframes…":`🖼 ${ge?"Re-shoot":"Generate"} keyframes only`}),e.jsx("button",{className:"btn-primary",onClick:Ze,disabled:N||ce||!Z?.ok,children:N?"Filming — this takes minutes…":ge?"🎬 Film from this storyboard":"🎬 Generate + film"}),e.jsx("button",{className:"btn-ghost",onClick:()=>R("markers"),disabled:N||ce,children:"← Markers"})]}),N&&e.jsx("p",{className:"hint",children:"Each keyframe is generated in turn, then the take is submitted. Keep this tab open — the request is what is holding the result."}),ge&&!r&&e.jsxs("div",{className:"fep-result",children:[e.jsxs("div",{className:"fep-board-head",children:[e.jsx("strong",{children:"Storyboard"}),e.jsxs("span",{className:"fep-count",children:[(ge.keyframes||[]).filter(h=>!h.usedRaw).length,"/",(ge.keyframes||[]).length," styled"," · run #",ge.runId]}),e.jsx("span",{className:"fep-tag",children:"no video bought yet"})]}),e.jsx("div",{className:"fep-frames",children:(ge.keyframes||[]).map(h=>e.jsxs("figure",{className:h.usedRaw?"is-raw":"",children:[e.jsx("img",{src:h.styledUrl||h.rawUrl,alt:`keyframe ${h.index+1}`}),e.jsxs("figcaption",{children:[h.atSeconds,"s",h.usedRaw?" · RAW (not restyled)":"",h.promptSource==="marker"?" · custom"
8:""]})]},h.index))}),e.jsx("p",{className:"hint",children:"Filming from here buys one video take and re-uses these exact images — they are not generated again. Changing a marker or the look means a new storyboard."})]}),r&&e.jsxs("div",{className:"fep-result",children:[r.ok&&r.playbackUrl?e.jsx("video",{className:"fep-video",src:r.playbackUrl,controls:!0}):e.jsxs("p",{className:"fep-warn",children:["No video came back",r.error?`: ${r.error}`:"",". The keyframes below were still made."]}),e.jsx("div",{className:"fep-frames",children:(r.keyframes||[]).map(h=>e.jsxs("figure",{className:h.usedRaw?"is-raw":"",children:[e.jsx("img",{src:h.styledUrl||h.rawUrl,alt:`keyframe ${h.index+1}`}),e.jsxs("figcaption",{children:[h.atSeconds,"s",h.usedRaw?" · RAW (re-style failed)":"",h.promptSource==="marker"?" · custom":""]})]},h.index))}),r.ok&&r.playbackUrl&&e.jsx("p",{className:"hint",children:"This take is saved but NOT attached to the episode — putting one into the show is a separate step that does not exist yet."}),!r.ok&&e.jsxs("p",{className:"hint",children:["The keyframes above were still made and are kept. Run #",r.runId," has the exact prompt and references that produced this."]})]}),ae.length>0&&e.jsxs("details",{className:"fep-runs",children:[e.jsxs("summary",{children:[ae.length," earlier run",ae.length===1?"":"s"," on this episode"]}),e.jsx("ul",{children:ae.map(h=>e.jsxs("li",{children:[e.jsx("span",{className:`fep-run-status is-${h.status}`,children:h.status}),"#",h.run_id," · ",h.keyframe_count," keyframes · ",h.duration_seconds,"s",h.style_note?` · “${h.style_note}”`:"",h.error?` · ${String(h.error).slice(0,80)}`:""]},h.run_id))})]})]})]}):e.jsxs("div",{className:"show-section fep-collapsed",children:[e.jsx("label",{children:"Film an episode"}),e.jsx("p",{className:"hint",children:"Capture this show's 3D stage as footage, mark where the keyframes go, and re-style it into live action. Nothing is written to the episode."}),e.jsx("button",{className:"btn-primary",onClick:()=>l(!0),children:"🎬 Film an episode"})]})}const Js=6e3,zs=100;function Ws({episode:t,token:o,onDone:c}){const[v,l]=s.useState(null),[f,R]=s.useState(!1),[L,z]=s.useState(""),m=s.useRef(null),x=s.useRef(0),b=s.useRef(!0);s.useEffect(()=>()=>{b.current=!1,clearTimeout(m.current)},[]);const B=s.useCallback(async()=>{try{const q=await(await fetch(`${Ue}/api/podcast/episodes/${t.episode_id}/animation`,{headers:{Authorization:`Bearer ${o}`}})).json().catch(()=>({}));return b.current?(l(q?.status||"none"),q?.status||"none"):null}catch{return null}},[t.episode_id,o]);s.useEffect(()=>{B()},[B]);const d=s.useCallback(()=>{clearTimeout(m.current),m.current=setTimeout(async()=>{if(!b.current)return;const I=await B();if(x.current+=1,I==="ready"){R(!1),c?.();return}if(I==="failed"){R(!1),z("The render failed — see the API log.");return}x.current<zs?d():R(!1)},Js)},[B,c]),D=s.useCallback(async I=>{if(!f){R(!0),z(""),x.current=0;try{const q=await fetch(`${Ue}/api/podcast/episodes/${t.episode_id}/animate-cover`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify({force:!!I})}),j=await q.json().catch(()=>({}));if(!q.ok){z(j?.error||"Could not start"),R(!1);return}if(j.alreadyAnimated){l("ready"),R(!1);return}l("rendering"),d()}catch{z("Could not start"),R(!1)}}},[f,t.episode_id,d,o]);if(t.status!=="published"||v==="ineligible"||v==="unavailable")return null;const S=f||v==="rendering",E=v==="ready";return e.jsxs("button",{type:"button",className:`ep-anim-btn${E?" is-ready":""}`,disabled:S,onClick:()=>{if(E){window.confirm("This cover already has an animation. Render a new one? It costs a render.")&&D(!0);return}D(!1)},title:E?"Animated — click to render a replacement":"Animate this episode cover so the show hero plays it",children:[e.jsx(de,{name:E?"check":"wand-magic-sparkles",size:12}),S?"Rendering…":E?"Animated":"Animate cover",L&&e.jsxs("span",{className:"ep-anim-err",children:[" · ",L]})]})}const Hs=[["playful","Playful","serious ↔ very playful"],["confrontational","Confrontational","agreeable ↔ heated"],["reflective","Reflective","surface ↔ philosophical"]];function Qe({value:t,onChange:o,disabled:c=!1}){return t?e.jsx("div",{className:"vibe-grid",children:Hs.map(([v,l,f])=>e.jsxs("div",{className:"vibe-row",children:[e.jsxs("div",{className:"vibe-label",children:[e.jsx("span",{children:l}),e.jsx("span",{className:"vibe-value",children:Number(t[v]??0).toFixed(2)})]}),e.jsx("input",{type:"range",min:"0",max:"1",step:"0.05",value:t[v]??0,disabled:c,onChange:R=>o({...t,[v]:Number(R.target.value)})}),e.jsx("span",{className:"vibe-scale",children:f})]},v))}):null}const es={news:{base:"news",subject:"story",subjects:"stories",panel:"story panel",blurb:"every angle edited out of that episode’s own cover art, one host on camera at a time, with the story on a floating panel beside them",s
8ource:"the newsletter rows behind this episode",noneWarn:"Every panel comes from a story’s own artwork, so filming now buys a whole episode with no graphics in it at all. The newsletter rows are missing — fix those before spending.",carousel:!0},decoded:{base:"decoded",subject:"record",subjects:"records",panel:"record panel",blurb:"every angle edited out of that episode’s own cover art, one person on camera at a time, with the record being discussed on a floating sleeve beside them",source:"the guest’s own released records",noneWarn:"The panel carries the sleeve of the record being discussed, so this episode would film with no graphics at all. Either the guest has no released tracks with artwork, or the guest row is missing — look before spending.",carousel:!1}},Gs=()=>localStorage.getItem("token"),Ne=async(t,o={})=>{const{headers:c,...v}=o;return(await fetch(`${Ue}${t}`,{...v,headers:{Authorization:`Bearer ${Gs()}`,...v.body?{"Content-Type":"application/json"}:{},...c}})).json()},qs=t=>{const o=Math.max(0,Math.round(Number(t)||0));return`${Math.floor(o/60)}:${String(o%60).padStart(2,"0")}`};function Vs(t,o,c,v){const l=String(t).split(":");if(l[1]==="hud"){const f=c.find(R=>String(R.topic_id)===l[3]);return`${o[l[2]]||l[2]} · panel: ${f?String(f.headline).slice(0,34):`${v.subject} ${l[3]}`}`}return`${o[l[2]]||l[2]} · ${l[1]}`}function Ys({show:t,episodes:o=[],showMsg:c,lane:v="news"}){const[l,f]=s.useState(null),[R,L]=s.useState(null),[z,m]=s.useState(null),x=es[v]||es.news;es[v]||console.warn(`[ShowVideoPanel] unknown lane "${v}" — falling back to news`);const[b,B]=s.useState(!1),[d,D]=s.useState(""),[S,E]=s.useState(null),[I,q]=s.useState(""),[j,k]=s.useState(null),[C,M]=s.useState(null),[V,X]=s.useState(!1),[ie,Q]=s.useState(null),[u,g]=s.useState(""),[_,U]=s.useState(""),[te,ue]=s.useState(4),re=s.useRef(null),J=s.useRef({assemble:!1,videoUrl:null}),je=s.useMemo(()=>(o||[]).filter(n=>n.status==="published"&&n.cover_image_url),[o]),$=s.useCallback(async n=>{if(!n)return null;const P=await Ne(`/api/podcast/${x.base}/video/${n}/plan`);return P?.success&&E(P.prepared?P:null),P},[x.base]);s.useEffect(()=>{b&&d&&$(d)},[b,d,$]),s.useEffect(()=>{if(!b||!d){L(null),m(null);return}let n=!1;return Ne(`/api/admin/episodes/${d}/vibe`).then(P=>{n||!P?.success||(L(P),m(P.vibe))}).catch(()=>{}),()=>{n=!0}},[b,d,x.base]),s.useEffect(()=>{if(!b||!d||!x.carousel){M(null);return}let n=!1;return Ne(`/api/podcast/news/carousel/${d}`).then(P=>{!n&&P?.success&&M(P.carousel||null)}).catch(()=>{}),Ne(`/api/podcast/news/carousel/${d}/targets`).then(P=>{!n&&P?.success&&Q(P.targets||null)}).catch(()=>{}),()=>{n=!0}},[b,d,x.carousel]),s.useEffect(()=>{if(!d||_!=="frames"&&_!=="film")return;let n=0,P=-1,Y=0;const we=_==="frames"?1e4:3e4,me=Math.ceil(480*1e3/we),ke=async()=>{const fe=await Ne(`/api/podcast/${x.base}/video/${d}/plan`);if(!fe?.success||!fe.prepared)return;E(fe);const Ee=_==="frames"?Object.keys(fe.keyframes||{}).length:Object.keys(fe.takes||{}).length;if(_==="frames"){if(Ee>=new Set((fe.plan?.shots||[]).map(Ae=>Ae.frame)).size){U("");return}}else{const Ae=Ee>=(fe.plan?.shots||[]).length,Je=J.current.assemble&&(!fe.videoUrl||fe.videoUrl===J.current.videoUrl);if(Ae&&!Je){U(""),J.current.assemble&&c?.("Filmed and re-assembled — the new cut is below.");return}if(Ae&&Je){Y+=1,Y>Math.ceil(360*1e3/we)&&(U(""),c?.("Every take is in but no new cut appeared within six minutes — the assembly may have failed. Nothing is lost; press again to assemble from the takes already bought.",!0));return}}n=Ee===P?n+1:0,P=Ee,n>me&&(U(""),c?.("Nothing new for eight minutes — the run may have stopped. Press the button again to resume; nothing already made is re-bought.",!0))};return ke(),re.current=setInterval(ke,we),()=>clearInterval(re.current)},[_,d,c]);const A=S?.plan||null,he=S?.keyframes||{},ee=S?.takes||{},y=A?{[A.cast.a.avatarId]:A.cast.a.name,[A.cast.b.avatarId]:A.cast.b.name}:{},W=A?.topics||[],xe=new Set((A?.angles||[]).filter(n=>n.hud).map(n=>n.key)),be=A?new Set(A.shots.map(n=>n.frame)).size:0,Z=A?A.shots.filter(n=>ee[n.index]?.url).length:0,ve=A?A.shots.length-Z:0,Te=async(n=!1)=>{U("vibe");try{const P=await Ne(`/api/admin/episodes/${d}/vibe`,{method:"POST",body:JSON.stringify(n?{clear:!0}:z)});if(P?.error)return c?.(P.error,!0);L(P),m(P.vibe),c?.(P.message)}finally{U("")}},$e=async(n=!1)=>
8{if(window.confirm((n?`Rebuild the cover from the set this artist already described?
9
10`:`Ask the guest where they want to be interviewed, then build the cover?
11
12`)+"This spends: one chat call to the artist"+(n?" is skipped, ":", ")+`then the 3:4 cover and the 16:9 banner.
13
14⚠ IT REPLACES THIS EPISODE’S PUBLISHED COVER AND THERE IS NO UNDO.
15
16The film pass edits every camera angle out of the banner, so this has to happen before the keyframes.`)){U("cover");try{const P=await Ne(`/api/podcast/${x.base}/video/${d}/cover`,{method:"POST",body:JSON.stringify({reuseSet:n})});if(P?.error)return c?.(P.error,!0);f(P),c?.(P.message,!P.wideReady),await $(d)}finally{U("")}}},N=async n=>{U("plan");try{const P=await Ne(`/api/podcast/${x.base}/video/${d}/prepare`,{method:"POST",body:JSON.stringify({planOnly:!0,force:!!n})});P?.error?c?.(P.error,!0):(c?.(P.message),await $(d))}finally{U("")}},H=async()=>{const n=await Ne(`/api/podcast/${x.base}/video/${d}/prepare`,{method:"POST",body:JSON.stringify({background:!0})});if(n?.error)return c?.(n.error,!0);c?.(n.message),U("frames")},ce=async n=>{const P=await Ne(`/api/podcast/${x.base}/video/${d}/render`,{method:"POST",body:JSON.stringify({background:!0,maxShots:n||null,assemble:!n})});if(P?.error)return c?.(P.error,!0);c?.(P.message),J.current={assemble:!n,videoUrl:S?.videoUrl||null},U("film")},Re=async()=>{if(!window.confirm(`Film the whole episode unattended?
17
18This plans it, draws the keyframes and buys every take without stopping for you to look at the frames. Nobody will catch a keyframe with two faces in it, or a ${x.panel} across a mouth.
19
20Use the numbered steps instead if you want to check the frames first.`))return;const n=await Ne(`/api/podcast/${x.base}/video/${d}/run`,{method:"POST",body:JSON.stringify({})});if(n?.error)return c?.(n.error,!0);c?.(n.message),J.current={assemble:!0,videoUrl:S?.videoUrl||null},U("film")},ge=async n=>{const P=u.trim();if(P){U("edit");try{const Y=await Ne(`/api/podcast/${x.base}/video/${d}/frame/${encodeURIComponent(n)}/edit`,{method:"POST",body:JSON.stringify({instruction:P})});Y?.error?c?.(Y.error,!0):(c?.(Y.message),q(""),g(""),await $(d))}finally{U("")}}},pe=async n=>{const P=await Ne(`/api/podcast/${x.base}/video/${d}/frame/${encodeURIComponent(n)}`,{method:"DELETE"});if(P?.error)return c?.(P.error,!0);await $(d),await H()},r=async n=>{const P=await Ne(`/api/podcast/${x.base}/video/${d}/frame/${encodeURIComponent(n)}`,{method:"DELETE"});P?.error?c?.(P.error,!0):(c?.("Keyframe dropped — build again to replace it. Takes filmed from it are kept."),await $(d))},w=async()=>{U("thumb");try{const n=await Ne(`/api/podcast/${x.base}/video/${d}/youtube/thumbnail`,{method:"POST",body:JSON.stringify({})});n?.error?c?.(n.error,!0):(k(n),c?.(n.message))}finally{U("")}},ae=async()=>{if(window.confirm(`Publish this episode to the HypeTunes YouTube channel?
21
22A thumbnail is built from the episode's cover with the show logo, the title and the watermark, then the film is uploaded.
23
24This is public.`)){U("youtube");try{const n=await Ne(`/api/podcast/${x.base}/video/${d}/youtube`,{method:"POST",body:JSON.stringify({thumbnailUrl:j?.thumbnail||null})});n?.error?c?.(n.error,!0):(c?.(`${n.message}${n.url?` — ${n.url}`:""}`),await $(d))}finally{U("")}}},oe=async(n=!1,P=!1)=>{if(!(V&&!n&&!window.confirm(`Build the carousel with VIDEO cards?
25
26Each story card gets a segment of this episode's film playing in its picture well, and the cover uses its animated version. This takes a few minutes and needs the episode to have been filmed already.
27
28The still slides are built too — TikTok and the HypeTunes feed always use those.`))&&!(P&&!window.confirm(`Shoot a NEW cover portrait for slide 1?
29
30This generates a fresh two-up of the two hosts — one image generation. Redrawing the slides re-uses the one you already have and costs nothing.`))){U(P?"deckCover":"deck");try{const Y=await Ne(`/api/podcast/news/carousel/${d}`,{method:"POST",body:JSON.stringify({force:n,newCoverArt:P,media:V?"video":"still"})});Y?.error?c?.(Y.error,!0):(M(Y.carousel),c?.(Y.message),Ne(`/api/podcast/news/carousel/${d}/targets`).then(we=>{we?.success&&Q(we.targets)}).catch(()=>{}))}finally{U("")}}},Se=!!C?.slides?.some(n=>n.type==="video"&&n.videoUrl),a=V&&!!C?.slides?.length&&!Se,p=async()=>{const n=C?.slides?.length||0;if(a){c?.('These slides are stills. Press "Redraw the slides" with Video cards ticked, check them, then post.',!0);return}let P=null;U("deckPost");try{const me=await Ne(`/api/podcast/news/carousel/${d}/targets`);me?.success&&(P=me.targets,Q(me.targets))}catch{}finally{U("")}if(!P){c?.("Could not confirm which accounts this would post to — not posting.",!0);return}const Y=["instagram","tiktok","feed"].map(me=>{const ke=P[me];return ke?ke.ok?`${me}: ${ke.account?"@"+ke.account:"ok"}`:`${me}: ${ke.error}`:`${me}: (unknown)`}
30).concat((P.instagramCollaborators||[]).length?[`instagram collaborators (must accept in the app): ${P.instagramCollaborators.map(me=>"@"+me).join(", ")}`]:[]).concat((P.instagramAlso||[]).map(me=>me.ok?`instagram (also): @${me.account||"?"}`:`instagram (also, #${me.connectionId}): ${me.error}`)).join(`
31`),we=Object.keys(C?.posts||{});if(window.confirm(`Post these ${n} slides?
32
33${Y}
34
35`+(we.length?`⚠ This episode is ALREADY posted to: ${we.join(", ")}.
36This publishes a NEW post alongside those — it does not replace them.
37Delete the old one by hand if you want it gone.
38
39`:"")+"The caption shown below goes with them. This is public.")){U("deckPost");try{const me=await Ne(`/api/podcast/news/carousel/${d}/publish`,{method:"POST",body:JSON.stringify({force:!0,requireVideo:V})});if(me?.error)c?.(me.error,!0);else{const ke=(me.posted||[]).join(", ");c?.(me.skipped?me.message:`Posted to ${ke||"nothing"}`,!ke&&!me.skipped);const fe=await Ne(`/api/podcast/news/carousel/${d}`);fe?.success&&M(fe.carousel)}}finally{U("")}}},se=async n=>{const P=await Ne(`/api/podcast/${x.base}/video/${d}/take/${n}`,{method:"DELETE"});P?.error?c?.(P.error,!0):(c?.(`Take ${n} dropped — the next film pass re-shoots it.`),await $(d))};return b?e.jsxs("div",{className:"show-section nvp",children:[e.jsxs("div",{className:"nvp-head",children:[e.jsxs("label",{children:["Film it from the cover — ",t?.name]}),e.jsx("button",{className:"nvp-close",onClick:()=>B(!1),title:"Close",children:e.jsx(de,{name:"xmark",size:14})})]}),e.jsxs("div",{className:"nvp-row",children:[e.jsxs("select",{value:d,onChange:n=>{D(n.target.value),E(null),U("")},children:[e.jsx("option",{value:"",children:"Pick a published episode…"}),je.map(n=>e.jsxs("option",{value:n.episode_id,children:["#",n.episode_id," · ",n.title,n.video_url?" · has video":""]},n.episode_id))]}),t?.show_format==="interview"&&e.jsx("button",{className:"btn-secondary",disabled:!d||!!_,onClick:()=>$e(!1),title:"Ask the guest where they want to be interviewed, then build the cover and the 16:9 banner this pass shoots from. Replaces the published cover.",children:_==="cover"?"Asking & building…":"0 · Set + cover"}),e.jsx("button",{className:"btn-secondary",disabled:!d||!!_,onClick:()=>N(!1),children:_==="plan"?"Planning…":A?"Re-read the plan":"1 · Plan it (free)"}),A&&e.jsx("button",{className:"btn-secondary",disabled:!!_,onClick:()=>N(!0),title:"Throw the plan away and cut it again",children:"Re-cut"}),e.jsx("button",{className:"btn-primary nvp-all",disabled:!d||!!_,onClick:Re,title:"Plan, draw the keyframes and film every take without stopping — no chance to check the frames",children:"▶ Film the whole episode"})]}),!je.length&&e.jsx("p",{className:"nvp-warn",children:"No episode here can be filmed yet. This pass needs a PUBLISHED episode with cover art — the cover is the location every shot is edited out of."}),!!d&&R&&z&&e.jsxs("div",{className:"nvp-vibe",children:[e.jsxs("div",{className:"nvp-vibe-head",children:[e.jsxs("b",{children:["Mood — “",R.key,"”"]}),R.smiling?e.jsx("span",{className:"nvp-vibe-tag",children:"a smile is allowed"}):e.jsx("span",{className:"nvp-vibe-tag",children:"nobody smiling"}),R.episodeSpecific?e.jsx("span",{className:"nvp-vibe-tag is-own",children:"set for this episode"}):e.jsx("span",{className:"nvp-vibe-tag is-default",children:"inherited from the show"})]}),e.jsx("p",{className:"nvp-vibe-faces",children:R.faces}),e.jsx(Qe,{value:z,onChange:m,disabled:!!_}),e.jsxs("div",{className:"nvp-vibe-actions",children:[e.jsx("button",{className:"btn-primary",disabled:!!_,onClick:()=>Te(!1),children:_==="vibe"?"Saving…":"Save for this episode"}),R.episodeSpecific&&e.jsx("button",{className:"btn-secondary",disabled:!!_,onClick:()=>Te(!0),children:"Back to the show default"})]}),e.jsxs("p",{className:"hint",style:{margin:"8px 0 0"},children:["Applies to this episode only. The cover is generated from it, and every camera angle is an edit of that cover — so set it ",e.jsx("b",{children:"before"})," step 0."]})]}),l&&e.jsxs("div",{className:"nvp-set",children:[e.jsxs("div",{className:"nvp-set-head",children:[e.jsx("b",{children:l.set?.place||"The set"}),l.reusedSet&&e.jsx("span",{className:"nvp-set-tag",children:"reused — not re-asked"}),!l.wideReady&&e.jsx("span",{className:"nvp-set-tag is-bad",children:"16:9 banner missing — do not build keyframes"})]}),e.jsx("p",{className:"nvp-set-scene",children:l.set?.scene}),l.set?.why&&e.jsxs("p",{className:"nvp-set-why",children:[e.jsx("b",{children:"Why here:"})," ",l.set.why]}),l.set?.outfit&&e.jsxs("p",{className:"nvp-set-why",children:[e.jsx("b",{children:"Wearing:"})," ",l.set.outfit]}),e.jsxs("div",{className:"nvp-set-shots",children:[l.coverUrl&&e.jsx("a",{href:l.coverUrl,target:"_blank",rel:"noreferrer",title:"The 3:4 cover",children:e.jsx("img",{src:l.coverUrl,alt:"Episode cover"})}),l.wideUrl&&e.jsx("a",{href:l.wideUrl,target:"_blank",rel:"noreferrer",title:"The 16:9 banner — every keyframe is edited out of THIS",children:e.jsx("img",{src:l.wideUrl,alt:"16:9 banner"})})]}),e.jsx("button",{className:"btn-secondary",disabled:!!_,onClick:()=>$e(!0),title:"Build the cover again from the same set description, without asking the artist a second time",children:_==="cover"?"Rebuilding…":"Re-shoot the cover, same set"})]}),A&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"nvp-stats",children:[e.jsxs("div",{children:[e.jsx("b",{children:A.shots.length}),e.jsx("span",{children:"shots"})]}),e.jsxs("div",{children:[e.jsxs("b",{children:[A.stats.averageSeconds,"s"]}),e.jsx("span",{children:"average"})]}),e.jsxs("div",{children:[e.jsx("b",{children:A.stats.hudShots}),e.jsx("span",{children:"with a panel"})]}),e.jsxs("div",{children:[e.jsx("b",{children:be}),e.jsx("span",{children:"keyframes"})]}),e.jsxs("div",{className:"nvp-money",title:"A rate-card ceiling, not a charge: nothing is billed to anyone, and short takes are priced as the next product tier up.",children:[e.jsxs("b",{children:["≤",A.cost.credits]}),e.jsx("span",{children:"credits · ceiling, not a bill"})]})]}),e.jsxs("div",{className:"nvp-where",children:[S?.coverSigned&&e.jsx("a",{className:"nvp-cover",href:S.coverSigned,target:"_blank",rel:"noreferrer",title:"The location every shot is edited out of — open full size",children:e.jsx("img",{src:S.coverSigned,alt:"episode cover",loading:"lazy"})}),e.jsxs("div",{className:"nvp-where-main",children:[e.jsxs("div",{className:"nvp-where-row",children:[e.jsx("span",{className:"nvp-where-k",children:"location"}),e.jsx("span",{className:"nvp-where-v",title:A.cover,children:String(A.cover||"").split("/").pop()||"— none —"})]}),e.jsxs("div",{className:"nvp-where-row",children:[e.jsx("span",{className:"nvp-where-k",children:x.subjects}),e.jsx("span",{className:"nvp-where-v",children:W.length?`${W.length} · ${W.filter(n=>n.image_url).length} with a picture for the panel`:"none"})]}),!W.length&&e.jsxs("p",{className:"nvp-warn",children:["⚠ ",e.jsx("b",{children:"Nothing to put on a panel."}
39)," This pass found no ",x.subjects," behind this episode, from ",x.source,". ",x.noneWarn]}),!!W.length&&e.jsx("ol",{className:"nvp-stories",children:W.map((n,P)=>{const Y=A.shots.filter(we=>String(we.topicId)===String(n.topic_id)&&xe.has(we.angle)).length;return e.jsxs("li",{className:n.image_url?"":"is-bare",children:[e.jsx("span",{className:"nvp-story-n",children:P+1}),n.image_url?e.jsx("span",{className:"nvp-chip",title:"has artwork for the panel",children:"🖼"}):e.jsx("span",{className:"nvp-chip",title:`NO artwork — this ${x.subject} can never raise a panel`,children:"⚠"}),e.jsx("span",{className:"nvp-story-h",children:n.headline}),e.jsxs("span",{className:"nvp-story-c",children:[Y," panel",Y===1?"":"s"]})]},n.topic_id||P)})})]})]}),!!A.stats.paddedShots&&e.jsxs("p",{className:"nvp-warn",children:[A.stats.paddedShots," shot(s) are shorter than ",A.audioFloor,"s — interruptions. Each is still a whole take: their audio is padded with silence to reach the provider's ",A.audioFloor,"s minimum, and the picture is cut back to the real length at assembly. That is the format, not a fault."]}),e.jsxs("details",{className:"nvp-cut",children:[e.jsxs("summary",{children:["The cut — ",A.shots.length," shots"]}),e.jsx("ol",{className:"nvp-shots",children:A.shots.map(n=>{const P=n.frame&&n.frame!=="chain"?he[n.frame]:null,Y=!!ee[n.index]?.url;return e.jsxs("li",{className:Y?"is-filmed":"",children:[e.jsx("span",{className:"nvp-ix",children:n.index}),P?.signed?e.jsx("img",{className:"nvp-shot-thumb",src:P.signed,alt:n.angle,loading:"lazy"}):e.jsx("span",{className:"nvp-shot-thumb is-empty",title:n.frame==="chain"?"continues from the previous take":"not built yet",children:n.frame==="chain"?"⇢":"—"}),e.jsxs("div",{className:"nvp-shot-body",children:[e.jsxs("div",{className:"nvp-shot-meta",children:[e.jsx("span",{className:"nvp-angle",children:n.angle}),e.jsx("span",{className:"nvp-who",children:y[n.avatarId]||n.speaker}),e.jsxs("span",{className:"nvp-secs",children:[n.seconds,"s"]}),xe.has(n.angle)?e.jsx("span",{className:"nvp-chip",title:`the ${x.panel} is in this shot`,children:"🖼"}):null,n.padded?e.jsx("span",{className:"nvp-chip",title:"padded to the audio floor",children:"↔"}):null,Y?e.jsx("span",{className:"nvp-chip",title:"already filmed — re-shooting costs a take",children:"🎬"}):null]}),e.jsx("p",{className:"nvp-text",children:String(n.text||"").replace(/\s+/g," ")})]}),e.jsxs("span",{className:"nvp-shot-acts",children:[P?.signed&&e.jsx("button",{className:"nvp-drop",disabled:!!_,title:`Regenerate this keyframe (${n.frame}) — costs one image, not a take`,onClick:()=>pe(n.frame),children:e.jsx(de,{name:"rotate-left",size:11})}),Y&&e.jsx("button",{className:"nvp-drop",title:"Re-shoot this take",onClick:()=>se(n.index),children:e.jsx(de,{name:"rotate-left",size:11})})]})]},n.index)})})]}),e.jsxs("div",{className:"nvp-step",children:[e.jsxs("div",{className:"nvp-step-head",children:[e.jsxs("span",{children:[e.jsx("b",{children:"2 · Keyframes"})," — ",Object.keys(he).length,"/",be," built"]}),e.jsx("button",{className:"btn-secondary",disabled:!!_||Object.keys(he).length>=be,onClick:H,children:_==="frames"?"Building…":Object.keys(he).length?"Build the missing ones":"Build the keyframes"})]}),e.jsxs("p",{className:"nvp-warn",children:["⚠ Check two things by eye before filming. ",e.jsx("b",{children:"No frame may show two faces"})," — the lip-sync model animates every face it can see, so a second face is a second mouth moving to one voice. And the ",e.jsxs("b",{children:[x.panel," must not cover the speaker's mouth"]}),". Drop a bad frame and build again; it costs one image, not a take."]}),!!Object.keys(he).length&&e.jsx("div",{className:"nvp-frames",children:Object.entries(he).map(([n,P])=>e.jsxs("figure",{className:"nvp-frame",children:[P.signed?e.jsx("img",{src:P.signed,alt:n,loading:"lazy"}):e.jsx("div",{className:"nvp-frame-ph",children:"no preview"}),e.jsxs("figcaption",{children:[e.jsx("span",{children:Vs(n,y,W,x)}),e.jsx("button",{className:"nvp-drop",title:"Edit this frame — one small change, keeps everything else",onClick:()=>{q(I===n?"":n),g("")},children:e.jsx(de,{name:"pen",size:11})}),e.jsx("button",{className:"nvp-drop",disabled:!!_,title:"Regenerate: draw this frame again from the cover. Re-rolls every decision — use it when the wardrobe, pose or framing is wrong, not for a small fix.",onClick:()=>pe(n),children:e.jsx(de,{name:"rotate-left",size:11})}
39),e.jsx("button",{className:"nvp-drop",title:"Drop this frame and leave it missing",onClick:()=>r(n),children:e.jsx(de,{name:"xmark",size:11})})]}),I===n&&e.jsxs("div",{className:"nvp-edit",children:[e.jsx("input",{type:"text",autoFocus:!0,value:u,maxLength:400,placeholder:"have him look into the lens",onChange:Y=>g(Y.target.value),onKeyDown:Y=>{Y.key==="Enter"&&ge(n)}}),e.jsx("button",{className:"btn-secondary",disabled:!!_||!u.trim(),onClick:()=>ge(n),children:_==="edit"?"Editing…":"Apply"}),e.jsx("p",{className:"hint",children:"One change, kept small. Everything else about the shot is held — face, room, lighting, camera. Costs one image, not a take."})]})]},n))})]}),e.jsxs("div",{className:"nvp-step",children:[e.jsxs("div",{className:"nvp-step-head",children:[e.jsxs("span",{children:[e.jsx("b",{children:"3 · Film it"})," — ",Z,"/",A.shots.length," takes"]}),e.jsx("span",{className:"nvp-spacer"}),e.jsxs("label",{className:"nvp-max",children:["first",e.jsx("input",{type:"number",min:"1",max:"20",value:te,onChange:n=>ue(Math.max(1,Math.min(20,Number(n.target.value)||1)))})]}),e.jsxs("button",{className:"btn-secondary",disabled:!!_||Object.keys(he).length<be,onClick:()=>ce(te),title:"Buy a few takes and look at them before committing to the episode",children:["Film ",te," — a cheap look"]}),e.jsx("button",{className:"btn-primary",disabled:!!_||Object.keys(he).length<be,onClick:()=>ce(null),children:_==="film"?"Filming…":ve===A.shots.length?`Film the rest · ≈${A.cost.credits} credits`:`Re-shoot ${ve} take${ve===1?"":"s"}${S?.videoUrl?" & re-assemble":""}`})]}),Object.keys(he).length<be&&e.jsx("p",{className:"hint",children:"Build every keyframe first — a shot with no frame cannot be filmed."}),!_&&!!S?.videoUrl&&ve>0&&e.jsxs("p",{className:"nvp-warn",children:[ve," take",ve===1?" was":"s were"," dropped and not yet re-shot. The published cut still contains the old one",ve===1?"":"s"," until you press the button above — it re-shoots only what is missing and rebuilds the episode."]}),_==="film"&&e.jsx("p",{className:"hint",children:"Filming runs on the server and keeps going if you close this. Takes are saved one by one, so nothing already bought is ever bought twice."}),!!S?.videoUrl&&e.jsxs("div",{className:"nvp-publish",children:[e.jsxs("div",{className:"nvp-step-head",children:[e.jsxs("span",{children:[e.jsx("b",{children:"4 · YouTube"}),S?.youtube?.videoId?" — published":""]}),e.jsx("span",{className:"nvp-spacer"}),S?.youtube?.url?e.jsx("a",{className:"nvp-yt-link",href:S.youtube.url,target:"_blank",rel:"noreferrer",children:"Watch on YouTube ↗"}):e.jsxs(e.Fragment,{children:[e.jsx("button",{className:"btn-secondary",disabled:!!_,onClick:w,children:_==="thumb"?"Building…":j?"Rebuild the card":"1 · Build the card"}),e.jsx("button",{className:"btn-primary",disabled:!!_||!j,onClick:ae,title:j?"Publish this episode and this thumbnail":"Build the card first — you should see the artwork before it is public",children:_==="youtube"?"Publishing…":"2 · Push to YouTube"})]})]}),j?.signed&&!S?.youtube?.url&&e.jsxs("figure",{className:"nvp-thumb",children:[e.jsx("a",{href:j.signed,target:"_blank",rel:"noreferrer",children:e.jsx("img",{src:j.signed,alt:"YouTube thumbnail"})}),e.jsx("figcaption",{children:"This exact image is what publishes. Rebuild it if the title wraps badly or the logo sits wrong."})]}),e.jsx("p",{className:"hint",children:S?.youtube?.videoId?"Already on the channel. Publishing again would post a second copy.":"Builds the thumbnail from this episode’s cover — show logo, episode title, watermark — then uploads the film to the HypeTunes channel. This is public."})]}),!!Z&&e.jsx("div",{className:"nvp-takes",children:A.shots.filter(n=>ee[n.index]?.signed).map(n=>e.jsxs("figure",{className:"nvp-take",children:[e.jsx("video",{src:ee[n.index].signed,controls:!0,preload:"metadata",playsInline:!0}),e.jsxs("figcaption",{children:[e.jsxs("span",{children:[n.index," · ",n.angle," · ",qs(n.start)]}),e.jsx("button",{className:"nvp-drop",title:"Re-shoot this take",onClick:()=>se(n.index),children:e.jsx(de,{name:"rotate-left",size:11})})]})]},n.index))})]}),S.videoSigned&&e.jsxs("div",{className:"nvp-step",children:[e.jsx("div",{className:"nvp-step-head",children:e.jsxs("span",{children:[e.jsx("b",{children:"Done"})," — the episode, filmed"]})}),e.jsx("video",{className:"nvp-final",src:S.videoSigned,controls:!0,preload:"metadata",playsInl
39ine:!0})]})]}),!!d&&x.carousel&&e.jsx(e.Fragment,{children:e.jsxs("div",{className:"nvp-step nvp-deck",children:[e.jsxs("div",{className:"nvp-step-head",children:[e.jsxs("span",{children:[e.jsx("b",{children:"5 · Story carousel"}),C?.slides?.length?` — ${C.slides.length} slides`:"",C?.posts&&Object.keys(C.posts).length?` · posted to ${Object.keys(C.posts).join(", ")}`:""]}),e.jsx("span",{className:"nvp-spacer"}),e.jsxs("label",{className:"nvp-deck-media",title:"Cut each story card's picture well out of the episode's film. Needs a filmed episode; takes a few minutes.",children:[e.jsx("input",{type:"checkbox",checked:V,disabled:!!_,onChange:n=>X(n.target.checked)}),e.jsx("span",{children:"Video cards"})]}),e.jsx("button",{className:"btn-secondary",disabled:!!_,onClick:()=>oe(!!C?.slides?.length),title:"Free — re-composites pictures that already exist, cover portrait included",children:_==="deck"?"Drawing…":C?.slides?.length?"Redraw the slides":"1 · Build the slides"}),!!C?.slides?.length&&e.jsx("button",{className:"btn-secondary",disabled:!!_,onClick:()=>oe(!0,!0),title:"Generates a new two-up portrait of the hosts for slide 1 — one image generation",children:_==="deckCover"?"Shooting…":"New cover photo"}),e.jsx("button",{className:"btn-primary",disabled:!!_||!C?.slides?.length||a,onClick:p,title:a?"These slides are stills — redraw them with Video cards ticked first":C?.slides?.length?"Video to Instagram and the HypeTunes feed, stills to TikTok":"Build the slides first — you should see them before they are public",children:_==="deckPost"?"Posting…":a?"Redraw with video first":"2 · Post the carousel"})]}),C?.slides?.length?e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"nvp-deck-strip",children:C.slides.map(n=>e.jsxs("figure",{className:"nvp-slide",children:[n.type==="video"&&n.signedVideo?e.jsx("video",{src:n.signedVideo,poster:n.signed||void 0,muted:!0,loop:!0,playsInline:!0,controls:!0,preload:"metadata"}):n.signed?e.jsx("a",{href:n.signed,target:"_blank",rel:"noreferrer",children:e.jsx("img",{src:n.signed,alt:`Slide ${n.index+1}`})}):e.jsxs("div",{className:"nvp-slide-missing",children:["slide ",n.index+1]}),e.jsxs("figcaption",{children:[n.index+1," · ",n.kind,n.type==="video"&&e.jsx("span",{className:"nvp-slide-vid",children:" · video"}),n.type==="image"&&n.stillReason&&e.jsxs("span",{className:"nvp-slide-why",title:n.stillReason,children:[" · still: ",n.stillReason]})]})]},n.index))}),C.caption&&e.jsx("pre",{className:"nvp-deck-caption",children:C.caption})]}):e.jsx("p",{className:"hint",children:"One slide per story, drawn from this episode's newsletter — its headlines, its summaries, its pictures, none of it generated. Slide 1 is a two-up portrait of the hosts in the show's older cover style, made once for this shape (the on-location episode cover crops badly into a band) and re-used on every redraw. The last slide says where to hear it."}),ie&&e.jsxs("p",{className:"hint nvp-deck-targets",children:["Posts as:"," ",["instagram","tiktok","feed"].map((n,P)=>{const Y=ie[n];return e.jsxs("span",{className:Y?.ok?"":"nvp-target-bad",children:[P?" · ":"",n," ",Y?.ok?Y.account?`@${Y.account}`:"ok":"— not connected"]},n)}),ie.instagram?.ok&&!ie.instagram?.pinned&&" · ⚠ not pinned — set the account in the show settings",(ie.instagramCollaborators||[]).length>0&&e.jsxs("span",{children:[" ","with ",ie.instagramCollaborators.map(n=>"@"+n).join(", ")]}),(ie.instagramAlso||[]).map((n,P)=>e.jsxs("span",{className:n.ok?"":"nvp-target-bad",children:[" · ","instagram ",n.ok?`@${n.account||"?"}`:`#${n.connectionId} — not connected`]},`also${P}`))]}),C?.posts&&Object.keys(C.posts).length>0&&e.jsxs("p",{className:"hint",children:["Already posted to ",Object.entries(C.posts).map(([n,P])=>`${n}${P.account?` (@${P.account})`:""}`).join(", "),". Posting again skips those and only tries the rest."]})]})})]}):e.jsxs("div",{className:"show-section nvp-collapsed",children:[e.jsx("label",{children:"Film it from the cover"}),e.jsxs("p",{className:"hint",children:["Generate a video of an episode the way The Booth is made: ",x.blurb,". Separate from the 3D capture above."]}),e.jsx("button",{className:"btn-primary",onClick:()=>B(!0),children:"🎥 Film it from the cover"})]})}const We=()=>localStorage.getItem("token");async function T(t,o={}){const{headers:c,...v}=o;return(await fetch(`${Ue}${t}`,{...v,headers:{Authorization:`Bearer ${We()}`,...c}})).json()}function Ks({station:t,onChanged:o}){const[c,v]=s.useState(t.cover_image_url||null),[l,f]=s.useState(""),[R,L]=s.useState(null),[z,m]=s.useState(null),x=s.useRef(null);s.useEffect(()=>
39{v(t.cover_image_url||null),f(""),m(null)},[t.station_id,t.cover_image_url]);const b=(S,E)=>{S?.success?(v(S.signed_url||c),m({ok:!0,text:`Cover ${E}.`}),o?.()):m({ok:!1,text:S?.error||`Cover not ${E}.`})},B=async S=>{if(S){L("upload"),m(null);try{const E=new FormData;E.append("cover",S),b(await T(`/api/admin/stations/${t.station_id}/cover`,{method:"POST",body:E}),"uploaded")}catch{m({ok:!1,text:"Upload failed"})}finally{L(null),x.current&&(x.current.value="")}}},d=async()=>{L("write"),m(null);try{const S=await T(`/api/admin/stations/${t.station_id}/cover/prompt`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({idea:l.trim()})});S?.success&&S.prompt?f(S.prompt):m({ok:!1,text:S?.error||"Could not write a brief"})}catch{m({ok:!1,text:"Could not write a brief"})}finally{L(null)}},D=async()=>{if(l.trim()){L("generate"),m(null);try{b(await T(`/api/admin/stations/${t.station_id}/cover/generate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({prompt:l.trim()})}),"generated")}catch{m({ok:!1,text:"Generation failed"})}finally{L(null)}}};return e.jsxs("div",{className:"panel station-cover-panel",children:[e.jsx("h3",{children:"🖼 Station cover"}),e.jsxs("div",{className:"station-cover-row",children:[e.jsx("div",{className:"station-cover-preview",children:c?e.jsx("img",{src:c,alt:`${t.name} cover`}):e.jsx("span",{children:"No cover yet"})}),e.jsxs("div",{className:"station-cover-actions",children:[e.jsx("input",{ref:x,type:"file",accept:"image/*",hidden:!0,onChange:S=>B(S.target.files?.[0])}),e.jsx("button",{type:"button",className:"btn-ghost",disabled:!!R,onClick:()=>x.current?.click(),children:R==="upload"?"Uploading…":c?"Upload a replacement":"Upload a cover"}),e.jsx("textarea",{value:l,onChange:S=>f(S.target.value),rows:5,placeholder:`…or describe a cover to generate for ${t.name} — or press Auto-write (the name is added to the artwork)`}),e.jsxs("div",{className:"station-cover-btns",children:[e.jsx("button",{type:"button",className:"btn-ghost",disabled:!!R,onClick:d,children:R==="write"?"Writing…":l.trim()?"✍️ Enhance":"✍️ Auto-write"}),e.jsx("button",{type:"button",className:"btn-primary",disabled:!!R||!l.trim(),onClick:D,children:R==="generate"?"Generating…":"✨ Generate cover"})]}),z&&e.jsx("p",{className:`vh-admin-msg ${z.ok?"ok":"err"}`,children:z.text})]})]})]})}function Qs({station:t}){const o=t.host_avatar_id,[c,v]=s.useState(null),[l,f]=s.useState(null),[R,L]=s.useState([]),[z,m]=s.useState(""),[x,b]=s.useState(""),[B,d]=s.useState(!1),[D,S]=s.useState(null),[E,I]=s.useState(!1),[q,j]=s.useState(null),[k,C]=s.useState(!1),M=()=>T(`/api/avatars/${o}/images`).then(g=>L((g?.images||[]).map(_=>_.image_url).filter(_=>_&&!String(_).startsWith("gs://")))).catch(()=>L([])),V=Cs({token:We(),showToast:(g,_)=>S({ok:_!=="error",text:g}),onImageStarted:M});s.useEffect(()=>{const g=V.result?.images?.[0]?.url;g&&(m(g),b(""))},[V.result]);const X=async()=>{try{const[g,_]=await Promise.all([T(`/api/anam/avatar/${o}/status`),T(`/api/anam/radio/${t.station_id}/status`)]);g?.success&&v(g),_?.success&&f(_)}catch{}};s.useEffect(()=>{v(null),f(null),L([]),m(""),b(""),S(null),I(!1),o&&(X(),M(),j(null),T(`/api/avatars/${o}`).then(g=>j(g?.avatar||(g?.avatar_id?g:null))).catch(()=>j(null)))},[o,t.station_id]);const ie=x.trim()||z,Q=async()=>{if(!(!ie||B)){I(!1),d(!0),S(null);try{const g=await T(`/api/anam/avatar/${o}/reface`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({imageUrl:ie,stationId:t.station_id})});g.success?(S(g.faceRecorded?{ok:!0,text:"Photo replaced — the next cameo uses the new face, and /listen now shows this still."}:{ok:!0,text:"Face replaced on camera, but the still was NOT recorded on this station — /listen will keep showing the profile photo. Check the server log for [anam] reface."}),m(""),b(""),X()):S({ok:!1,text:g.error||"Replace failed"})}catch{S({ok:!1,text:"Replace failed"})}finally{d(!1)}}},u=c?.status==="provisioning"?{cls:"warn",label:"Provisioning…"}:c?.status==="failed"?{cls:"err",label:"Face creation failed — replace the photo to retry"}:c?.ready&&l?.available?{cls:"ok",label:"LIVE on this station"}:c?.ready?{cls:"warn",label:"Host ready — video unavailable (check ANAM_API_KEY)"}:{cls:"warn",label:"Not enabled for video yet"};return e.jsxs("div",{className:"panel vh-admin-panel",children:[e.jsxs("h3",{children:["🎥 Video Host",t.host_name?` — ${t.host_name}`:"",e.jsx("span",{className:`vh-chip vh-chip-${u.cls}`,children:u.label})]}),e.jsx("p",{className:"hint",children:"The on-camera face fans see in the /listen room. Pick a new photo from the host's gallery (or paste an image URL) and hit Replace — it goes live on the very next cameo, no restart."}),e.jsx("div",{className:"vh-admin-create",children:e.jsxs("button",{type:"button",className:"btn-ghost btn-small",onClick:()=>C(!0),disabled:!q,children:["✨ Create a new image of ",t.host_name||"the host"]})}),q&&e.jsx(Ns,{isOpen:k,onClose:()=>C(!1),showImageTypePicker:!0,initialImageType:"scene",avatar:q,avatarTracks:[],trackTitle:t.host_name||q.name||"the host",isLoading:V.busy,resultImages:V.result?.images,onResultDismiss:V.clearResult,onGenerate:g=>V.generate(g,{avatarId:o,avatarName:t.host_name||q.name})}),R.length>0?e.jsx("div",{className:"vh-admin-gallery",children:R.map(g=>e.jsx("button",{type:"button",className:`vh-admin-thumb ${z===g&&!x.trim()?"sel":""}`,onClick:()=>{m(_=>_===g?"":g),b("")},title:"Use this photo",children:e.jsx("img",{src:g,alt:""})},g))}):e.jsx("p",{className:"empty-state",children:"No gallery images found — paste an image URL below."}),e.jsxs("div",{className:"vh-admin-urlrow",children:[e.jsx("input",{type:"text",value:x,onChange:g=>b(g.target.value),placeholder:"…or paste an image URL (https:// or gs://)"}),e.jsx("button",{className:"btn-primary",onClick:()=>I(!0),disabled:B||!ie,children:B?"Replacing…":"Replace photo"})]}),D&&e.jsx("p",{className:`vh-admin-msg ${D.ok?"ok":"err"}`,children:D.text}),E&&e.jsx("div",{className:"vh-confirm-overlay",onClick:()=>I(!1),children:e.jsxs("div",{className:"vh-confirm",role:"dialog","aria-modal":"true",onClick:g=>g.stopPropagation(),children:[e.jsxs("h4",{className:"vh-confirm-title",children:["Replace ",t.host_name||"the host","'s on-camera face?"]}),e.jsx("div",{className:"vh-confirm-preview",children:e.jsx("img",{src:ie,alt:"New host face"})}),e.jsx("p",{className:"vh-confirm-body",children:"The old face is deleted from Anam (freeing its plan slot) and the next cameo uses the new one. The voice is untouched."}),e.jsxs("div",{className:"vh-confirm-actions",children:[e.jsx("button",{className:"btn-ghost",onClick:()=>I(!1),disabled:B,children:"Cancel"}),e.jsx("button",{className:"btn-primary",onClick:Q,disabled:B,children:B?"Replacing…":"Replace photo"})]})]})})]})}
39function os(){const[t,o]=s.useState([]),[c,v]=s.useState(null),[l,f]=s.useState([]),[R,L]=s.useState([]),[z,m]=s.useState(""),[x,b]=s.useState(""),[B,d]=s.useState(!0),[D,S]=s.useState(!1),[E,I]=s.useState(""),[q,j]=s.useState("");s.useEffect(()=>{ue(),re()},[]),s.useEffect(()=>{c&&J(c.station_id)},[c]);const[k,C]=s.useState(!1),[M,V]=s.useState([]),[X,ie]=s.useState([]),[Q,u]=s.useState({name:"",description:"",station_type:"genre",genre_id:"",host_avatar_id:"",narration_frequency:4}),[g,_]=s.useState(null),U=async()=>{C(!0);try{const[y,W]=await Promise.all([T("/api/taxonomy/genres"),T("/api/admin/avatars/list")]);V(y?.genres||(Array.isArray(y)?y:[])),ie(W?.avatars||(Array.isArray(W)?W:[]))}catch{}},te=async()=>{if(!Q.name.trim()){I("Name is required");return}if(Q.station_type==="genre"&&!Q.genre_id){I("Pick a genre");return}S(!0),I("");try{const y=await T("/api/admin/stations",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(Q)});if(!y.success)throw new Error(y.error||"Could not create station");if(g){const W=new FormData;W.append("cover",g);const xe=await T(`/api/admin/stations/${y.station.station_id}/cover`,{method:"POST",body:W});xe.success||I(xe.error||"Station saved, but the cover did not upload")}C(!1),u({name:"",description:"",station_type:"genre",genre_id:"",host_avatar_id:"",narration_frequency:4}),_(null),await ue(),j("Station created — run the nightly build (or the two populate scripts) to fill it"),setTimeout(()=>j(""),6e3)}catch(y){I(y.message)}finally{S(!1)}},ue=async()=>{try{const y=await T("/api/radio/stations");if(y.success){const W=y.stations.filter(be=>be.station_type!=="podcast");o(W);const xe=W.find(be=>be.slug==="hypetunes-featured");v(xe||W[0]||null)}}catch{I("Failed to load stations")}finally{d(!1)}},re=async()=>{try{const y=await T("/api/admin/tracks?limit=500");y.success&&L(y.tracks||[])}catch(y){console.error("Failed to load tracks:",y)}},J=async y=>{try{const W=await T(`/api/admin/station/${y}/tracks`);W.success&&f(W.tracks||[])}catch{I("Failed to load station tracks")}},je=async y=>{if(c){S(!0),I(""),j("");try{const W=await T(`/api/admin/station/${c.station_id}/tracks`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({trackId:y})});W.success?(j("Track added!"),J(c.station_id),setTimeout(()=>j(""),2e3)):I(W.error||"Failed to add track")}catch{I("Failed to add track")}finally{S(!1)}}},$=async y=>{if(c){S(!0),I("");try{const W=await T(`/api/admin/station/${c.station_id}/tracks/${y}`,{method:"DELETE"});W.success?J(c.station_id):I(W.error||"Failed to remove track")}catch{I("Failed to remove track")}finally{S(!1)}}},A=new Set(l.map(y=>y.track_id)),he=l.filter(y=>{if(!x)return!0;const W=x.toLowerCase();return y.title?.toLowerCase().includes(W)||y.avatar_name?.toLowerCase().includes(W)||y.genre?.toLowerCase().includes(W)}),ee=R.filter(y=>{if(A.has(y.track_id))return!1;if(!z)return!0;const W=z.toLowerCase();return y.title?.toLowerCase().includes(W)||y.avatar_name?.toLowerCase().includes(W)||y.genre?.toLowerCase().includes(W)});return B?e.jsx("div",{className:"loading-state",children:"Loading stations…"}):e.jsxs(e.Fragment,{children:[E&&e.jsx("div",{className:"error-message",children:E}),e.jsxs("div",{className:"station-manager-content",children:[e.jsxs("div",{className:"station-selector",children:[e.jsxs("h3",{children:["Select Station ",e.jsxs("button",{type:"button",className:"station-new-btn",onClick:U,children:[e.jsx(de,{name:"plus",size:12})," New station"]})]}),k&&ys.createPortal(e.jsx("div",{className:"station-create-overlay",onClick:()=>!D&&C(!1),role:"presentation",children:e.jsxs("div",{className:"station-create",role:"dialog","aria-modal":"true","aria-labelledby":"station-create-title",onClick:y=>y.stopPropagation(),children:[e.jsx("h3",{id:"station-create-title",children:"New station"}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"Title"}),e.jsx("input",{type:"text",value:Q.name,onChange:y=>u(W=>({...W,name:y.target.value})),placeholder:"Station name"})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"Type"}
39),e.jsxs("select",{value:Q.station_type,onChange:y=>u(W=>({...W,station_type:y.target.value})),children:[e.jsx("option",{value:"genre",children:"Genre"}),e.jsx("option",{value:"mood",children:"Mood"}),e.jsx("option",{value:"featured",children:"Featured"}),e.jsx("option",{value:"popular",children:"Popular"})]})]}),Q.station_type==="genre"&&e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"Genre"}),e.jsxs("select",{value:Q.genre_id,onChange:y=>u(W=>({...W,genre_id:y.target.value})),children:[e.jsx("option",{value:"",children:"Pick a genre…"}),M.map(y=>e.jsx("option",{value:y.id||y.genre_id,children:y.name},y.id||y.genre_id))]})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"Host"}),e.jsxs("select",{value:Q.host_avatar_id,onChange:y=>u(W=>({...W,host_avatar_id:y.target.value})),children:[e.jsx("option",{value:"",children:"No host"}),X.map(y=>e.jsx("option",{value:y.avatar_id,children:y.name},y.avatar_id))]})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"Description"}),e.jsx("input",{type:"text",value:Q.description,onChange:y=>u(W=>({...W,description:y.target.value})),placeholder:"One line, shown on the station tile"})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"Narration every N tracks"}),e.jsx("input",{type:"number",min:"0",max:"20",value:Q.narration_frequency,onChange:y=>u(W=>({...W,narration_frequency:y.target.value}))})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"Cover (16:9)"}),e.jsx("input",{type:"file",accept:"image/*",onChange:y=>_(y.target.files?.[0]||null)})]}),e.jsxs("div",{className:"station-create-actions",children:[e.jsx("button",{type:"button",className:"station-create-cancel",disabled:D,onClick:()=>C(!1),children:"Cancel"}),e.jsx("button",{type:"button",className:"station-create-save",disabled:D,onClick:te,children:D?"Creating…":"Create station"})]})]})}),document.body),e.jsx("div",{className:"station-buttons",children:t.map(y=>e.jsxs("button",{className:`station-btn ${c?.station_id===y.station_id?"active":""}`,onClick:()=>v(y),children:[e.jsx("span",{className:"station-name",children:y.name}),e.jsxs("span",{className:"station-count",children:[y.track_count," tracks"]})]},y.station_id))})]}),c&&e.jsxs("div",{className:"station-panels",children:[e.jsxs("div",{className:"panel current-tracks-panel",children:[e.jsxs("h3",{children:[c.name," ",e.jsxs("span",{className:"track-count",children:["(",he.length,"/",l.length," tracks)"]})]}),e.jsxs("div",{className:"search-box",children:[e.jsx(de,{name:"magnifying-glass",size:14}),e.jsx("input",{type:"text",value:x,onChange:y=>b(y.target.value),placeholder:"Search this station…"})]}),he.length===0?e.jsx("p",{className:"empty-state",children:l.length?"No matches":"No tracks in this station yet"}):e.jsx("ul",{className:"admin-track-list",children:he.map(y=>e.jsxs("li",{className:"admin-track-row",children:[e.jsx("img",{src:y.artwork_url,alt:"",className:"admin-track-art"}),e.jsxs("div",{className:"admin-track-meta",children:[e.jsx("strong",{children:y.title}),e.jsx("span",{children:y.avatar_name})]}),e.jsx("button",{className:"remove-btn",onClick:()=>$(y.track_id),disabled:D,title:"Remove from station",children:e.jsx(de,{name:"xmark",size:14})})]},y.track_id))})]}),e.jsxs("div",{className:"panel available-tracks-panel",children:[e.jsx("h3",{children:"Add Tracks"}),e.jsxs("div",{className:"search-box",children:[e.jsx(de,{name:"magnifying-glass",size:14}),e.jsx("input",{type:"text",value:z,onChange:y=>m(y.target.value),placeholder:"Search tracks…"})]}),e.jsx("ul",{className:"admin-track-list",children:ee.slice(0,50).map(y=>e.jsxs("li",{className:"admin-track-row",children:[e.jsx("img",{src:y.artwork_url,alt:"",className:"admin-track-art"}),e.jsxs("div",{className:"admin-track-meta",children:[e.jsx("strong",{children:y.title}),e.jsx("span",{children:y.avatar_name}),y.genre&&e.jsx("span",{className:"admin-genre-tag",children:y.genre})]}),e.jsx("button",{className:"add-btn",onClick:()=>je(y.track_id),disabled:D,title:"Add to station",children:e.jsx(de,{name:"plus",size:14})})]},y.track_id))}),ee.length>50&&e.jsxs("p",{className:"more-hint",children:["Showing 50 of ",ee.length,". Use search to find more."]})]})]}),c&&e.jsx(Ks,{station:c,onChanged:ue},`cover-${c.station_id}`),c?.host_avatar_id&&e.jsx(Qs,{station:c},c.station_id)]}),q&&e.jsx("div",{className:"success-message",children:q})]})}const Xs={title:"",type:"genre",description:"",genre:"",mood:"",topic_prompt:"",sort_order:0,cover_image_url:null},Zs=["technology","money","ownership","fame","collaboration","audie
39nce","craft","industry","promotion","identity","life"];function Ms({showMsg:t}){const[o,c]=s.useState(null),[v,l]=s.useState(!1),[f,R]=s.useState(!1),[L,z]=s.useState(""),[m,x]=s.useState(""),[b,B]=s.useState(""),[d,D]=s.useState(""),[S,E]=s.useState(!1),[I,q]=s.useState(!1),[j,k]=s.useState(!1),[C,M]=s.useState({title:"",brief:"",faultLine:"",note:"",subject:"",source:"manual"}),V=async($=!1)=>{l(!0);try{const A=$&&o?.pair?.length===2?[o.pair[0].avatar_id,o.pair[1].avatar_id].sort((ee,y)=>ee-y).join(":"):"",he=await T(`/api/podcast/session/casting${A?`?avoid=${encodeURIComponent(A)}`:""}`);he.error?t(he.error,!0):(c(he),B(""),D(""))}catch{t("Failed to load casting",!0)}finally{l(!1)}};s.useEffect(()=>{V()},[]);const X=o?.eligible||[],ie=$=>X.find(A=>String(A.avatar_id)===String($))||null,Q=!!(b||d),u=b?ie(b):o?.pair?.[0]||null,g=d?ie(d):o?.pair?.[1]||null,_=!!(u&&g&&u.avatar_id===g.avatar_id),U=u&&g&&!_?[u,g]:null,te=async()=>{if(!U)return t("Cast a pair first",!0);q(!0);try{const $=await T("/api/podcast/session/topics/draft",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({avatarAId:U[0].avatar_id,avatarBId:U[1].avatar_id})});if($.error)return t($.error,!0);M({title:$.draft.title,brief:$.draft.brief,faultLine:$.draft.faultLine,note:$.draft.note,subject:$.draft.subject||"",source:"auto"}),E(!0)}catch{t("Failed to draft a topic",!0)}finally{q(!1)}},ue=async()=>{if(!C.title.trim()||!C.brief.trim())return t("Title and brief are both required",!0);k(!0);try{const $=await T("/api/podcast/session/topics",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({title:C.title,brief:C.brief,subject:C.subject||null,source:C.source})});if($.error)return t($.error,!0);t(`✅ Topic saved — ${$.topic.note}${$.topic.subject?` · ${$.topic.subject}`:""}`),E(!1),M({title:"",brief:"",faultLine:"",note:"",subject:"",source:"manual"}),await V(),x(String($.topic.topic_id))}catch{t("Failed to save topic",!0)}finally{k(!1)}},re=async()=>{if(!U)return t(_?"Pick two different artists":"Cast a pair first",!0);const $=`${U[0].name} × ${U[1].name}`;if(confirm(`Record a Session episode — ${$}? This generates dialogue, audio and cover art.`)){R(!0);try{const A=await T("/api/podcast/session/generate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({avatarAId:U[0].avatar_id,avatarBId:U[1].avatar_id,sessionTopicId:m?Number(m):null,endingMode:L||null})});A.error?t(A.error,!0):(t(`✅ ${A.message}: ${A.pair.map(he=>he.name).join(" × ")} on "${A.topic.title}"`),V())}catch{t("Failed to produce session",!0)}finally{R(!1)}}},J=o?.topicHealth,je=!!(o?.topics||[]).length;return e.jsxs("div",{className:"session-panel",children:[e.jsx("div",{className:"session-head",children:e.jsxs("div",{className:"session-head-text",children:[e.jsx("h3",{children:"🎙️ The Session"}),e.jsx("p",{className:"hint",children:"Two artists, one subject, no referee. The system casts a pair with real contrast, they pick a topic, and they argue about it — ending in agreement, in a stalemate, or nowhere at all."})]})}),J&&e.jsxs("div",{className:"session-health",children:[e.jsxs("span",{className:`sh-pill ${J.sharp?"good":""}`,children:[J.sharp," sharp"]}),e.jsxs("span",{className:"sh-pill",children:[J.workable," workable"]}),e.jsxs("span",{className:`sh-pill ${J.tooAbstract?"warn":""}`,children:[J.tooAbstract," too abstract"]}),J.tooAbstract>J.sharp&&e.jsx("span",{className:"sh-note",children:'Bucket is drifting abstract. Specific, slightly unfair questions make better episodes — "when is it fine to not write your own hook" beats "authenticity in music."'}),J.subjectWarning&&e.jsx("span",{className:"sh-note",children:J.subjectWarning}),!!(J.recentSubjects||[]).length&&e.jsxs("span",{className:"sh-recent",children:["Last episodes were about: ",J.recentSubjects.join(", ")," — the shortlist is steering away from those."]})]}),o&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"session-cast",children:[e.jsxs("div",{className:"session-label",children:["Cast · ",o.eligibleCount," eligible artists"]}),U?e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"session-pair",children:U.map($=>e.jsxs("div",{className:"session-artist",children:[$.profile_image_url?e.jsx("img",{src:$.profile_image_url,alt:$.name}):e.jsx("div",{className:"session-artist-blank"}),e.jsxs("div",{className:"session-artist-info",children:[e.jsx("div",{className:"session-artist-name",children:$.name}),e.jsx("div",{className:"session-artist-meta",children:$.main_genre||"—"}),e.jsxs("div",{className:"session-artist-meta",children:[$.track_count," tracks"]})]})]},$.avatar_id))}),e.jsx("p",{className:"session-reason",children:Q?"Hand-picked cast — re-cast to go back to the system's contrast scoring.":o.pairReason})]}):e.jsx("p",{className:"hint",children:_?"Both slots are the same artist — pick two different ones.":o.pairReason}),X.length>=2&&e.jsxs("div",{className:"session-picks",children:[[{label:"Artist A",value:b,set:B,auto:o.pair?.[0]||null},{label:"Artist B",value:d,set:D,auto:o.pair?.[1]||null}].map($=>e.jsxs("label",{className:"session-pick",children:[e.jsx("span",{className:"session-pick-label",children:$.label}),e.jsxs("select",{value:$.value||($.auto?String($.auto.avatar_id):""),onChange:A=>$.set(A.target.value),children:[!$.auto&&e.jsx("option",{value:"",children:"— choose an artist —"}),X.map(A=>e.jsxs("option",{value:A.avatar_id,children:[A.name,A.main_genre?` · ${A.main_genre}`:""," · ",A.track_count," tracks"]},A.avatar_id))]})]},$.label)),Q&&e.jsx("button",{className:"btn-ghost btn-small",onClick:()=>{B(""),D("")},children:"Clear"})]})]}),e.jsxs("div",{className:"session-topic-block",children:[e.jsxs("div",{className:"session-topic-head",children:[e.jsx("label",{children:"Topic"}),e.jsxs("div",{className:"session-topic-actions",children:[e.jsx("button",{className:"btn-ghost btn-small",onClick:te,disabled:I||!o.pair,children:I?"Reading both artists…":"✨ Auto-write for this pair"}),e.jsx("button",{className:"btn-ghost btn-small",onClick:()=>{E($=>!$),M($=>({...$,source:"manual"}))},children:S?"Cancel":"+ New topic"})]})]}),S?e.jsxs("div",{className:"session-composer",children:[e.jsx("input",{value:C.title,onChange:$=>M(A=>({...A,title:$.target.value})),placeholder:"The question — specific enough that you can't answer it without taking a side"}),e.jsx("textarea",{rows:5,value:C.brief,onChange:$=>M(A=>({...A,brief:$.target.value})),placeholder:"The framing sent to both artists: the concrete situation, what's at stake, and where reasonable people split. Don't name a winner."}),e.jsxs("div",{className:"session-subject-row",children:[e.jsx("label",{children:"Subject"}),e.jsxs("select",{value:C.subject,onChange:$=>M(A=>({...A,subject:$.target.value})),children:[e.jsx("option",{value:"",children:"Work it out from the question"}),Zs.map($=>e.jsx("option",{value:$,children:$},$))]}),e.jsx("span",{className:"hint",children:"Keeps the bucket from filling with one subject."})]}),C.faultLine&&e.jsxs("p",{className:"session-faultline",children:["Fault line: ",C.faultLine]}),C.note&&e.jsxs("p",{className:"hint",children:["Scored: ",C.note]}),e.jsx("div",{className:"session-composer-actions",children:e.jsx("button",{className:"btn-primary",onClick:ue,disabled:j,children:j?"Saving…":"Save topic"})})]}):e.jsxs("select",{value:m,onChange:$=>x($.target.value),children:[e.jsx("option",{value:"",children:"Let the pair choose (from the shortlist)"}),(o.topics||[]).map($=>e.jsxs("option",{value:$.topic_id,children:[$.title," — ",$.note,$.subjectTag?` · ${$.subjectTag}`:""]},$.topic_id))]})]}),!S&&e.jsxs("div",{className:"session-launch",children:[e.jsxs("div",{className:"session-ending",children:[e.jsx("label",{children:"Ending"}),e.jsxs("select",{value:L,onChange:$=>z($.target.value),children:[e.jsx("option",{value:"",children:"Random"}),e.jsx("option",{value:"agree",children:"They agree (earned)"}),e.jsx("option",{value:"agree_to_disagree",children:"Agree to disagree"}),e.jsx("option",{value:"unresolved",children:"Unresolved"})]})]}),e.jsx("button",{className:"btn-topic-gen",onClick:re,disabled:f||!o.pair||!je,children:f?"Recording…":"🎙️ Record an episode"})]}),!je&&!S&&e.jsx("p",{className:"hint",children:"No topics yet — write one, or auto-write it for this pair."})]})]})}const ls={key:"",name:"",set_prompt:"",set_sfx:"",fit_json:'{"genres":[],"mood":"","season":null}',sort_order:0,use_logo:!1};function et({showMsg:t}){const[o,c]=s.useState([]),[v,l]=s.useState(!0),[f,R]=s.useState(ls),[L,z]=s.useState(!1),[m,x]=s.useState(null),[b,B]=s.useState(null),[d,D]=s.useState(null),[S,E]=s.useState(null),I=async()=>{try{const u=await T("/api/podcast/booth/sets");if(u.error)return t(u.error,!0);c(u.sets||[]),l(u.migrated!==!1)}catch{t("Failed to load the rooms",!0)}};s.useEffect(()=>{I()},[]);const q=async u=>
39{if(!(u==="enhance"&&!f.set_prompt.trim())){B(u);try{const g=await T("/api/podcast/booth/sets/write",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({mode:u,idea:f.set_prompt})});g.error?t(g.error,!0):R(_=>({..._,set_prompt:g.set_prompt}))}catch{t("Could not write the room",!0)}finally{B(null)}}},j=async u=>{if(!(u==="enhance"&&!f.set_sfx.trim())){if(u==="auto"&&!f.set_prompt.trim())return t("Describe the room first",!0);D(u);try{const g=await T("/api/admin/speaking-scenes/write-sfx",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({mode:u,idea:f.set_sfx,scene_prompt:f.set_prompt})});g.error?t(g.error,!0):R(_=>({..._,set_sfx:g.sfx_prompt}))}catch{t("Could not write the room tone",!0)}finally{D(null)}}},k=async()=>{if(!f.name.trim()||!f.set_prompt.trim())return t("A name and a description are required",!0);let u=null;try{u=f.fit_json.trim()?JSON.parse(f.fit_json):null}catch{return t("Fit JSON is not valid JSON",!0)}z(!0);try{const g=await T("/api/podcast/booth/sets",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key:f.key,name:f.name,setPrompt:f.set_prompt,setSfx:f.set_sfx||null,fit:u,sortOrder:Number(f.sort_order)||0,useLogo:!!f.use_logo})});g.error?t(g.error,!0):(t("Room added — build it to make it real"),R(ls),I())}catch{t("Failed to add the room",!0)}finally{z(!1)}},C=async u=>{if(!(u.reference_image_url&&!confirm(`Rebuild ${u.name}?
40
41Episodes already filmed keep the old room, so the show will have a visible before and after.`))){x(u.booth_id);try{const g=await T(`/api/podcast/booth/sets/${u.booth_id}/image`,{method:"POST"});g.error?t(g.error,!0):(t(`${u.name} is built`),I())}catch{t("Failed to build the room",!0)}finally{x(null)}}},M=async u=>{try{const g=await T(`/api/podcast/booth/sets/${u.booth_id}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({useLogo:!u.use_logo})});g.error?t(g.error,!0):(t(u.use_logo?"Logo off — rebuild to apply":"Logo on — rebuild to apply"),I())}catch{t("Failed to save",!0)}},V=async(u,g)=>{if(g){if(g.size>25*1024*1024)return t("Image must be under 25MB",!0);E(u.booth_id);try{const _=new FormData;_.append("image",g);const U=await T(`/api/podcast/booth/sets/${u.booth_id}/reference`,{method:"POST",body:_});U.error?t(U.error,!0):(t("Prop added — rebuild the room to dress it in"),I())}catch{t("Upload failed",!0)}finally{E(null)}}},X=async(u,g)=>{const _=(u.reference_urls||[]).filter(U=>U!==g);try{const U=await T(`/api/podcast/booth/sets/${u.booth_id}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({referenceUrls:_})});U.error?t(U.error,!0):(t("Prop removed — rebuild to apply"),I())}catch{t("Failed to save",!0)}},ie=async u=>{try{const g=await T(`/api/podcast/booth/sets/${u.booth_id}/default`,{method:"POST"});g.error?t(g.error,!0):(t(`${u.name} is the default room`),I())}catch{t("Failed to set the default",!0)}},Q=async u=>{try{const g=await T(`/api/podcast/booth/sets/${u.booth_id}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({isActive:!u.is_active})});g.error?t(g.error,!0):I()}catch{t("Failed to save",!0)}};return v?e.jsxs("div",{className:"booth-rooms",children:[e.jsx("div",{className:"session-label",children:"The rooms"}),e.jsxs("p",{className:"hint",children:["Every episode is filmed in one of these. A room only holds continuity once it has been ",e.jsx("strong",{children:"built"})," — the photograph is what each episode references, not the words."]}),e.jsx("div",{className:"booth-room-list",children:o.map(u=>e.jsxs("div",{className:`booth-room ${u.is_active?"":"is-off"}`,children:[e.jsx("div",{className:"booth-set-preview",children:u.image_url?e.jsx("img",{src:u.image_url,alt:u.name}):e.jsx("div",{className:"booth-set-blank",children:e.jsx(de,{name:"video",size:18})})}),e.jsxs("div",{className:"booth-room-info",children:[e.jsxs("div",{className:"booth-room-head",children:[e.jsx("span",{className:"booth-room-name",children:u.name}),e.jsx("button",{type:"button",className:"booth-room-slug",title:`--booth ${u.key}  ·  click to copy`,onClick:()=>navigator.clipboard?.writeText(u.key),children:u.key}),u.is_default&&e.jsx("span",{className:"sh-pill good",children:"default"}),!u.reference_image_url&&e.jsx("span",{className:"sh-pill warn",children:"not built"})]}),e.jsx("p",{className:"booth-room-prompt",children:u.set_prompt}),e.jsxs("div",{className:"booth-dress",children:[e.jsxs("label",{className:"booth-dress-logo",children:[e.jsx("input",{type:"checkbox",checked:!!u.use_logo,onChange:()=>M(u)}),e.jsx("span",{children:"HypeTunes logo in the set"})]}),e.jsxs("div",{className:"booth-props",children:[(u.reference_urls||[]).map((g,_)=>e.jsxs("span",{className:"booth-prop",children:[u.reference_signed?.[_]?e.jsx("img",{src:u.reference_signed[_],alt:`Prop ${_+1}`}):e.jsx("span",{className:"booth-prop-blank",children:e.jsx(de,{name:"image",size:12})}),e.jsx("button",{type:"button",className:"booth-prop-x",title:"Remove this prop",onClick:()=>X(u,g),children:e.jsx(de,{name:"xmark",size:10})})]},g)),e.jsxs("label",{className:"booth-prop booth-prop-add",title:"Add artwork or an object to this room",children:[S===u.booth_id?"…":e.jsx(de,{name:"plus",size:12}),e.jsx("input",{type:"file",accept:"image/*",hidden:!0,disabled:S===u.booth_id,onChange:g=>{V(u,g.target.files?.[0]),g.target.value=""}})]})]}),(u.use_logo||(u.reference_urls||[]).length>0)&&u.reference_image_url&&e.jsx("span",{className:"booth-dress-note",children:"Rebuild to apply"})]}),e.jsxs("div",{className:"booth-room-actions",children:[e.jsx("button",{className:"btn-ghost btn-small",onClick:()=>C(u),disabled:m===u.booth_id,children:m===u.booth_id?"Building…":u.reference_image_url?"↻ Rebuild":"
41🎬 Build the room"}),!u.is_default&&e.jsx("button",{className:"btn-ghost btn-small",onClick:()=>ie(u),children:"Make default"}),e.jsx("button",{className:"btn-ghost btn-small",onClick:()=>Q(u),children:u.is_active?"Retire":"Restore"})]})]})]},u.booth_id))}),e.jsxs("div",{className:"scene-card scene-card-new",children:[e.jsx("div",{className:"scene-card-title",children:"➕ Add a room"}),e.jsxs("div",{className:"scene-row",children:[e.jsxs("label",{className:"scene-f",children:[e.jsx("span",{children:"Key"}),e.jsx("input",{value:f.key,onChange:u=>R(g=>({...g,key:u.target.value})),placeholder:"late_night"})]}),e.jsxs("label",{className:"scene-f",children:[e.jsx("span",{children:"Name"}),e.jsx("input",{value:f.name,onChange:u=>R(g=>({...g,name:u.target.value})),placeholder:"Late night, one lamp"})]}),e.jsxs("label",{className:"scene-f scene-f-xs",children:[e.jsx("span",{children:"Sort"}),e.jsx("input",{type:"number",value:f.sort_order,onChange:u=>R(g=>({...g,sort_order:u.target.value}))})]})]}),e.jsxs("label",{className:"scene-f scene-f-full",children:[e.jsxs("span",{className:"scene-f-labelrow",children:[e.jsx("span",{children:"The room — empty, nobody in it"}),e.jsxs("span",{className:"scene-write-btns",children:[e.jsx("button",{type:"button",className:"scene-mini",disabled:!!b||!f.set_prompt.trim(),onClick:()=>q("enhance"),title:"Improve what you typed into a polished room",children:b==="enhance"?"…":"✨ Enhance"}),e.jsx("button",{type:"button",className:"scene-mini",disabled:!!b,onClick:()=>q("auto"),title:"Invent a fresh room",children:b==="auto"?"…":"Auto-write"})]})]}),e.jsx("textarea",{rows:3,value:f.set_prompt,onChange:u=>R(g=>({...g,set_prompt:u.target.value})),placeholder:"Type a rough idea then hit ✨ Enhance — e.g. 'a stripped-back room with one lamp' — or Auto-write to seed one. Describe only the room; nobody is in it."})]}),e.jsxs("label",{className:"scene-f scene-f-full",children:[e.jsxs("span",{className:"scene-f-labelrow",children:[e.jsx("span",{children:"Room tone — ambient only (no music, no speech)"}),e.jsxs("span",{className:"scene-write-btns",children:[e.jsx("button",{type:"button",className:"scene-mini",disabled:!!d||!f.set_sfx.trim(),onClick:()=>j("enhance"),title:"Polish the sound you typed",children:d==="enhance"?"…":"✨ Enhance"}),e.jsx("button",{type:"button",className:"scene-mini",disabled:!!d,onClick:()=>j("auto"),title:"Derive it from the room above",children:d==="auto"?"…":"Auto-write"})]})]}),e.jsx("textarea",{rows:2,value:f.set_sfx,onChange:u=>R(g=>({...g,set_sfx:u.target.value})),placeholder:"The quiet of the room, laid under the narration. Auto-write reads the room above."})]}),e.jsxs("label",{className:"scene-f scene-f-full",children:[e.jsxs("span",{children:["Fit JSON — ",'{ "genres":[], "mood":"", "season":null }']}),e.jsx("textarea",{className:"scene-mono",rows:2,value:f.fit_json,onChange:u=>R(g=>({...g,fit_json:u.target.value}))})]}),e.jsxs("label",{className:"booth-dress-logo",style:{margin:"2px 0 10px"},children:[e.jsx("input",{type:"checkbox",checked:!!f.use_logo,onChange:u=>R(g=>({...g,use_logo:u.target.checked}))}),e.jsx("span",{children:"HypeTunes logo in the set"})]}),e.jsx("div",{className:"scene-actions",children:e.jsx("button",{className:"scene-btn scene-btn-save",onClick:k,disabled:L,children:L?"Adding…":"Add room"})})]})]}):e.jsx("p",{className:"hint",children:"The room library arrives with migration 301. Until then the show uses the single room described in its config."})}function st({showMsg:t}){const[o,c]=s.useState(null),[v,l]=s.useState(!1),[f,R]=s.useState(!1),[L,z]=s.useState(!1),[m,x]=s.useState(5),[b,B]=s.useState({title:"",summary:"",anchor:"",verbatim:!1,emotionTags:!1}),[d,D]=s.useState(null),[S,E]=s.useState([]),[I,q]=s.useState(""),[j,k]=s.useState(""),[C,M]=s.useState(null),[V,X]=s.useState(null),[ie,Q]=s.useState(null),[u,g]=s.useState(null),[_,U]=s.useState(null),[te,ue]=s.useState(null),[re,J]=s.useState(""),[je,$]=s.useState(null),[A,he]=s.useState(null),ee=async(r=!1,w=null)=>{l(!0),he(null);try{const ae=w?`?handle=${encodeURIComponent(String(w).trim().replace(/^@/,""))}`:r&&o?.artist?`?avoid=${o.artist.avatar_id}`:"",oe=await T(`/api/podcast/booth/casting${ae}`);oe.error?(he(oe.error),t(oe.error,!0)):(c(oe),oe.beatCount&&x(oe.beatCount),B({title:"",summary:"",anchor:"",verbatim:!1,emotionTags:!1}))}catch(ae){he(ae?.message||"Could not reach the casting endpoint"),t("Failed to load casting",!0)}finally{l(!1)}},y=async()=>{try{const r=await T("/api/podcast/booth/sets");r.error||E((r.sets||[]).filter(w=>w.is_active))}catch{}};s.useEffect(()=>{ee(),y()},[]);const W=async r=>{const w=r.status==="published"?`Rebuild "${r.title}"?
42
43All ${r.beats_done||0} takes are already filmed, so nothing is re-rendered and no credits are charged — the episode is simply cut together again, using the current walk-in.`:`Finis
43h "${r.title}"?
44
45${r.beats_done||0} of ${r.beats_total||"?"} takes are already filmed and paid for — only the missing ones are rendered.`;if(confirm(w)){M(r.episode_id);try{const ae=await T("/api/podcast/booth/generate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({resumeEpisodeId:r.episode_id})});ae.error?t(ae.error,!0):(t(`✅ ${ae.message}`),ee())}catch{t("Failed to finish the episode",!0)}finally{M(null)}}},xe=async r=>{if(confirm(`Re-shoot "${r.title}"?
46
47The script, the outfit and the room stay exactly as they are — only the ${r.shots?.length||0} keyframes are generated again. No credits are charged.`)){ue(r.episode_id);try{const w=await T(`/api/podcast/booth/episodes/${r.episode_id}/reshoot`,{method:"POST"});w.error?t(w.error,!0):(t(`✅ ${w.message}`),ee())}catch{t("Failed to re-shoot the episode",!0)}finally{ue(null)}}},be=async r=>{U(r.episode_id);try{const w=await T(`/api/podcast/booth/episodes/${r.episode_id}/cover`,{method:"POST"});w.error?t(w.error,!0):(t(`✅ ${w.message}`),ee())}catch{t("Failed to make the cover",!0)}finally{U(null)}},Z=async(r,w)=>{if(!(!w&&!confirm(`Take "${r.title}" down?
48
49It leaves the channel and every listing immediately. The video, the audio and the takes are all kept — you can put it back any time.`))){g(r.episode_id);try{const ae=await T(`/api/podcast/booth/episodes/${r.episode_id}/publish`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({live:w})});ae.error?t(ae.error,!0):(t(`✅ ${ae.message}`),ee())}catch{t("Failed to change the episode",!0)}finally{g(null)}}},ve=async r=>{if(confirm(`Re-run "${r.title}"?
50
51A NEW episode is created with the same script and the same ${r.shots?.length||0} shots. Nothing is written again and no keyframe is generated again.
52
53Filming it is a separate press, and costs a full set of takes. This episode is left exactly as it is.`)){Q(r.episode_id);try{const w=await T(`/api/podcast/booth/episodes/${r.episode_id}/rerun`,{method:"POST"});w.error?t(w.error,!0):(t(`✅ ${w.message}`),ee())}catch{t("Failed to set up the re-run",!0)}finally{Q(null)}}},Te=async(r,w)=>{if(w){X(r.episode_id);try{const ae=new FormData;ae.append("clip",w);const oe=await T(`/api/podcast/booth/episodes/${r.episode_id}/arrival`,{method:"POST",body:ae});oe.error?t(oe.error,!0):(t(`✅ ${oe.message}`),ee())}catch{t("Failed to replace the walk-in",!0)}finally{X(null)}}},$e=async r=>{if(confirm(`Remove the walk-in from "${r.title}"?
54
55The episode will open on the artist instead. The clip itself is kept.`)){X(r.episode_id);try{const w=await T(`/api/podcast/booth/episodes/${r.episode_id}/arrival`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:null})});w.error?t(w.error,!0):(t(`✅ ${w.message}`),ee())}catch{t("Failed to remove the walk-in",!0)}finally{X(null)}}},N=async(r,w)=>{const ae=`${r.episode_id}:${w}`;$(ae);try{const oe=await T(`/api/podcast/booth/episodes/${r.episode_id}/keyframe/${w}`,{method:"POST"});oe.error?t(oe.error,!0):(t(`✅ ${oe.message}`),ee())}catch{t("Failed to re-shoot that frame",!0)}finally{$(null)}},H=async(r,w,ae)=>{const oe=confirm(`Re-film take ${w} (${ae}) of "${r.title}"?
56
57This costs ONE take. The narration is re-used, so the performance stays continuous.
58
59OK = also re-roll the frame it animates from (a second generation).
60Cancel = keep the frame, just re-film the motion.`);if(!confirm(`Go ahead and re-film take ${w}${oe?" with a new frame":""}?`))return;const Se=`${r.episode_id}:take:${w}`;$(Se);try{const a=await T(`/api/podcast/booth/episodes/${r.episode_id}/take/${w}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({newKeyframe:oe})});a.error?t(a.error,!0):(t(`✅ ${a.message}`),ee())}catch{t("Failed to re-film that take",!0)}finally{$(null)}},ce=async()=>{if(!o?.artist)return t("Cast an artist first",!0);z(!0);try{const r=await T("/api/podcast/booth/subject",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({avatarId:o.artist.avatar_id})});if(r.error)return t(r.error,!0);B({title:r.subject.title,summary:r.subject.summary,anchor:r.subject.anchor||"",verbatim:!1,emotionTags:!1})}catch{t("Failed to find a story",!0)}finally{z(!1)}},Re=s.useCallback(async(r,w,ae)=>{if(!String(r||"").trim()){D(null);return}try{const oe=await T("/api/podcast/booth/verbatim-preview",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({text:r,emotionTags:w,takes:ae})});D(oe.error?null:{...oe,from:r,withTags:w,forTakes:ae})}catch{D(null)}},[]);s.useEffect(()=>{if(!b.verbatim){D(null);return}const r=setTimeout(()=>Re(b.summary,b.emotionTags,m),b.emotionTags?1100:400);return()=>clearTimeout(r)},[b.verbatim,b.emotionTags,b.summary,m,Re]);const ge=async(r=!1)=>
60{if(!o?.artist)return t("Cast an artist first",!0);if(!r&&!o.artist.affordable)return t("This artist's owner cannot cover the render",!0);const w=b.verbatim&&d?.beats?.length?d.beats.length:m,ae=(o.cost?.total||0)/Math.max(1,m),oe=Math.round(ae*w);if(b.verbatim&&!String(b.summary||"").trim())return t("Verbatim is on but there is no text to film — paste what they said, or turn it off",!0);if(b.verbatim&&d&&d.intact===!1)return t("That text cannot be cut safely — see the note under the box",!0);const Se=r?`Prepare an episode for ${o.artist.name}?
61
62${b.verbatim?`Films THEIR EXACT WORDS in ${w} takes — nothing is rewritten.`:`Writes the story and builds ${w} shots.`} No video is rendered and nothing is charged — you can film it later.`:`Record ${o.artist.name} in The Booth?
63
64${b.verbatim?`THEIR EXACT WORDS, cut into ${w} takes — no model rewrites anything.
65
66`:""}This renders ${w} camera angles and ${w} lip-synced takes, and charges about ${oe} credits to ${o.artist.name}'s owner. It takes several minutes.`;if(confirm(Se)){R(!0);try{const a=await T("/api/podcast/booth/generate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({avatarId:o.artist.avatar_id,beatCount:m,boothId:I||null,signOff:j===""?null:j==="yes",previewOnly:r,subject:b.title.trim()||b.summary.trim()?{title:b.title,summary:b.verbatim&&b.emotionTags&&d?.tagged&&d.from===b.summary?d.text:b.summary,anchor:b.anchor||"",verbatim:!!b.verbatim,emotionTags:!!(b.verbatim&&b.emotionTags)&&!(d?.tagged&&d.from===b.summary)}:null})});a.error?t(a.error,!0):(t(a.prepared?`✅ ${a.message}`:`✅ ${a.message} — ${a.beats} beats, ${a.creditsSpent} credits`),ee())}catch{t("Failed to produce the episode",!0)}finally{R(!1)}}}
66,pe=o?.artist;return e.jsxs("div",{className:"session-panel",children:[e.jsx("div",{className:"session-head",children:e.jsxs("div",{className:"session-head-text",children:[e.jsx("h3",{children:"🎬 The Booth"}),e.jsx("p",{className:"hint",children:"One artist alone in a room, telling the story of something that happened to them. No host, no questions. The subject comes out of their own history, and the episode is video — a lip-synced take per beat, cut between camera angles."})]})}),!!(o?.unfinished||[]).length&&e.jsxs("div",{className:"booth-unfinished",children:[e.jsx("div",{className:"session-label",children:"Recent episodes"}),o.unfinished.map(r=>e.jsxs("div",{className:"booth-unfinished-row",children:[e.jsxs("div",{className:"booth-unfinished-info",children:[e.jsx("span",{className:"booth-room-name",children:r.title}),e.jsxs("span",{className:"session-artist-meta",children:[r.artist_name||"unknown artist"," · ",r.beats_done||0,"/",r.beats_total||"?"," takes filmed",r.status==="published"?" · published":"",r.status==="failed"?" · stopped":"",r.error&&r.status!=="published"?` · ${String(r.error).slice(0,60)}`:""]})]}),e.jsxs("div",{className:"booth-unfinished-actions",children:[!r.beats_done&&e.jsx("button",{className:"btn-ghost btn-small",onClick:()=>xe(r),disabled:te===r.episode_id||C===r.episode_id,title:"Generate the keyframes again — same script, same outfit, same room. No credits.",children:te===r.episode_id?"Re-shooting…":"↻ Re-shoot"}),e.jsx("button",{className:"btn-ghost btn-small",onClick:()=>be(r),disabled:_===r.episode_id,title:"Generate the episode cover from the establishing shot — 1:1 and 16:9. One image generation.",children:_===r.episode_id?"Making…":"🖼 Cover"}),r.status==="published"&&e.jsx("button",{className:"btn-ghost btn-small",onClick:()=>Z(r,!1),disabled:u===r.episode_id,title:"Remove it from the channel and every listing. The film is kept.",children:u===r.episode_id?"Taking down…":"⏻ Unpublish"}),r.status!=="published"&&r.video_url&&e.jsx("button",{className:"btn-ghost btn-small",onClick:()=>Z(r,!0),disabled:u===r.episode_id,title:"Put it back on the channel.",children:u===r.episode_id?"Publishing…":"⏻ Publish"}),r.beats_done>0&&e.jsx("button",{className:"btn-ghost btn-small",onClick:()=>ve(r),disabled:ie===r.episode_id,title:"Film this script again as a new episode — same shots, same words. Costs a new set of takes.",children:ie===r.episode_id?"Setting up…":"⎘ Re-run"}),e.jsx("button",{className:"btn-ghost btn-small",onClick:()=>W(r),disabled:C===r.episode_id||te===r.episode_id,title:r.status==="published"?"Cut the episode together again from the takes and the current walk-in. No credits.":"Film the takes that are still missing, then assemble.",children:C===r.episode_id?r.status==="published"?"Rebuilding…":"Filming…":r.status==="published"?"⟲ Rebuild":"▶ Finish it"})]}),r.cover_preview&&e.jsxs("div",{className:"booth-walkin",children:[e.jsx("span",{className:"booth-walkin-label",children:"Cover"}),e.jsx("img",{className:"booth-cover-thumb",src:r.cover_preview,alt:""}),r.cover_wide_preview&&e.jsx("img",{className:"booth-cover-thumb booth-cover-thumb--wide",src:r.cover_wide_preview,alt:""})]}),e.jsxs("div",{className:"booth-walkin",children:[e.jsx("span",{className:"booth-walkin-label",children:"Walk-in"}),r.arrival_preview?e.jsx("video",{className:"booth-walkin-clip",src:r.arrival_preview,controls:!0,muted:!0,playsInline:!0,preload:"metadata"}):e.jsx("span",{className:"session-artist-meta",children:"none — the episode opens on the artist"}),e.jsxs("div",{className:"booth-walkin-actions",children:[e.jsxs("label",{className:"btn-ghost btn-small",style:{cursor:"pointer"},children:[V===r.episode_id?"Uploading…":"↑ Replace",e.jsx("input",{type:"file",accept:"video/*",style:{display:"none"},disabled:V===r.episode_id,onChange:w=>{Te(r,w.target.files?.[0]),w.target.value=""}})]}),r.arrival_preview&&e.jsx("button",{className:"btn-ghost btn-small",onClick:()=>$e(r),disabled:V===r.episode_id,title:"Open on the artist instead. The clip is kept.",children:"✕ Remove"})]})]}),r.shots?.length>0&&e.jsxs("div",{className:"booth-shots",children:[(r.outfit||r.ending_note)&&e.jsxs("div",{className:"booth-shot-meta",children:[r.outfit&&e.jsxs("span",{children:[e.jsx("strong",{children:"Wearing:"})," ",r.outfit]}),e.jsxs("span",{children:[e.jsx("strong",{children:"Ending:"})," ",r.sign_off?"they close it out":"stops mid-thought",r.ending_note?` — “${r.ending_note}”`:""]})]}),r.shots.map(w=>e.jsxs("div",{className:"booth-beat",children:[e.jsxs("a",{className:"booth-shot",href:w.url||void 0,target:"_blank",rel:"noopener noreferrer",title:`Beat ${w.beat} — ${w.angle}`,children:[w.url?e.jsx("img",{src:w.url,alt:`Beat ${w.beat}, ${w.angle}`,loading:"lazy"}):e.jsx("span",{className:"booth-shot-blank",children:e.jsx(de,{name:"image",size:12})}),e.jsx("span",{className:"booth-shot-tag",children:w.beat})]}),!w.filmed&&e.jsx("button",{type:"button",className:"booth-shot-regen",title:`Re-shoot beat ${w.beat} — this frame only`,disabled:je===`${r.episode_id}:${w.beat}`,onClick:()=>N(r,w.beat),children:je===`${r.episode_id}:${w.beat}`?"…":"↻"}),w.filmed&&e.jsx("button",{type:"button",className:"booth-shot-regen booth-shot-refilm",title:`Re-film take ${w.beat} — costs one take, narration re-used`,disabled:je===`${r.episode_id}:take:${w.beat}`,onClick:()=>H(r,w.beat,w.angle),children:je===`${r.episode_id}:take:${w.beat}`?"…":"🎬"}),e.jsxs("div",{className:"booth-beat-text",children:[e.jsx("span",{className:"booth-beat-angle",children:w.role||w.angle}),e.jsx("p",{children:w.text||e.jsx("em",{children:"no line recorded"})}),w.take_preview&&e.jsxs("a",{className:"booth-beat-take",href:w.take_preview,target:"_blank",rel:"noopener noreferrer",children:[e.jsx(de,{name:"play",size:10})," watch take",w.reshot_at?" · re-shot":""]})]})]},w.beat))]})]},r.episode_id))]}),A&&e.jsxs("div",{className:"booth-cast-error",children:[e.jsx("strong",{children:"Casting could not load."})," ",A,e.jsx("div",{className:"hint",children:"Everything below — the cast, the story and the Film button — is hidden until this succeeds. Common causes: the API has not been restarted since a migration, or no artist currently meets the prerequisites."})]}),o&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"session-health",children:[e.jsxs("span",{className:"sh-pill good",children:[o.affordableCount," can be cast"]}),e.jsxs("span",{className:"sh-pill",children:[o.eligibleCount," eligible"]}),e.jsxs("span",{className:"sh-pill",children:["~",o.cost?.total||0," credits an episode"]}),o.affordableCount<o.eligibleCount&&e.jsxs("span",{className:"sh-note",children:[o.eligibleCount-o.affordableCount," eligible artist(s) can't be cast — their owners don't have the credits. Booth episodes are charged to the artist's owner."]}
66)]}),e.jsxs("div",{className:"session-cast",children:[e.jsxs("div",{className:"booth-cast-head",children:[e.jsx("div",{className:"session-label",children:"Cast"}),e.jsxs("div",{className:"booth-cast-pick",children:[e.jsx("input",{type:"text",value:re,placeholder:"@handle",onChange:r=>J(r.target.value),onKeyDown:r=>{r.key==="Enter"&&re.trim()&&ee(!1,re)},title:"Cast a specific artist by their handle"}),e.jsx("button",{className:"btn-ghost btn-small",onClick:()=>ee(!1,re),disabled:v||!re.trim(),children:"Look them up"}),e.jsx("button",{className:"btn-ghost btn-small",onClick:()=>ee(!0),disabled:v,children:v?"Loading…":"↻ Re-cast"})]})]}),pe?e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"session-pair",children:e.jsxs("div",{className:"session-artist",children:[pe.profile_image_url?e.jsx("img",{src:pe.profile_image_url,alt:pe.name}):e.jsx("div",{className:"session-artist-blank"}),e.jsxs("div",{className:"session-artist-info",children:[e.jsx("div",{className:"session-artist-name",children:pe.name}),e.jsx("div",{className:"session-artist-meta",children:pe.main_genre||"—"}),e.jsxs("div",{className:"session-artist-meta",children:[pe.track_count," tracks · ",pe.post_count," posts · ",pe.bio_words,"-word bio"]}),e.jsxs("div",{className:`session-artist-meta ${pe.affordable?"":"booth-unaffordable"}`,children:["owner has ",pe.owner_balance," credits",pe.booth_count>0?` · ${pe.booth_count} previous episode${pe.booth_count===1?"":"s"}`:" · never been in"]})]})]})}),e.jsx("p",{className:"session-reason",children:o.castReason})]}):e.jsx("p",{className:"hint",children:o.castReason})]}),e.jsxs("div",{className:"session-topic-block",children:[e.jsxs("div",{className:"session-topic-head",children:[e.jsx("label",{children:"The story"}),e.jsx("div",{className:"session-topic-actions",children:e.jsx("button",{className:"btn-ghost btn-small",onClick:ce,disabled:L||!pe,children:L?"Reading their history…":"✨ Find one in their history"})})]}),e.jsxs("div",{className:"session-composer",children:[b.verbatim&&e.jsx("label",{className:"booth-field-label",children:"Episode title"}),e.jsx("input",{value:b.title,onChange:r=>B(w=>({...w,title:r.target.value})),placeholder:b.verbatim?"The title on the card — leave empty and their opening line names it":"Leave empty and the show finds a story itself — or write the one you want told"}),e.jsx("textarea",{rows:4,value:b.summary,onChange:r=>B(w=>({...w,summary:r.target.value})),placeholder:b.verbatim?"Paste exactly what they said. This IS the script — it gets cut into takes and filmed word for word.":"Paste what they said, in their words. It gets CARRIED into the episode, not paraphrased."}),e.jsxs("label",{className:"booth-verbatim",children:[e.jsx("input",{type:"checkbox",checked:!!b.verbatim,onChange:r=>B(w=>({...w,verbatim:r.target.checked}))}),e.jsxs("span",{children:[e.jsx("strong",{children:"Verbatim"})," — film these exact words."," ","No model rewrites anything; the text is cut into takes and checked word for word."]})]}),b.verbatim&&e.jsxs("label",{className:"booth-verbatim booth-verbatim--sub",children:[e.jsx("input",{type:"checkbox",checked:!!b.emotionTags,onChange:r=>B(w=>({...w,emotionTags:r.target.checked}))}),e.jsxs("span",{children:[e.jsx("strong",{children:"Use emotion tags"})," — let the delivery be marked up"," ","(",e.jsx("code",{children:"[quietly]"}),", ",e.jsx("code",{children:"[sighs]"}),", ",e.jsx("code",{children:"[breathes]"}),")."," ","Direction only: the words stay theirs, and that is checked, not trusted."]})]}),b.verbatim&&e.jsx("div",{className:`booth-verbatim-cut${d&&d.intact===!1?" is-bad":""}`,children:b.summary.trim()?d?d.intact===!1?e.jsxs("span",{children:["⚠ ",d.error]}):e.jsxs(e.Fragment,{children:[e.jsxs("span",{children:["✅ ",d.beats.length," take",d.beats.length===1?"":"s"," · ~",d.seconds,"s of speech ·"," ","every word preserved",d.tagged?` · ${d.tags} emotion tag${d.tags===1?"":"s"}`:""]}),e.jsxs("span",{className:"booth-verbatim-note",children:[d.beats.map(r=>`${r.seconds}s`).join(" · ")," ","(one camera angle each)"]}),b.emotionTags&&!d.tagged&&d.tagNote&&e.jsxs("span",{className:"booth-verbatim-note",children:["⚠ ",d.tagNote]}),e.jsx("ol",{className:"booth-verbatim-beats",children:d.beats.map(r=>e.jsxs("li",{children:[e.jsxs("em",{children:[r.seconds,"s"]})," ",r.text]},r.beat))}),(d.notes||[]).map(r=>e.jsxs("span",{className:"booth-verbatim-note",children:["⚠ ",r]},r))]}):e.jsx("span",{children:"Working out the cut…"}
66):e.jsx("span",{children:"Paste what they said above — verbatim has nothing to film without it."})}),e.jsx("input",{value:b.anchor,onChange:r=>B(w=>({...w,anchor:r.target.value})),placeholder:"Anchor (optional) — the real thing in their life this comes from"})]})]}),e.jsxs("div",{className:"session-launch",children:[e.jsxs("div",{className:"session-ending",children:[e.jsx("label",{children:"Room"}),e.jsxs("select",{value:I,onChange:r=>q(r.target.value),children:[e.jsx("option",{value:"",children:"Default room"}),S.map(r=>e.jsxs("option",{value:r.key,children:[r.name,r.is_default?" (default)":"",r.reference_image_url?"":" — not built"]},r.booth_id))]})]}),e.jsxs("div",{className:"session-ending",children:[e.jsx("label",{children:"Beats"}),e.jsx("select",{value:m,onChange:r=>x(Number(r.target.value)),children:[4,5,6].map(r=>e.jsxs("option",{value:r,children:[r," takes",b.verbatim?"":` · ~${Math.round(r*22)}s of speech`]},r))}),b.verbatim&&d?.beats?.length?e.jsx("span",{className:"booth-beats-locked",children:d.beats.length===m?"cut to match":`text needs ${d.beats.length}`}):null]}),e.jsxs("div",{className:"session-ending",children:[e.jsx("label",{children:"Ending"}),e.jsxs("select",{value:j,onChange:r=>k(r.target.value),children:[e.jsx("option",{value:"",children:"Ask the artist"}),e.jsx("option",{value:"yes",children:"They sign off"}),e.jsx("option",{value:"no",children:"Ends mid-thought"})]})]}),e.jsx("button",{className:"btn-ghost btn-small",onClick:()=>ge(!0),disabled:f||!pe,children:f?"Working…":"Prepare only"}),e.jsx("button",{className:"btn-topic-gen",onClick:()=>ge(!1),disabled:f||!pe||!pe.affordable,children:f?"Filming… (several minutes)":"🎬 Film an episode"})]}),e.jsxs("p",{className:"hint",children:["Renders ",b.verbatim&&d?.beats?.length?d.beats.length:m," camera angles of the chosen room, then a lip-synced take per beat, then cuts them together behind a title card.",b.verbatim?e.jsxs(e.Fragment,{children:[" The script is ",e.jsx("strong",{children:"their pasted text"})," — no model writes or edits it, and a run whose cut does not match what you pasted is refused before anything is charged."]}):e.jsxs(e.Fragment,{children:[" The script is written and checked ",e.jsx("strong",{children:"before"})," anything is charged"]})," ","— and ",e.jsx("strong",{children:"Prepare only"})," stops there, so you can read the story and see the shots before paying to film it."]})]}),e.jsx(et,{showMsg:t})]})}function tt(){const[t,o]=s.useState([]),[c,v]=s.useState([]),[l,f]=s.useState(null),[R,L]=s.useState([]),[z,m]=s.useState(""),[x,b]=s.useState(""),[B,d]=s.useState(!0),[D,S]=s.useState(!1),[E,I]=s.useState(""),[q,j]=s.useState(""),[k,C]=s.useState(""),[M,V]=s.useState(!1),[X,ie]=s.useState(!1),[Q,u]=s.useState(!1),[g,_]=s.useState(null),U=s.useRef();s.useEffect(()=>{te(),ue()},[]),s.useEffect(()=>{l?.playlist_id?re(l.playlist_id):L([])},[l?.playlist_id]);const te=async()=>{try{const N=await T("/api/playlists?limit=100");o((N.playlists||[]).filter(H=>H.playlist_id!=="liked"&&H.playlist_id!=="saved"))}catch{I("Failed to load playlists")}finally{d(!1)}},ue=async()=>{try{const N=await T("/api/admin/tracks?limit=500");N.success&&v(N.tracks||[])}catch(N){console.error("Failed to load tracks:",N)}},re=async N=>{try{const H=await T(`/api/playlists/${N}/tracks`);H.success||console.error("Failed to load playlist tracks:",H.error||H),L(H.tracks||[])}catch(H){console.error("Error in loadPlaylistTracks:",H)}},J=(N,H=!1)=>{H?I(N):j(N),setTimeout(()=>{I(""),j("")},3e3)},je=async()=>{if(!l?.title||!l?.type)return J("Title and type required",!0);S(!0);try{const N=!l.playlist_id,H=N?"/api/playlists":`/api/playlists/${l.playlist_id}`,ce=await T(H,{method:N?"POST":"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(l)});ce.success?(J(N?"Playlist created!":"Playlist saved!"),await te(),N&&ce.playlist&&f(ce.playlist)):J(ce.error||"Save failed",!0)}catch{J("Save failed",!0)}finally{S(!1)}},$=async()=>{if(l?.playlist_id&&confirm(`Delete "${l.title}"? This cannot be undone.`))try{await T(`/api/playlists/${l.playlist_id}`,{method:"DELETE"}),J("Playlist deleted"),f(null),te()}catch{J("Delete failed",!0)}},A=async N=>{const H=N.target.files?.[0];if(!(!H||!l?.playlist_id)){S(!0);try{const ce=new FormData;ce.append("cover",H);const ge=await(await fetch(`${Ue}/api/admin/playlist/${l.playlist_id}/upload-cover`,{method:"POST",headers:{Authorization:`Bearer ${We()}`},body:ce})).json();
66ge.success?(f(pe=>({...pe,cover_image_url:ge.cover_image_url,_signed_url:ge.signed_url})),J("Cover uploaded!")):J(ge.error||"Upload failed",!0)}catch{J("Upload failed",!0)}finally{S(!1)}}},he=async()=>{if(!k.trim())return J("Enter a prompt first",!0);if(!l?.playlist_id)return J("Save the playlist first to generate a cover",!0);ie(!0);try{const N=await T(`/api/playlists/${l.playlist_id}/generate-cover`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({prompt:k})});N.success?(f(H=>({...H,cover_image_url:N.cover_image_url,_signed_url:N.signed_url})),V(!1),C(""),J("Cover generated!")):J(N.error||"Generation failed",!0)}catch{J("Generation failed",!0)}finally{ie(!1)}},ee=async()=>{if(!l?.playlist_id)return J("Save the playlist first",!0);u(!0);try{const N=await T(`/api/playlists/${l.playlist_id}/cover-prompt`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({title:l.title,genre:l.genre,mood:l.mood,description:l.description,topic_prompt:l.topic_prompt,idea:k.trim()})});N?.success&&N.prompt?C(N.prompt):J(N?.error||"Could not write a brief",!0)}catch{J("Could not write a brief",!0)}finally{u(!1)}},y=async N=>{if(!l?.playlist_id)return;const H=R.length;try{await T(`/api/playlists/${l.playlist_id}/tracks`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({trackId:N,position:H})}),re(l.playlist_id)}catch{J("Failed to add track",!0)}},W=async N=>{if(l?.playlist_id)try{await T(`/api/playlists/${l.playlist_id}/tracks/${N}`,{method:"DELETE"}),re(l.playlist_id)}catch{J("Failed to remove track",!0)}},xe=async()=>{if(l?.playlist_id){if(!l?.topic_prompt?.trim())return J("Add a topic prompt first",!0);if(confirm("This will queue all opted-in avatars to create songs for this topic. Continue?")){S(!0);try{const N=await T(`/api/playlists/${l.playlist_id}/topic-generate`,{method:"POST"});N.success?(_(N.queued),J(`✅ Queued ${N.queued} avatar(s) for topic song creation`)):J(N.error||"Failed to trigger generation",!0)}catch{J("Failed",!0)}finally{S(!1)}}}},be=new Set(R.map(N=>N.track_id)),Z=R.filter(N=>{if(!x)return!0;const H=x.toLowerCase();return N.title?.toLowerCase().includes(H)||N.avatar_name?.toLowerCase().includes(H)}),ve=c.filter(N=>{if(be.has(N.track_id))return!1;if(!z)return!0;const H=z.toLowerCase();return N.title?.toLowerCase().includes(H)||N.avatar_name?.toLowerCase().includes(H)||N.genre?.toLowerCase().includes(H)}),Te={genre:"🎸 Genre",mood:"🌙 Mood",topic:"💬 Topic"},$e=l?._signed_url||l?.cover_image_url;return B?e.jsx("div",{className:"loading-state",children:"Loading playlists…"}):e.jsxs("div",{className:"playlists-tab",children:[E&&e.jsx("div",{className:"error-message",children:E}),q&&e.jsx("div",{className:"success-message",children:q}),e.jsxs("div",{className:"playlists-layout",children:[e.jsxs("div",{className:"playlist-list-panel",children:[e.jsxs("button",{className:"new-playlist-btn",onClick:()=>f({...Xs}),children:[e.jsx(de,{name:"plus",size:14})," New Playlist"]}),["genre","mood","topic"].map(N=>{const H=t.filter(ce=>ce.type===N);return!H.length&&!l?null:e.jsxs("div",{className:"playlist-type-group",children:[e.jsx("div",{className:"type-chip",children:Te[N]}),H.map(ce=>e.jsxs("button",{className:`playlist-list-item ${l?.playlist_id===ce.playlist_id?"active":""}`,onClick:()=>f(ce),children:[ce.cover_image_url&&e.jsx("img",{src:ce.signed_cover||ce.cover_image_url,alt:"",className:"playlist-list-thumb"}),e.jsxs("div",{className:"playlist-list-meta",children:[e.jsx("strong",{children:ce.title}),e.jsxs("span",{children:[ce.track_count_cached??"?"," tracks"]})]})]},ce.playlist_id))]},N)})]}),l?e.jsxs("div",{className:"playlist-editor",children:[e.jsx("h3",{className:"editor-title",children:l.playlist_id?"Edit Playlist":"New Playlist"}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"Title"}),e.jsx("input",{value:l.title||"",onChange:N=>f(H=>({...H,title:N.target.value})),placeholder:"Playlist title"})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"Type"}
66),e.jsxs("select",{value:l.type,onChange:N=>f(H=>({...H,type:N.target.value})),children:[e.jsx("option",{value:"genre",children:"Genre"}),e.jsx("option",{value:"mood",children:"Mood"}),e.jsx("option",{value:"topic",children:"Topic"})]})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"Description"}),e.jsx("textarea",{rows:2,value:l.description||"",onChange:N=>f(H=>({...H,description:N.target.value})),placeholder:"Optional description"})]}),l.type==="genre"&&e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"Genre (matches tracks.genre)"}),e.jsx("input",{value:l.genre||"",onChange:N=>f(H=>({...H,genre:N.target.value})),placeholder:"e.g. Hip-Hop"})]}),l.type==="mood"&&e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"Mood Label"}),e.jsx("input",{value:l.mood||"",onChange:N=>f(H=>({...H,mood:N.target.value})),placeholder:"e.g. Chill, Hype, Late Night…"})]}),l.type==="topic"&&e.jsxs("div",{className:"form-group",children:[e.jsxs("label",{children:["Topic Brief ",e.jsx("span",{className:"label-hint",children:"— full brief sent to avatars as creative context"})]}),e.jsx("textarea",{rows:6,value:l.topic_prompt||"",onChange:N=>f(H=>({...H,topic_prompt:N.target.value})),placeholder:"Describe the topic in as much detail as you'd like. Avatars will read this and create songs that respond to it in their own voice and style.",className:"topic-prompt-area"})]}),e.jsxs("div",{className:"form-group",children:[e.jsxs("label",{children:["Cover Art ",e.jsx("span",{className:"label-hint",children:"16:9 · 1280×720 · JPEG optimized on upload"})]}),$e&&e.jsx("div",{className:"cover-preview",children:e.jsx("img",{src:$e,alt:"Cover preview"})}),e.jsxs("div",{className:"cover-buttons",children:[e.jsx("input",{ref:U,type:"file",accept:"image/*",style:{display:"none"},onChange:A}),e.jsxs("button",{className:"btn-secondary",onClick:()=>U.current?.click(),disabled:D||!l.playlist_id,title:l.playlist_id?"":"Save playlist first",children:[e.jsx(de,{name:"arrow-up-tray",size:14})," Upload Image"]}),e.jsx("button",{className:"btn-secondary btn-ai",onClick:()=>V(!M),disabled:D||!l.playlist_id,title:l.playlist_id?"":"Save playlist first",children:"✨ Generate with AI"})]}),!l.playlist_id&&e.jsx("p",{className:"hint",children:"Save the playlist first to enable cover art."}),M&&e.jsxs("div",{className:"ai-prompt-box",children:[e.jsx("textarea",{rows:4,value:k,onChange:N=>C(N.target.value),placeholder:"Describe the visual style for this playlist cover — or press Auto-write to draft one from the title, description and genre…"}),e.jsxs("div",{className:"ai-prompt-actions",children:[e.jsx("button",{className:"btn-secondary btn-sm",onClick:ee,disabled:Q||X,children:Q?"Writing…":k.trim()?"✍️ Enhance":"✍️ Auto-write"}),e.jsx("button",{className:"btn-primary btn-sm",onClick:he,disabled:X||Q||!k.trim(),children:X?"⏳ Generating…":"🎨 Generate"}),e.jsx("button",{className:"btn-ghost btn-sm",onClick:()=>{V(!1),C("")},children:"Cancel"})]})]})]}),(l.type==="mood"||l.type==="genre")&&l.playlist_id&&e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"Curated Tracks"}),e.jsxs("div",{className:"station-panels mood-track-editor",children:[e.jsxs("div",{className:"panel",children:[e.jsxs("h3",{children:["In Playlist ",e.jsxs("span",{className:"track-count",children:["(",Z.length,"/",R.length,")"]})]}),e.jsxs("div",{className:"search-box",children:[e.jsx(de,{name:"magnifying-glass",size:14}),e.jsx("input",{type:"text",value:x,onChange:N=>b(N.target.value),placeholder:"Search this playlist…"})]}),Z.length===0?e.jsx("p",{className:"empty-state",children:R.length?"No matches":"No tracks yet"}):e.jsx("ul",{className:"admin-track-list",children:Z.map(N=>e.jsxs("li",{className:"admin-track-row",children:[e.jsx("img",{src:N.artwork_url,alt:"",className:"admin-track-art"}),e.jsxs("div",{className:"admin-track-meta",children:[e.jsx("strong",{children:N.title}),e.jsx("span",{children:N.avatar_name})]}),e.jsx("button",{className:"remove-btn",onClick:()=>W(N.track_id),title:"Remove",children:e.jsx(de,{name:"xmark",size:14})})]},N.track_id))})]}),e.jsxs("div",{className:"panel",children:[e.jsx("h3",{children:"Add Tracks"}),e.jsxs("div",{className:"search-box",children:[e.jsx(de,{name:"magnifying-glass",size:14}),e.jsx("input",{type:"text",value:z,onChange:N=>m(N.target.value),placeholder:"Search…"})]}
66),e.jsx("ul",{className:"admin-track-list",children:ve.slice(0,50).map(N=>e.jsxs("li",{className:"admin-track-row",children:[e.jsx("img",{src:N.artwork_url,alt:"",className:"admin-track-art"}),e.jsxs("div",{className:"admin-track-meta",children:[e.jsx("strong",{children:N.title}),e.jsx("span",{children:N.avatar_name}),N.genre&&e.jsx("span",{className:"admin-genre-tag",children:N.genre})]}),e.jsx("button",{className:"add-btn",onClick:()=>y(N.track_id),title:"Add",children:e.jsx(de,{name:"plus",size:14})})]},N.track_id))}),ve.length>50&&e.jsxs("p",{className:"more-hint",children:["Showing 50 of ",ve.length,". Search to find more."]})]})]})]}),l.type==="topic"&&l.playlist_id&&e.jsxs("div",{className:"form-group topic-gen-section",children:[e.jsx("label",{children:"Avatar Song Generation"}),e.jsx("p",{className:"hint",children:"Sends the topic brief to all eligible opted-in artists. They will create one song each, which will appear in this playlist automatically."}),e.jsx("button",{className:"btn-topic-gen",onClick:xe,disabled:D||!l.topic_prompt?.trim(),children:"🚀 Send to Artists"}),g!==null&&e.jsxs("p",{className:"topic-queued",children:["✅ Queued ",g," avatar(s)"]})]}),e.jsxs("div",{className:"editor-actions",children:[e.jsx("button",{className:"btn-primary",onClick:je,disabled:D,children:D?"Saving…":"Save Playlist"}),l.playlist_id&&e.jsx("button",{className:"btn-danger",onClick:$,disabled:D,children:"Delete"}),e.jsx("button",{className:"btn-ghost",onClick:()=>f(null),children:"Cancel"})]})]}):e.jsx("div",{className:"playlist-editor-empty",children:e.jsx("p",{children:"Select a playlist to edit, or create a new one."})})]})]})}const cs={url:"",title:"",subtitle:"",thumbnail_url:"",sort_order:0,is_active:!0},at=t=>t.thumbnail_url||(t.youtube_id?`https://img.youtube.com/vi/${t.youtube_id}/mqdefault.jpg`:"");function nt(){const[t,o]=s.useState([]),[c,v]=s.useState({...cs}),[l,f]=s.useState(null),[R,L]=s.useState(!0),[z,m]=s.useState(!1),[x,b]=s.useState(""),[B,d]=s.useState("");s.useEffect(()=>{D()},[]);const D=async()=>{try{const k=await T("/api/admin/featured-youtube");k.success?o(k.items||[]):b(k.error||"Failed to load items")}catch{b("Failed to load YouTube items")}finally{L(!1)}},S=()=>{v({...cs}),f(null)},E=k=>{f(k.id),v({url:k.youtube_url||"",title:k.title||"",subtitle:k.subtitle||"",thumbnail_url:k.thumbnail_url||"",sort_order:k.sort_order??0,is_active:!!k.is_active}),b(""),d("")},I=async()=>{if(!c.url.trim()){b("YouTube URL is required");return}if(!c.title.trim()){b("Title is required");return}m(!0),b(""),d("");try{const k=l?`/api/admin/featured-youtube/${l}`:"/api/admin/featured-youtube",C=await T(k,{method:l?"PUT":"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(c)});C.success?(d(l?"Updated!":"Added!"),S(),D(),setTimeout(()=>d(""),2e3)):b(C.error||"Save failed")}catch{b("Save failed")}finally{m(!1)}},q=async k=>{b("");try{const C=await T(`/api/admin/featured-youtube/${k.id}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({is_active:!k.is_active})});C.success?D():b(C.error||"Failed to update")}catch{b("Failed to update")}},j=async k=>{if(window.confirm(`Remove "${k.title}" from Featured?`)){b("");try{const C=await T(`/api/admin/featured-youtube/${k.id}`,{method:"DELETE"});C.success?(l===k.id&&S(),D()):b(C.error||"Failed to remove")}catch{b("Failed to remove")}}};return R?e.jsx("div",{className:"loading-state",children:"Loading YouTube items…"}):e.jsxs(e.Fragment,{children:[x&&e.jsx("div",{className:"error-message",children:x}),e.jsx("div",{className:"station-manager-content",children:e.jsxs("div",{className:"station-panels",children:[e.jsxs("div",{className:"panel",children:[e.jsx("h3",{children:l?"Edit YouTube Item":"Add YouTube Item"}),e.jsxs("div",{className:"yt-form",children:[e.jsxs("label",{className:"yt-field",children:["YouTube URL",e.jsx("input",{type:"text",value:c.url,onChange:k=>v(C=>({...C,url:k.target.value})),placeholder:"https://www.youtube.com/watch?v=…"})]}),e.jsxs("label",{className:"yt-field",children:["Title",e.jsx("input",{type:"text",value:c.title,onChange:k=>v(C=>({...C,title:k.target.value})),placeholder:"Displayed title"})]}),e.jsxs("label",{className:"yt-field",children:["Subtitle ",e.jsx("span",{className:"yt-opt",children:"optional"}),e.jsx("input",{type:"text",value:c.subtitle,onChange:k=>v(C=>({...C,subtitle:k.target.value})),placeholder:"Artist / channel / short descriptor"})]}),e.jsxs("label",{className:"yt-field",children:["Thumbnail URL ",e.jsx("span",{className:"yt-opt",children:"optional — defaults to YouTube's"}),e.jsx("input",{type:"text",value:c.thumbnail_url,onChange:k=>v(C=>({...C,thumbnail_url:k.target.value})),placeholder:"https://…"})]}),e.jsxs("div",{className:"yt-row",children:[e.jsxs("label",{className:"yt-field yt-narrow",children:["Sort order",e.jsx("input",{type:"number",value:c.sort_order,onChange:k=>v(C=>({...C,sort_order:k.target.value}))})]}),e.jsxs("label",{className:"yt-checkbox",children:[e.jsx("input",{type:"checkbox",checked:c.is_active,onChange:k=>v(C=>({...C,is_active:k.target.checked}))}),"Active (visible on /listen)"]})]}),e.jsxs("div",{className:"yt-form-actions",children:[e.jsx("button",{className:"btn-primary",onClick:I,disabled:z,children:z?"
66Saving…":l?"Update":"Add"}),l&&e.jsx("button",{className:"btn-ghost",onClick:S,disabled:z,children:"Cancel"})]})]})]}),e.jsxs("div",{className:"panel",children:[e.jsxs("h3",{children:["Featured Clips ",e.jsxs("span",{className:"track-count",children:["(",t.length,")"]})]}),t.length===0?e.jsx("p",{className:"empty-state",children:"No YouTube items yet — add one on the left."}):e.jsx("ul",{className:"admin-track-list",children:t.map(k=>e.jsxs("li",{className:`admin-track-row ${k.is_active?"":"yt-inactive"}`,children:[e.jsx("img",{src:at(k),alt:"",className:"admin-track-art"}),e.jsxs("div",{className:"admin-track-meta",children:[e.jsx("strong",{children:k.title}),e.jsx("span",{children:k.subtitle||k.youtube_id}),e.jsxs("span",{className:"admin-genre-tag",children:["#",k.sort_order,k.is_active?"":" · hidden"]})]}),e.jsx("button",{className:"add-btn",onClick:()=>q(k),title:k.is_active?"Hide from /listen":"Show on /listen",children:e.jsx(de,{name:k.is_active?"eye":"eye-slash",size:14})}),e.jsx("button",{className:"add-btn",onClick:()=>E(k),title:"Edit",children:e.jsx(de,{name:"pen",size:14})}),e.jsx("button",{className:"remove-btn",onClick:()=>j(k),title:"Remove",children:e.jsx(de,{name:"xmark",size:14})})]},k.id))})]})]})}),B&&e.jsx("div",{className:"success-message",children:B})]})}const ds={key:"",name:"",shot:"medium",scene_prompt:"",sfx_prompt:"",fit_json:"",weight:1,is_active:1,sort_order:0},it=["selfie","mirror","medium","wide"];function hs({scene:t,onChange:o}){const c=(m,x)=>o({...t,[m]:x}),[v,l]=s.useState(null),[f,R]=s.useState(null);async function L(m){if(!(m==="enhance"&&!t.scene_prompt?.trim())){l(m);try{const x=await T("/api/admin/speaking-scenes/write",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({mode:m,idea:t.scene_prompt||"",shot:t.shot||"medium"})});x&&x.scene_prompt&&c("scene_prompt",x.scene_prompt)}catch{}l(null)}}async function z(m){if(!(m==="auto"&&!t.scene_prompt?.trim())&&!(m==="enhance"&&!t.sfx_prompt?.trim())){R(m);try{const x=await T("/api/admin/speaking-scenes/write-sfx",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({mode:m,scene_prompt:t.scene_prompt||"",idea:t.sfx_prompt||""})});x&&x.sfx_prompt&&c("sfx_prompt",x.sfx_prompt)}catch{}R(null)}}return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"scene-row",children:[e.jsxs("label",{className:"scene-f",children:[e.jsx("span",{children:"Key"}),e.jsx("input",{value:t.key,onChange:m=>c("key",m.target.value),placeholder:"car_selfie_night"})]}),e.jsxs("label",{className:"scene-f",children:[e.jsx("span",{children:"Name"}),e.jsx("input",{value:t.name,onChange:m=>c("name",m.target.value),placeholder:"Car selfie, night"})]}),e.jsxs("label",{className:"scene-f scene-f-sm",children:[e.jsx("span",{children:"Shot"}),e.jsx("select",{value:t.shot,onChange:m=>c("shot",m.target.value),children:it.map(m=>e.jsx("option",{value:m,children:m},m))})]}),e.jsxs("label",{className:"scene-f scene-f-xs",children:[e.jsx("span",{children:"Weight"}),e.jsx("input",{type:"number",value:t.weight,onChange:m=>c("weight",m.target.value)})]}),e.jsxs("label",{className:"scene-f scene-f-xs",children:[e.jsx("span",{children:"Sort"}),e.jsx("input",{type:"number",value:t.sort_order,onChange:m=>c("sort_order",m.target.value)})]}),e.jsxs("label",{className:"scene-f scene-f-chk",children:[e.jsx("span",{children:"Active"}),e.jsx("input",{type:"checkbox",checked:!!t.is_active,onChange:m=>c("is_active",m.target.checked?1:0)})]})]}),e.jsxs("label",{className:"scene-f scene-f-full",children:[e.jsxs("span",{className:"scene-f-labelrow",children:[e.jsx("span",{children:'Scene prompt — setting only (no "looking at camera")'}),e.jsxs("span",{className:"scene-write-btns",children:[e.jsx("button",{type:"button",className:"scene-mini",disabled:!!v||!t.scene_prompt?.trim(),onClick:()=>L("enhance"),title:"Improve what you typed into a polished prompt",children:v==="enhance"?"…":"✨ Enhance"}),e.jsx("button",{type:"button",className:"scene-mini",disabled:!!v,onClick:()=>L("auto"),title:"Generate a fresh scene idea",children:v==="auto"?"…":"Auto-write"})]})]}),e.jsx("textarea",{rows:2,value:t.scene_prompt,onChange:m=>c("scene_prompt",m.target.value),placeholder:"Type a rough idea then hit ✨ Enhance — e.g. 'on a fire escape at night' — or Auto-write to seed one."})]}),e.jsxs("label",{className:"scene-f scene-f-full",children:[e.jsxs("span",{className:"scene-f-labelrow",children:[e.jsx("span",{children:"Environment sound — ambient only (no music, no speech)"}),e.jsxs("span",{className:"scene-write-btns",children:[e.jsx("button",{type:"button",className:"scene-mini",disabled:!!f||!t.sfx_prompt?.trim(),onClick:()=>z("enhance"),title:"Polish the sound prompt you typed",children:f==="enhance"?"…":"✨ Enhance"}),e.jsx("button",{type:"button",className:"scene-mini",disabled:!!f||!t.scene_prompt?.trim(),onClick:()=>z("auto"),title:"Write the ambient sound from the scene setting above",children:f==="auto"?"…":"Auto-write"})]})]}),e.jsx("textarea",{rows:2,value:t.sfx_prompt||"",onChange:m=>c("sfx_prompt",m.target.value),placeholder:"Ambient environment sound for this setting — e.g. 'quiet parked-car interior at night, faint muffled city hum, occasional passing-car whoosh'. Auto-write reads the scene above."})]}),e.jsxs("label",{className:"scene-f scene-f-full",children:[e.jsxs("span",{children:["Fit JSON — ",'{ "genres":[], "sliders":{"energy":"high"}, "mood":"" }']}),e.jsx("textarea",{rows:2,className:"scene-mono",value:t.fit_json,onChange:m=>c("fit_json",m.target.value),placeholder:'{"genres":[],"sliders":{},"mood":""}'})]})]})}
66function rt(){const[t,o]=s.useState([]),[c,v]=s.useState(ds),[l,f]=s.useState(!0),[R,L]=s.useState(!1),[z,m]=s.useState(""),[x,b]=s.useState(""),B=(j,k)=>{k?(m(j),setTimeout(()=>m(""),4e3)):(b(j),setTimeout(()=>b(""),2500))},d=j=>({...j,fit_json:j.fit_json==null?"":typeof j.fit_json=="string"?j.fit_json:JSON.stringify(j.fit_json)});async function D(){f(!0);try{const j=await T("/api/admin/speaking-scenes");if(j.error)throw new Error(j.error);o((j.scenes||[]).map(d))}catch{B("Failed to load scenes",!0)}f(!1)}s.useEffect(()=>{D()},[]);const S=(j,k)=>o(C=>C.map(M=>M.scene_id===j?k:M)),E=j=>({key:j.key,name:j.name,shot:j.shot,scene_prompt:j.scene_prompt,sfx_prompt:j.sfx_prompt||"",fit_json:j.fit_json,weight:Number(j.weight)||1,is_active:j.is_active?1:0,sort_order:Number(j.sort_order)||0});async function I(j){if(!j.key?.trim()||!j.name?.trim()||!j.scene_prompt?.trim())return B("Key, name and scene prompt are required",!0);if(j.fit_json?.trim())try{JSON.parse(j.fit_json)}catch{return B("Fit JSON is not valid JSON",!0)}L(!0);try{const k={headers:{"Content-Type":"application/json"},body:JSON.stringify(E(j))},C=j.scene_id?await T(`/api/admin/speaking-scenes/${j.scene_id}`,{method:"PUT",...k}):await T("/api/admin/speaking-scenes",{method:"POST",...k});if(C.error)throw new Error(C.error);j.scene_id||v(ds),B(j.scene_id?"Saved":"Scene added"),D()}catch(k){B(k.message||"Save failed",!0)}L(!1)}async function q(j){if(window.confirm("Delete this scene?")){L(!0);try{const k=await T(`/api/admin/speaking-scenes/${j}`,{method:"DELETE"});if(k.error)throw new Error(k.error);B("Deleted"),D()}catch{B("Delete failed",!0)}L(!1)}}return e.jsxs("div",{className:"scenes-tab",children:[e.jsxs("p",{className:"scenes-hint",children:['The "frozen moment" settings talking-head update videos are filmed in. Each is single-subject & lip-sync-safe — the universal framing (address the viewer, no other faces, mouthless-only background motion) is added automatically, so write only the ',e.jsx("em",{children:"setting"})," here. Edits apply immediately."]}),z&&e.jsx("div",{className:"scenes-msg scenes-msg-err",children:z}),x&&e.jsx("div",{className:"scenes-msg scenes-msg-ok",children:x}),e.jsxs("div",{className:"scene-card scene-card-new",children:[e.jsx("div",{className:"scene-card-title",children:"➕ Add a scene"}),e.jsx(hs,{scene:c,onChange:v}),e.jsx("div",{className:"scene-actions",children:e.jsx("button",{className:"scene-btn scene-btn-save",disabled:R,onClick:()=>I(c),children:"Add scene"})})]}),l?e.jsx("div",{className:"scenes-loading",children:"Loading…"}):t.map(j=>e.jsxs("div",{className:`scene-card ${j.is_active?"":"scene-inactive"}`,children:[e.jsx(hs,{scene:j,onChange:k=>S(j.scene_id,k)}),e.jsxs("div",{className:"scene-actions",children:[e.jsx("button",{className:"scene-btn scene-btn-save",disabled:R,onClick:()=>I(j),children:"Save"}),e.jsx("button",{className:"scene-btn scene-btn-del",disabled:R,onClick:()=>q(j.scene_id),children:"Delete"})]})]},j.scene_id))]})}const ot=[{key:"youtube",label:"YouTube"},{key:"tracks",label:"Tracks"},{key:"videos",label:"Videos"},{key:"artists",label:"Artists"}];function lt({kind:t}){const[o,c]=s.useState([]),[v,l]=s.useState(""),[f,R]=s.useState(!1),[L,z]=s.useState(!1),[m,x]=s.useState(!1),[b,B]=s.useState(null),d=async(E=v,I=f)=>{z(!0);try{const q=new URLSearchParams;E&&q.set("q",E),I&&q.set("featuredOnly","true");const j=await T(`/api/admin/features/${t}?${q}`);c(j.items||[]),x(!!j.searchRequired)}catch{c([])}finally{z(!1)}};s.useEffect(()=>{d("",!1),l(""),R(!1)},[t]),s.useEffect(()=>{const E=setTimeout(()=>d(v,f),300);return()=>clearTimeout(E)},[v,f]);const D=async E=>{B(E.id);const I=!E.is_featured;c(q=>q.map(j=>j.id===E.id?{...j,is_featured:I?1:0}:j));try{(await T(`/api/admin/features/${t}/${E.id}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({featured:I})})).error&&d()}catch{d()}finally{B(null)}},S=t==="tracks"?"Search all tracks by title, artist or genre…":t==="videos"?"Search videos by title or artist…":"Search artists…";return e.jsxs("div",{className:"feat-list",children:[e.jsxs("div",{className:"feat-control
66s",children:[e.jsxs("div",{className:"search-box",children:[e.jsx(de,{name:"magnifying-glass",size:14}),e.jsx("input",{type:"text",value:v,onChange:E=>l(E.target.value),placeholder:S})]}),e.jsxs("label",{className:"feat-only",children:[e.jsx("input",{type:"checkbox",checked:f,onChange:E=>R(E.target.checked)}),"Featured only"]})]}),m&&t==="tracks"&&e.jsxs("p",{className:"hint",children:["Showing the ",o.length," currently featured track",o.length===1?"":"s",". Search to find others."]}),L?e.jsx("div",{className:"loading-state",children:"Loading…"}):o.length===0?e.jsx("p",{className:"empty-state",children:"Nothing found."}):e.jsx("ul",{className:"feat-rows",children:o.map(E=>e.jsxs("li",{className:`feat-row ${E.is_featured?"is-on":""}`,children:[e.jsx("div",{className:"feat-art",style:E.image_url?{backgroundImage:`url(${E.image_url})`}:void 0}),e.jsxs("div",{className:"feat-meta",children:[e.jsx("div",{className:"feat-title",children:E.title}),e.jsxs("div",{className:"feat-sub",children:[E.subtitle||"—",E.genre?` · ${E.genre}`:"",E.video_type?` · ${E.video_type==="hypeclip"?"HypeClip":"Music video"}`:"",E.track_count!=null?` · ${E.track_count} tracks`:""]})]}),e.jsxs("label",{className:"feat-switch",title:E.is_featured?"Featured":"Not featured",children:[e.jsx("input",{type:"checkbox",checked:!!E.is_featured,disabled:b===E.id,onChange:()=>D(E)}),e.jsx("span",{})]})]},E.id))})]})}function ct(){const[t,o]=s.useState("youtube");return e.jsxs("div",{className:"features-tab",children:[e.jsx("div",{className:"sub-tabs",children:ot.map(c=>e.jsx("button",{className:`sub-tab ${t===c.key?"active":""}`,onClick:()=>o(c.key),children:c.label},c.key))}),t==="youtube"?e.jsx(nt,{}):e.jsx(lt,{kind:t})]})}const us={interview:"Interview",banter:"Banter",monologue:"Monologue",session:"Two artists"},ps={banter:"news",interview:"decoded"};function dt({setId:t,showMsg:o,onChanged:c}){const[v,l]=s.useState(null),[f,R]=s.useState(!0),[L,z]=s.useState(null),[m,x]=s.useState(!1),[b,B]=s.useState(!1),[d,D]=s.useState(""),[S,E]=s.useState(""),[I,q]=s.useState(""),[j,k]=s.useState(null),[C,M]=s.useState(null),[V,X]=s.useState(null),[ie,Q]=s.useState(!1),[u,g]=s.useState(null),[_,U]=s.useState(()=>new Set),[te,ue]=s.useState(!1),[re,J]=s.useState(null),[je,$]=s.useState(null),A=async()=>{R(!0);try{const i=await T(`/api/admin/shows/${t}`);if(i.error)return o(i.error,!0);l(i);const O=typeof i.show?.default_vibe=="string"?(()=>{try{return JSON.parse(i.show.default_vibe)}catch{return null}})():i.show?.default_vibe;z(O||{playful:.5,confrontational:.5,reflective:.5})}catch{o("Failed to load show",!0)}finally{R(!1)}};s.useEffect(()=>{A(),D(""),E(""),q(""),k(null),M(null),g(null),U(new Set),J(null)},[t]),s.useEffect(()=>{const i=v?.show?.show_format;$(null),i&&T(`/api/world/stage-sets?showFormat=${encodeURIComponent(i)}`).then(O=>$(O?.sets?.[0]||null)).catch(()=>$(null))},[v?.show?.show_format]);const he=async()=>{x(!0);try{const i=await T(`/api/admin/shows/${t}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({default_vibe:L})});i.error?o(i.error,!0):o("Show settings saved")}catch{o("Failed to save",!0)}finally{x(!1)}},[ee,y]=s.useState(!1),W=async i=>{if(i){y(!0);try{const O=new FormData;O.append("logo",i);const ye=await(await fetch(`${Ue}/api/admin/shows/${t}/logo`,{method:"POST",headers:{Authorization:`Bearer ${We()}`},body:O})).json();ye.success?(o("Logo uploaded"),l(_e=>_e&&{..._e,show:{..._e.show,logo_url:ye.signed_url}}),c?.()):o(ye.error||"Logo upload failed",!0)}catch{o("Logo upload failed",!0)}finally{y(!1)}}},[xe,be]=s.useState(!1),[Z,ve]=s.useState(null);s.useEffect(()=>{let i=!1;return T("/api/admin/social-accounts").then(O=>{!i&&O?.success&&ve(O)}).catch(()=>{}),()=>{i=!0}},[]);const Te=async(i,O)=>{be(!0);try{const G=await T(`/api/admin/shows/${t}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({[i]:O===""?null:Number(O)})});G.error?o(G.error,!0):(l(ye=>ye&&{...ye,show:{...ye.show,[i]:O===""?null:Number(O)}}),o(O===""?"Unpinned — falls back to the default account":"Account saved"))}catch{o("Failed to save",!0)}finally{be(!1)}},$e=async i=>{const O=(v?.show?.ig_also_connection_ids||[]).map(Number),G=O.includes(i)?O.filter(ye=>ye!==i):[...O,i];be(!0);try{const ye=await T(`/api/admin/shows/${t}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({ig_also_connection_ids:G})});ye.error?o(ye.error,!0):(l(_e=>_e&&{..._e,show:{..._e.show,ig_also_connection_ids:G}}),o(`Carousel also posts to ${G.length} other Instagram account${G.length===1?"":"s"}`))}catch{o("Failed to save",!0)}finally{be(!1)}},N=async()=>{const i=!v?.show?.auto_carousel;be(!0);try{const O=await T(`/api/admin/shows/${t}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({auto_carousel:i})});O.error?o(O.error,!0):(l(G=>G&&{...G,show:{...G.show,auto_carousel:i}}),o(i?"On — every new episode posts its stories automatically.":"Off — episodes publish without a carousel."))}catch{o("Failed to save",!0)}finally{be(!1)}}
66,[H,ce]=s.useState(!1),Re=async()=>{const i=!v?.show?.auto_distribute;ce(!0);try{const O=await T(`/api/admin/shows/${t}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({auto_distribute:i})});O.error?o(O.error,!0):(l(G=>G&&{...G,show:{...G.show,auto_distribute:i}}),o(i?"On — a finished film posts to the HypeTunes YouTube and Instagram.":"Off — films are delivered to the buyer and posted nowhere."))}catch{o("Failed to save",!0)}finally{ce(!1)}},[ge,pe]=s.useState(!1),r=async()=>{const i=!v?.show?.auto_film;pe(!0);try{const O=await T(`/api/admin/shows/${t}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({auto_film:i})});O.error?o(O.error,!0):(l(G=>G&&{...G,show:{...G.show,auto_film:i}}),o(i?"On — each new episode is filmed, its cover animated, and the video carousel posted.":"Off — episodes publish as audio; the carousel (if on) posts stills."))}catch{o("Failed to save",!0)}finally{pe(!1)}},w=async()=>{if(confirm("Remove the logo? The page goes back to the show name in type.")){y(!0);try{const i=await T(`/api/admin/shows/${t}/logo`,{method:"DELETE"});i.success?(o("Logo removed"),l(O=>O&&{...O,show:{...O.show,logo_url:null}}),c?.()):o(i.error||"Failed to remove logo",!0)}catch{o("Failed to remove logo",!0)}finally{y(!1)}}},[ae,oe]=s.useState(!1),Se=async i=>{if(i){oe(!0);try{const O=new FormData;O.append("cover",i);const G=await fetch(`${Ue}/api/admin/shows/${t}/cover`,{method:"POST",headers:{Authorization:`Bearer ${We()}`},body:O}),ye=await G.json();!G.ok||!ye.success?o(ye.error||"Cover upload failed",!0):(o("Cover art uploaded"),l(_e=>_e&&{..._e,show:{..._e.show,cover_image_url:ye.signed_url}}),c?.())}catch{o("Cover upload failed",!0)}finally{oe(!1)}}},a=async()=>{if(confirm("Remove the cover art? Cards for this show fall back to an empty art well.")){oe(!0);try{const i=await T(`/api/admin/shows/${t}/cover`,{method:"DELETE"});i.success?(o("Cover art removed"),l(O=>O&&{...O,show:{...O.show,cover_image_url:null}}),c?.()):o(i.error||"Failed to remove cover art",!0)}catch{o("Failed to remove cover art",!0)}finally{oe(!1)}}},[p,se]=s.useState(""),[n,P]=s.useState(!1),[Y,we]=s.useState(!1),[me,ke]=s.useState([]),fe=s.useCallback(async()=>{try{const i=await T(`/api/admin/shows/${t}/cover/versions`);ke(i?.versions||[])}catch{}},[t]);s.useEffect(()=>{let i=!1;return(async()=>{if(v?.show?.cover_prompt)se(v.show.cover_prompt);else try{const O=await T(`/api/admin/shows/${t}/cover/autowrite`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({mode:"auto"})});!i&&O?.prompt&&se(O.prompt)}catch{}fe()})(),()=>{i=!0}},[t,v?.show?.cover_prompt,fe]);const Ee=async i=>{P(!0);try{const O=await T(`/api/admin/shows/${t}/cover/autowrite`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i==="enhance"?{mode:i,idea:p}:{mode:"auto"})});O?.success?(se(O.prompt),o(i==="enhance"?"Prompt rewritten":"Reset to the show’s own direction")):o(O?.error||"Could not write the prompt",!0)}catch{o("Could not write the prompt",!0)}finally{P(!1)}},Ae=async()=>{we(!0);try{const i=await T(`/api/admin/shows/${t}/cover/generate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({prompt:p})});i?.success?(o("Cover generated"),l(O=>O&&{...O,show:{...O.show,cover_image_url:i.signed_url||i.cover_image_url}}),fe(),c?.()):o(i?.error||"Could not generate a cover",!0)}catch{o("Could not generate a cover",!0)}finally{we(!1)}},Je=async i=>{try{const O=await T(`/api/admin/shows/${t}/cover/use`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({versionId:i})});O?.success?(o("Cover switched"),l(G=>G&&{...G,show:{...G.show,cover_image_url:O.signed_url||O.cover_image_url}}),O.prompt&&se(O.prompt),fe(),c?.()):o(O?.error||"Could not switch cover",!0)}catch{o("Could not switch cover",!0)}},Xe=i=>U(O=>{const G=new Set(O);return G.has(i)?G.delete(i):G.add(i),G}),Ze=async()=>{const i=[..._];if(i.length){ue(!0),J({phase:"planning",done:0,total:i.length,results:[]});try{const O=[];for(const le of i){const Oe=await T(`/api/admin/episodes/${le}/rerecord`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({dryRun:!0})}).catch(Be=>({error:Be.message}));O.push({id:le,...Oe})}const G=O.filter(le=>!le.error),ye=O.filter(le=>le.error);if(!G.length){o(`None of these can be re-recorded: ${ye.map(le=>`#${le.id} ${le.error}`).join("; ")}`,!0);return}const _e=G.reduce((le,Oe)=>le+(Oe.chars||0),0),fs=G.reduce((le,Oe)=>le+(Oe.apiCalls||0),0),bs=G.reduce((le,Oe)=>le+(Oe.approxUsd||0),0),gs=[`Re-record ${G.length} episode${G.length===1?"":"s"} in the voices their cast has now?`,"",...G.map(le=>`  #${le.id} ${le.title} — ${le.turns} turns, ${le.apiCalls}
66 call(s)`),"",`${_e.toLocaleString()} characters · ${fs} ElevenLabs call(s) · roughly $${bs.toFixed(2)}`,"","The previous audio stays in the bucket — nothing is deleted.","Each episode keeps its publish date, its cover and its newsletter.","The visual timeline is realigned to the new audio, so anything already","filmed from these episodes will need its keyframes re-planned.",...ye.length?["",`${ye.length} cannot be planned and will be skipped: ${ye.map(le=>`#${le.id}`).join(", ")}`]:[]];if(!confirm(gs.join(`
67`)))return;const Ie=[];for(let le=0;le<G.length;le++){const Oe=G[le];J({phase:"running",done:le,total:G.length,current:Oe.title,results:[...Ie]});const Be=await T(`/api/admin/episodes/${Oe.id}/rerecord`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({})}).catch(vs=>({error:vs.message}));Ie.push({id:Oe.id,title:Oe.title,error:Be?.error||(Be?.success?null:"failed"),durationSeconds:Be?.durationSeconds,timelineOk:Be?.timelineOk,timelineReason:Be?.timelineReason})}J({phase:"done",done:G.length,total:G.length,results:Ie});const Ye=Ie.filter(le=>le.error),ss=Ie.filter(le=>!le.error&&!le.timelineOk);Ye.length?o(`${Ie.length-Ye.length} re-recorded, ${Ye.length} failed: ${Ye.map(le=>`#${le.id} ${le.error}`).join("; ")}`,!0):ss.length?o(`${Ie.length} re-recorded, but ${ss.length} could not realign its timeline — check before any video pass.`,!0):o(`${Ie.length} episode${Ie.length===1?"":"s"} re-recorded`),U(new Set),A(),c?.()}finally{ue(!1)}}},h=async()=>{if(confirm("Produce an episode? This writes dialogue, renders audio and generates cover art.")){B(!0);try{const i=await T(`/api/admin/shows/${t}/episodes`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({guestAvatarId:d||null,researchPrompt:S,...j?{vibeOverrides:j}:{}})});i.error?o(i.error,!0):(o(`Episode ${i.episodeId} ${i.status==="published"?"published":"started"}`),E(""),D(""),k(null),A(),c?.())}catch{o("Failed to produce episode",!0)}finally{B(!1)}}},F=async i=>{M(i),Q(!1),X(null);try{const O=await T(`/api/admin/episodes/${i.episode_id}/vibe`);X(O?.error?{playful:.5,confrontational:.5,reflective:.5}:{playful:O.playful,confrontational:O.confrontational,reflective:O.reflective})}catch{X({playful:.5,confrontational:.5,reflective:.5})}},ne=async()=>{if(!C||!confirm(`Rewrite "${C.title}"?
68
69The existing script is discarded and a new one is written, then the whole episode is spoken again. This costs a script generation and a full voice render, and cannot be undone.`))return;const i=C.episode_id;g(i);try{const O=await T(`/api/admin/episodes/${i}/rewrite`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({vibeOverrides:V,newArtwork:ie})});O.error?o(O.error,!0):(o(`Episode ${i} rewritten — ${O.wordCount||"?"} words`),M(null),A(),c?.())}catch{o("Failed to rewrite episode",!0)}finally{g(null)}},Ce=async i=>{if(confirm(`Delete "${i.title}"? Unfinished episodes only — this can't be undone.`))try{const O=await T(`/api/admin/episodes/${i.episode_id}`,{method:"DELETE"});O.error?o(O.error,!0):(o("Episode removed"),A(),c?.())}catch{o("Failed to delete",!0)}};if(f)return e.jsx("div",{className:"loading-state",children:"Loading show…"});if(!v?.show)return null;const K=v.show,Fe=K.show_format==="session",He=K.show_format==="monologue",Ve=K.show_format==="interview",De=(v.episodes||[]).filter(i=>{if(!I)return!0;const O=I.toLowerCase();return i.title?.toLowerCase().includes(O)||i.guest_name?.toLowerCase().includes(O)||i.status?.toLowerCase().includes(O)}),ze=(v.episodes||[]).filter(i=>!["published","failed"].includes(i.status)).length;return e.jsxs("div",{className:"show-editor",children:[e.jsxs("div",{className:"show-editor-head",children:[e.jsxs("div",{children:[e.jsx("h3",{children:K.name}),e.jsxs("p",{className:"show-meta",children:[us[K.show_format]||K.show_format,[K.host_a_name,K.host_b_name].filter(Boolean).length?` · ${[K.host_a_name,K.host_b_name].filter(Boolean).join(" & ")}`:" · cast per episode",K.station_slug?` · /${K.station_slug}`:""]}),v.wrapHost&&e.jsxs("p",{className:"show-meta show-wrap-host",children:[v.wrapHost.profile_image_url&&e.jsx("img",{src:v.wrapHost.profile_image_url,alt:"",className:"show-wrap-avatar"}),"Opened & closed by ",v.wrapHost.name," — written after the episode, so she can introduce what actually happened and quote what was actually said."]})]}),ze>0&&e.jsxs("span",{className:"show-count warn",children:[ze," unfinished"]})]}),e.jsxs("div",{className:"show-section",children:[e.jsxs("label",{children:["Logo ",e.jsx("span",{className:"label-hint",children:"SVG (with its own fill colour) or transparent PNG · replaces the show name in the page hero"})]}),e.jsxs("div",{className:"show-produce-row",style:{alignItems:"center",gap:14},children:[K.logo_url?e.jsx("img",{src:K.logo_url,alt:`${K.name} logo`,style:{maxHeight:56,maxWidth:220,objectFit:"contain",background:"rgba(255,255,255,.06)",borderRadius:6,padding:6}}):e.jsx("span",{style:{height:56,width:140,flex:"0 0 auto",borderRadius:6,border:"1px dashed rgba(255,255,255,.18)",display:"flex",alignItems:"center",justifyContent:"center",textAlign:"center",padding:6,fontSize:11,lineHeight:1.4,color:"rgba(255,255,255,.42)"},children:"No logo yet"}),e.jsxs("label",{className:"btn-secondary",style:{cursor:ee?"wait":"pointer",margin:0},children:[ee?"Uploading…":K.logo_url?"Replace":"Upload",e.jsx("input",{type:"file",accept:"image/svg+xml,image/png,image/webp,image/jpeg",style:{display:"none"},disabled:ee,onChange:i=>{W(i.target.files?.[0]),i.target.value=""}})]}),K.logo_url&&e.jsx("button",{type:"button",className:"btn-secondary",disabled:ee,onClick:w,children:"Remove"})]})]}),e.jsxs("div",{className:"show-section",children:[e.jsxs("label",{children:["Cover art",e.jsx("span",{className:"label-hint",children:"16:9 photo · stored at 1600×900 · the show name prints over the BOTTOM of it, so keep the lower third quiet"})]}),e.jsxs("div",{className:"show-produce-row",style:{alignItems:"center",gap:14},children:[K.cover_image_url?e.jsx("span",{style:{position:"relative",width:200,aspectRatio:"16 / 9",flex:"0 0 auto",borderRadius:6,overflow:"hidden",background:"rgba(255,255,255,.06)"},children:e.jsx("img",{src:K.cover_image_url,alt:`${K.name} cover art`,style:{width:"100%",height:"100%",objectFit:"cover",display:"block"}})}):e.jsx("span",{style:{width:200,aspectRatio:"16 / 9",flex:"0 0 auto",borderRadius:6,border:"1px dashed rgba(255,255,255,.18)",display:"flex",alignItems:"center",justifyContent:"center",textAlign:"center",padding:8,fontSize:11,lineHeight:1.4,color:"rgba(255,255,255,.42)"},children:"No cover art yet"}),e.jsxs("label",{className:"btn-secondary",style:{cursor:ae?"wait":"pointer",margin:0},children:[ae?"Uploading…":K.cover_image_url?"Replace":"Upload",e.jsx("input",{type:"file",accept:"image/png,image/webp,image/jpeg",style:{display:"none"}
69,disabled:ae,onChange:i=>{Se(i.target.files?.[0]),i.target.value=""}})]}),K.cover_image_url&&e.jsx("button",{type:"button",className:"btn-secondary",disabled:ae,onClick:a,children:"Remove"})]}),e.jsxs("div",{className:"show-cover-gen",children:[e.jsxs("label",{className:"show-cover-gen-label",children:["Prompt",e.jsx("span",{className:"label-hint",children:"The show's own direction, pre-filled. Edit it, or write your idea and press Enhance."})]}),e.jsx("textarea",{id:`show-cover-prompt-${t}`,className:"show-cover-prompt",rows:8,value:p,onChange:i=>se(i.target.value),placeholder:"What this show's cover should look like…",disabled:n||Y}),e.jsxs("div",{className:"show-cover-gen-row",children:[e.jsx("button",{type:"button",className:"btn-primary",disabled:Y||n||!p.trim(),onClick:Ae,children:Y?"Generating…":"Generate cover"}),e.jsx("button",{type:"button",className:"btn-secondary",disabled:n||Y||!p.trim(),onClick:()=>Ee("enhance"),children:n?"Writing…":"Enhance"}),e.jsx("button",{type:"button",className:"btn-secondary",disabled:n||Y,onClick:()=>Ee("auto"),children:"Start over"}),e.jsx("span",{className:"hint",style:{margin:0},children:Y?"One image plus the logo layer — about half a minute.":"Costs one image generation."})]}),me.length>0&&e.jsxs(e.Fragment,{children:[e.jsxs("label",{className:"show-cover-gen-label",style:{marginTop:18},children:["Previous covers",e.jsx("span",{className:"label-hint",children:"Click one to put it back. Nothing is ever deleted."})]}),e.jsx("div",{className:"show-cover-versions",children:me.map(i=>e.jsxs("button",{type:"button",className:`show-cover-version${i.is_current?" is-current":""}`,title:i.prompt?i.prompt.slice(0,300):"Uploaded — no prompt",onClick:()=>!i.is_current&&Je(i.version_id),disabled:i.is_current,children:[e.jsx("img",{src:i.image_url,alt:"",loading:"lazy"}),e.jsx("span",{className:"show-cover-version-tag",children:i.is_current?"In use":i.source==="uploaded"?"Uploaded":"Generated"})]},i.version_id))})]})]})]}),!Fe&&!Ve&&e.jsxs("div",{className:"show-section",children:[e.jsx("label",{children:"Weekly story carousel"}),K.auto_carousel===null?e.jsx("p",{className:"hint",children:"Unavailable until migration 351 has been run."}):e.jsxs(e.Fragment,{children:[e.jsxs("label",{className:"booth-dress-logo",style:{margin:"2px 0 8px"},children:[e.jsx("input",{type:"checkbox",checked:!!K.auto_carousel,disabled:xe,onCh
69ange:N}),e.jsx("span",{children:"Post the week’s stories automatically when an episode publishes"})]}),e.jsx("p",{className:"hint",children:"One slide per story, drawn from this episode’s newsletter — its headlines, its summaries, its pictures. The first slide is a two-up portrait of the hosts with the show logo, the title and the week. Goes to Instagram, TikTok and the HypeTunes feed."})]})]}),e.jsxs("div",{className:"show-section",children:[e.jsx("label",{children:"Finished films"}),K.auto_distribute===null?e.jsx("p",{className:"hint",children:"Unavailable until migration 367 has been run."}):e.jsxs(e.Fragment,{children:[e.jsxs("label",{className:"booth-dress-logo",style:{margin:"2px 0 8px"},children:[e.jsx("input",{type:"checkbox",checked:!!K.auto_distribute,disabled:H,onChange:Re}),e.jsx("span",{children:"Push to the HypeTunes YouTube channel and Instagram when a film finishes"})]}),e.jsxs("p",{className:"hint",children:["Fires on the assembly that produces the video, not on the audio episode. The YouTube upload carries the episode’s own thumbnail; Instagram gets it as a Reel from the account pinned below. Each is posted once — a resumed render will not publish a second copy.",!K.ig_connection_id&&e.jsxs(e.Fragment,{children:[" ",e.jsx("strong",{children:"No Instagram account is pinned, so the Reel will be skipped"})," — an unattended post is not allowed to guess which account it comes from."]})]})]})]}),e.jsxs("div",{className:"show-section",children:[e.jsx("label",{children:"Filmed episodes"}),K.auto_film===null?e.jsx("p",{className:"hint",children:"Unavailable until migration 373 has been run."}):e.jsxs(e.Fragment,{children:[e.jsxs("label",{className:"booth-dress-logo",style:{margin:"2px 0 8px"},children:[e.jsx("input",{type:"checkbox",checked:!!K.auto_film,disabled:ge,onChange:r}),e.jsx("span",{children:"Film each new episode automatically, animate its cover, and post the video carousel"})]}),e.jsxs("p",{className:"hint",children:["Runs after the audio publishes, with no keyframe review: plan, keyframes, every take, assembly",K.auto_distribute?", then YouTube and an Instagram Reel":"",". Then the cover animation, an opening card lip-synced to the episode’s first line, and the carousel as video to Instagram and the HypeTunes feed — TikTok gets the stills. Takes hours, and costs 40–55 video takes per episode.",!K.auto_carousel&&e.jsxs(e.Fragment,{children:[" ",e.jsx("strong",{children:"The carousel switch is off, so no carousel is posted."})]})]})]})]}),K.social_pins_available!==!1&&e.jsxs("div",{className:"show-section",children:[e.jsx("label",{children:"Where this show posts"}),e.jsxs("div",{className:"show-social-pins",children:[e.jsxs("label",{className:"show-pin-row",children:[e.jsx("span",{children:"Instagram"}),e.jsxs("select",{value:K.ig_connection_id??"",disabled:xe||!Z,onChange:i=>Te("ig_connection_id",i.target.value),children:[e.jsx("option",{value:"",children:"Default (guessed — not recommended)"}),(Z?.instagram||[]).map(i=>e.jsxs("option",{value:i.id,children:[i.ig_username?`@${i.ig_username}`:i.page_name||`connection ${i.id}`,i.linked_avatars?` — ${i.linked_avatars}`:"",i.is_active?"":" (disconnected)"]},i.id))]})]}),Array.isArray(K.ig_also_connection_ids)&&e.jsxs("div",{className:"show-pin-row show-pin-also",children:[e.jsx("span",{children:"Carousel also to"}),e.jsx("div",{className:"show-pin-also-list",children:(Z?.instagram||[]).filter(i=>i.is_active&&i.id!==K.ig_connection_id).map(i=>e.jsxs("label",{className:"show-pin-also-item",children:[e.jsx("input",{type:"checkbox",checked:K.ig_also_connection_ids.map(Number).includes(i.id),disabled:xe,onCh
69ange:()=>$e(i.id)}),e.jsxs("span",{children:["@",i.ig_username||i.page_name||i.id,i.linked_avatars?` — ${i.linked_avatars}`:""]})]},i.id))})]}),e.jsxs("label",{className:"show-pin-row",children:[e.jsx("span",{children:"TikTok"}),e.jsxs("select",{value:K.tiktok_connection_id??"",disabled:xe||!Z,onChange:i=>Te("tiktok_connection_id",i.target.value),children:[e.jsx("option",{value:"",children:"Default (guessed — not recommended)"}),(Z?.tiktok||[]).map(i=>e.jsxs("option",{value:i.id,children:[i.username?`@${i.username}`:i.display_name||`connection ${i.id}`,i.is_active?"":" (disconnected)"]},i.id))]})]}),e.jsx("p",{className:"hint",style:{margin:"4px 0 0"},children:"The artists post from their own accounts, so the list is mostly theirs — the avatar beside each handle is how you tell the brand account apart. Left on “Default” the publisher guesses, and it guessed wrong the first time."})]})]}),e.jsxs("div",{className:"show-section",children:[e.jsx("label",{children:"Produce an episode"}),Fe?e.jsx("p",{className:"hint",children:"The Session casts a pair and a topic first — use the producer panel above."}):He?e.jsx("p",{className:"hint",children:"The Booth casts one artist and finds a story in their history first — use the producer panel above."}):Ve?e.jsxs(e.Fragment,{children:[e.jsxs("p",{className:"hint",children:[(v.eligibleGuests||[]).length," artist(s) eligible and not yet interviewed on this show."]}),e.jsxs("div",{className:"show-produce-row",children:[e.jsxs("select",{value:d,onChange:i=>D(i.target.value),children:[e.jsx("option",{value:"",children:"Choose a guest…"}),(v.eligibleGuests||[]).map(i=>e.jsxs("option",{value:i.avatar_id,children:[i.name,i.main_genre?` — ${i.main_genre}`:""]},i.avatar_id))]}),e.jsx("button",{className:"btn-primary",onClick:h,disabled:b||!d,children:b?"Recording…":"Record"})]})]}):e.jsxs(e.Fragment,{children:[e.jsx("textarea",{rows:3,value:S,onChange:i=>E(i.target.value),placeholder:"What should this episode be about? This goes to research before the hosts get it."}),e.jsx("div",{className:"show-produce-row",children:e.jsx("button",{className:"btn-primary",onClick:h,disabled:b||!S.trim(),children:b?"Recording…":"Record"})})]}),!Fe&&!He&&L&&e.jsxs("div",{className:"show-epvibe",children:[e.jsxs("label",{className:"show-epvibe-toggle",children:[e.jsx("input",{type:"checkbox",checked:!!j,disabled:b,onChange:i=>k(i.target.checked?{...L}:null)}),e.jsx("span",{children:"Give this episode its own mood"})]}),j?e.jsxs(e.Fragment,{children:[e.jsx("p",{className:"hint",children:"Set before a word is written, so it steers the dialogue as well as the artwork. Starts from the show’s default — move what you want changed."}),e.jsx(Qe,{value:j,onChange:k,disabled:b})]}):e.jsxs("p",{className:"hint",children:["Using the show default below (",Math.round((L.playful??0)*100),"% playful,"," ",Math.round((L.confrontational??0)*100),"% confrontational,"," ",Math.round((L.reflective??0)*100),"% reflective)."]})]})]}),L&&e.jsxs("div",{className:"show-section",children:[e.jsx("label",{children:"Default vibe"}),e.jsx("p",{className:"hint",children:"Applied to every episode of this show unless an individual episode overrides it."}),e.jsx(Qe,{value:L,onChange:z,disabled:m}),e.jsx("button",{className:"btn-primary",onClick:he,disabled:m,children:m?"Saving…":"Save settings"})]}),je&&e.jsx(Bs,{show:K,episodes:v.episodes||[],showMsg:o}),ps[K.show_format]&&e.jsx(Ys,{show:K,episodes:v.episodes||[],showMsg:o,lane:ps[K.show_format]}),e.jsxs("div",{className:"show-section",children:[e.jsxs("label",{children:["Episodes ",e.jsxs("span",{className:"track-count",children:["(",De.length,"/",(v.episodes||[]).length,")"]})]}),e.jsxs("div",{className:"search-box",children:[e.jsx(de,{name:"magnifying-glass",size:14}),e.jsx("input",{type:"text",value:I,onChange:i=>q(i.target.value),placeholder:"Search episodes…"})]}),C&&e.jsxs("div",{className:"ep-rewrite-panel",children:[e.jsxs("div",{className:"ep-rewrite-head",children:[e.jsxs("div",{children:[e.jsxs("strong",{children:["Rewrite “",C.title,"”"]}),e.jsx("p",{className:"hint",children:"The script is discarded and written again, then the episode is spoken again from the new words. The episode keeps its id, its guest and its publish date — so the artist is not spent, and nothing has to be deleted."})]}),e.jsx("button",{type:"button",className:"btn-secondary",disabled:u,onClick:()=>M(null),children:"Cancel"})]}),V?e.jsxs(e.Fragment,{children:[e.jsx("label",{className:"ep-rewrite-label",children:"Mood for the rewrite"}),e.jsx(Qe,{value:V,onChange:X,disabled:!!u})]}):e.jsx("p",{className:"hint",children:"Loading the episode’s mood…"}),e.jsxs("label",{className:"ep-rewrite-art",children:[e.jsx("input",{type:"checkbox",checked:ie,disabled:!!u,onChange:i=>Q(i.target.checked)}),e.jsx("span",{children:"Regenerate the cover too — the artwork reads the same dials, so a big mood change leaves the old cover disagreeing with the new episode."})]}),e.jsx("button",{className:"btn-primary",disabled:!!u||!V,onClick:ne,children:u?"Rewriting…":"Rewrite episode"})]}),_.size>0&&e.jsxs("div",{className:"ep-rerecord-bar",children:[e.jsxs("span",{className:"ep-rerecord-count",children:[_.size," selected"]}),e.jsx("button",{type:"button",className:"btn-primary",disabled:te,onClick:Ze,title:"Speak these episodes again in the voices their cast has now. You will see the cost before anything is spent.",children:te?re?.phase==="planning"?"Checking…":`Re-recording ${(re?.done??0)+1}/${re?.total??_.size}…`:"Re-record audio"}),e.jsx("button",{type:"button",className:"btn-secondary",disabled:te,onClick:()=>U(new Set),children:"Clear"}),e.jsx("span",{className:"hint",style:{margin:0},children:"Re-renders the speech from the episode’s own script — nothing is rewritten. The old audio stays in the bucket, the publish date and cover are untouched, and the visual timeline is realigned to the new audio."})]}),te&&re?.phase==="running"&&e.jsxs("p",{className:"hint",children:["Now re-recording “",re.current,"” — leave this open until it finishes."]}),re?.phase==="done"&&e.jsx("ul",{className:"ep-rerecord-results",children:re.results.map(i=>
69e.jsxs("li",{className:i.error?"is-bad":i.timelineOk?"is-ok":"is-warn",children:["#",i.id," ",i.title," — ",i.error?`failed: ${i.error}`:`${i.durationSeconds}s${i.timelineOk?"":` · ⚠ timeline not realigned (${i.timelineReason}) — re-plan before filming`}`]},i.id))}),De.length===0?e.jsx("p",{className:"empty-state",children:"No episodes yet"}):e.jsx("ul",{className:"ep-list",children:De.map(i=>e.jsxs("li",{className:`ep-row ${i.status!=="published"?"is-pending":""}`,children:[i.status==="published"&&i.has_audio?e.jsx("input",{type:"checkbox",className:"ep-pick",checked:_.has(i.episode_id),disabled:te,onChange:()=>Xe(i.episode_id),title:"Select for re-recording","aria-label":`Select "${i.title}" for re-recording`}):e.jsx("span",{className:"ep-pick-ph","aria-hidden":"true"}),e.jsx("div",{className:"ep-art",style:i.cover_image_url?{backgroundImage:`url(${i.cover_image_url})`}:void 0}),e.jsxs("div",{className:"ep-meta",children:[e.jsx("div",{className:"ep-title",children:i.title}),e.jsxs("div",{className:"ep-sub",children:[[i.guest_name,i.guest_b_name].filter(Boolean).join(" × ")||us[K.show_format],i.published_at?` · ${new Date(i.published_at).toLocaleDateString()}`:"",i.error?` · ${String(i.error).slice(0,60)}`:""]})]}),e.jsx("span",{className:`ep-status ${i.status}`,children:i.status}),e.jsx(Ws,{episode:i,token:We()}),e.jsx("button",{type:"button",className:"btn-secondary ep-rewrite",disabled:te||u===i.episode_id,onClick:()=>F(i),title:"Write this episode again — new dialogue and new audio",children:u===i.episode_id?"Rewriting…":"Rewrite"}),i.status!=="published"&&e.jsx("button",{className:"remove-btn",onClick:()=>Ce(i),title:"Delete unfinished episode",children:e.jsx(de,{name:"xmark",size:14})})]},i.episode_id))})]})]})}function ht(){const[t,o]=s.useState(null),[c,v]=s.useState(!0),[l,f]=s.useState(null),[R,L]=s.useState(""),[z,m]=s.useState(""),x=(d,D=!1)=>{D?L(d):m(d),setTimeout(()=>{L(""),m("")},3500)},b=async()=>{try{const d=await T("/api/admin/shows");d.error?x(d.error,!0):(o(d),f(D=>D??d.shows?.[0]?.set_id??null))}catch{x("Failed to load shows",!0)}finally{v(!1)}};if(s.useEffect(()=>{b()},[]),c)return e.jsx("div",{className:"loading-state",children:"Loading shows…"});const B=(t?.shows||[]).find(d=>d.set_id===l);return e.jsxs("div",{className:"shows-tab",children:[R&&e.jsx("div",{className:"error-message",children:R}),z&&e.jsx("div",{className:"success-message",children:z}),e.jsxs("div",{className:"station-selector",children:[e.jsx("h3",{children:"Select Show"}),e.jsx("div",{className:"station-buttons",children:(t?.shows||[]).map(d=>e.jsxs("button",{className:`station-btn ${l===d.set_id?"active":""}`,onClick:()=>f(d.set_id),children:[e.jsx("span",{className:"station-name",children:d.name}),e.jsxs("span",{className:"station-count",children:[d.published_count," episode",d.published_count===1?"":"s",d.in_flight_count>0?` · ${d.in_flight_count} unfinished`:""]})]},d.set_id))})]}),B?.show_format==="session"&&e.jsx(Ms,{showMsg:x}),B?.show_format==="monologue"&&e.jsx(st,{showMsg:x}),l&&e.jsx(dt,{setId:l,showMsg:x,onChanged:b})]})}const ut=[{href:"/studio/daw",icon:"🎤",name:"Beat → Song",desc:"Send a Logic bounce (or any beat) back in as a remix base."},{href:"/daw-console.html",external:!0,icon:"🎛️",name:"DAW Console",desc:"Status board for the DAW / sound-sourcing workstream."}];function pt(){return e.jsx("div",{className:"tools-tab",children:e.jsx("div",{className:"tools-grid",children:ut.map(t=>e.jsxs("a",{className:"tool-card",href:t.href,...t.external?{target:"_blank",rel:"noopener noreferrer"}:{},children:[e.jsx("span",{className:"tool-icon",children:t.icon}),e.jsxs("span",{className:"tool-name",children:[t.name,t.external?" ↗":""]}),e.jsx("span",{className:"tool-desc",children:t.desc})]},t.href))})})}function mt(){return e.jsxs("div",{className:"sounds-tab",children:[e.jsxs("div",{className:"sounds-bar",children:[e.jsx("p",{className:"hint",children:"Review and classify self-hosted library sounds below. The Splice browser opens separately — it needs the local bridge running."}),e.jsx("a",{className:"btn-ghost",href:"/studio/sounds",children:"🎚️ Splice browser ↗"})]}),e.jsx(ks,{})]})}
69function yt(){const[t,o]=s.useState("stations"),c=[{key:"stations",icon:"📻",label:"Stations"},{key:"shows",icon:"🎙️",label:"Shows"},{key:"playlists",icon:"🎵",label:"Playlists"},{key:"features",icon:"⭐",label:"Features"},{key:"scenes",icon:"🎬",label:"Scenes"},{key:"knowledge",icon:"📚",label:"Knowledge"},{key:"sounds",icon:"🥁",label:"Sounds"},{key:"vj",icon:"📺",label:"VJ"},{key:"tools",icon:"🧰",label:"Tools"},{href:"/admin/voice-ingest",icon:"🎚️",label:"Voices"},{href:"/admin/deck-requests",icon:"📊",label:"Requests"}],l={stations:os,shows:ht,playlists:tt,features:ct,scenes:rt,knowledge:ws,sounds:mt,vj:Rs,tools:pt}[t]||os;return e.jsxs("div",{className:"admin-station-page",children:[e.jsxs("div",{className:"admin-header",children:[e.jsx("h1",{children:"Admin Console"}),e.jsx("p",{children:"Stations, shows, playlists, features and the platform's knowledge base."})]}),e.jsx("nav",{className:"page-tabs",children:c.map(f=>f.href?e.jsxs("a",{className:"page-tab",href:f.href,children:[e.jsx("span",{className:"page-tab-icon",children:f.icon}),f.label]},f.href):e.jsxs("button",{className:`page-tab ${t===f.key?"active":""}`,onClick:()=>o(f.key),children:[e.jsx("span",{className:"page-tab-icon",children:f.icon}),f.label]},f.key))}),e.jsx(l,{})]})}export{yt as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.