PageSourceSearch

https://dreaminginsights.com/assets/main-BXKI91M_.js

js dreaminginsights.com collected 2026-10-02 01:27:36 UTC 25,585 bytes, 1 lines download raw bytes

1import{r,j as e,b as we,a as Se,c as Ne,d as _e,e as Te,f as Ce,v as Xe,g as Ke,h as Ze,i as Qe,k as et,l as tt,m as st,n as at,o as nt,p as rt,q as it,s as ot,t as lt}from"./compute-B-idOUy9.js";import{I as ct,E as dt,D as ht,a as ut,b as mt,c as xt,C as gt,G as pt,d as ft,e as yt,T as jt}from"./DayStatsSection-BYTs5wmB.js";import"./App-Ch7ULu2a.js";function bt({charts:a,onConfirm:u,onClose:n}){var _,U;const[m,p]=r.useState(""),[k,D]=r.useState("");function q(){if(m&&!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(m)){D("Please enter a valid email address, or leave blank.");return}u(m.trim()||null)}const M=((_=a==null?void 0:a.rawWatchedVideos)==null?void 0:_.filter(b=>b.watched).length)??0,A=((U=a==null?void 0:a.rawExternalEntries)==null?void 0:U.length)??0,L=(a==null?void 0:a.dayTimeCount)??0;return e.jsx("div",{className:"modal-backdrop",onClick:n,children:e.jsxs("div",{className:"modal",onClick:b=>b.stopPropagation(),role:"dialog","aria-modal":"true","aria-label":"Research consent",children:[e.jsxs("div",{className:"modal__header",children:[e.jsx("h2",{children:"Contribute to Dreaming Insights Research"}),e.jsx("button",{className:"modal__close",onClick:n,"aria-label":"Close",children:"✕"})]}),e.jsxs("div",{className:"modal__body",children:[e.jsx("h3",{children:"What will be uploaded"}),e.jsx("p",{children:"The following data from your current session will be sent to our secure research database:"}),e.jsxs("ul",{children:[e.jsxs("li",{children:[e.jsx("strong",{children:"Watched videos history"})," — ",M.toLocaleString()," video records (video IDs and timestamps)"]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Daily watch time"})," — ",L.toLocaleString()," days of viewing data"]}),A>0&&e.jsxs("li",{children:[e.jsx("strong",{children:"External content"})," — ",A.toLocaleString()," external viewing records"]})]}),e.jsx("h3",{children:"How your data will be used"}),e.jsx("p",{children:"Contributed data is used exclusively for research into language acquisition patterns. This includes building recommendation engines, identifying effective content pathways, and creating tools that help learners progress more effectively on Dreaming Spanish and Dreaming French."}),e.jsx("h3",{children:"Your privacy"}),e.jsxs("ul",{children:[e.jsxs("li",{children:["Your data is stored under an ",e.jsx("strong",{children:"anonymised identifier"})," — a one-way hash derived from your earliest viewing records. It cannot be traced back to your Dreaming Spanish account."]}),e.jsx("li",{children:"No name, account details, or personal information are collected unless you choose to provide your email below."}),e.jsxs("li",{children:["Your data is ",e.jsx("strong",{children:"never sold"})," or shared with third parties outside the Dreaming Insights research programme."]}),e.jsx("li",{children:"Once opted in, your data refreshes automatically every 7 days when you use this tool. You can opt out at any time by clearing this site's local storage."})]}),e.jsx("h3",{children:"Optional: stay in the loop"}),e.jsx("p",{children:"If you'd like to be notified about research projects or findings that used your data, you can leave your email address below. This is entirely optional and stored separately from your viewing data."}),e.jsxs("div",{className:"research-email-field",children:[e.jsx("label",{htmlFor:"research-email",children:"Email address (optional)"}),e.jsx("input",{id:"research-email",type:"email",placeholder:"[email protected]",value:m,onChange:b=>{p(b.target.value),D("")},autoComplete:"email"}),k&&e.jsx("span",{className:"research-email-error",children:k})]})]}),e.jsxs("div",{className:"modal__footer",children:[e.jsx("button",{className:"modal__cancel-btn",onClick:n,children:"Cancel"}),e.jsx("button",{className:"modal__confirm-btn",onClick:q,children:"Confirm & Submit"})]})]})})}function vt({urls:a,onConfirm:u,onClose:n,status:m}){return e.jsx("div",{className:"modal-backdrop",onClick:n,children:e.jsxs("div",{className:"modal",onClick:p=>p.stopPropagation(),role:"dialog","aria-modal":"true","aria-label":"Submit YouTube URLs",children:[e.jsxs("div",{className:"modal__header",children:[e.jsx("h2",{children:"Submit YouTube URLs for labelling"}),e.jsx("button",{className:"modal__close",onClick:n,"aria-label":"Close",children:"✕"})]}),e.jsxs("div",{className:"modal__body",children:[e.jsx("h3",{children:"What will be sent"}),e.jsxs("p",{children:["The following ",e.jsxs("strong",{children:[a.length," YouTube URL",a.length!==1?"s":""]})," will be sent to the Dreaming Insights labelling service to identify the channel name for each video."]}),e.jsx("ul",{children:a.map(p=>e.jsx("li",{style:{fontFamily:"monospace",fontSize:"0.8rem",wordBreak:"break-all"},children:p},p))}),e.jsx("h3",{children:"Privacy notice"}),e.jsxs("p",{style:{background:"#fff8e1",border:"1px solid #ffcc80",borderRadius:6,padding:"0.75rem 1rem",fontSize:"0.875rem"},children:["⚠️ This is an exception to our usual on-device privacy policy. These YouTube URLs will be transmitted outside your browser to our labelling API. ",e.jsx("strong",{children:"No other information about you is 
1included"})," — only the video URLs listed above."]}),e.jsx("p",{children:"Once processed, channel names will be stored in our lookup table so future users with the same videos benefit automatically."}),m==="success"&&e.jsx("p",{style:{color:"#2e7d32",fontWeight:600},children:"✅ URLs submitted. Reloading your data…"}),m==="error"&&e.jsx("p",{style:{color:"#c62828"},children:"Something went wrong submitting the URLs. Please try again."})]}),e.jsxs("div",{className:"modal__footer",children:[e.jsx("button",{className:"modal__cancel-btn",onClick:n,children:"Cancel"}),e.jsx("button",{className:"modal__confirm-btn",onClick:u,disabled:m==="submitting"||m==="success",children:m==="submitting"?"Submitting…":"Submit URLs"})]})]})})}const wt="https://chromewebstore.google.com/detail/dreaming-insights-extensi/cafjfdpcidjkbpcjpgdkgoakjdiicbmh",De="https://buymeacoffee.com/dreaminginsights",St="https://e6tdv2lt2k6r4zbab3fvriolnm0knhlj.lambda-url.eu-west-2.on.aws/",Ee="ck1pkSt5xPsPcyK6Nk23PTorVG8NXwpD",Nt="https://ge2x6hgrb6tkm4fh7b3leopj4u0vcajp.lambda-url.eu-west-2.on.aws/",_t=[/[?&]v=([A-Za-z0-9_-]{11})/,/youtu\.be\/([A-Za-z0-9_-]{11})/,/\/shorts\/([A-Za-z0-9_-]{11})/,/\/embed\/([A-Za-z0-9_-]{11})/,/\/v\/([A-Za-z0-9_-]{11})/];function Tt(a){for(const u of _t){const n=a.match(u);if(n)return n[1]}return null}function Ct(a){return/youtube\.com|youtu\.be/i.test(a)}const ke="ds-stats-research";async function Et(a){if(!a||a.length<2)return null;const u=a[0],n=a[1],m=`${u.date}:${u.timeSeconds}:${n.date}:${n.timeSeconds}`,p=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(m));return Array.from(new Uint8Array(p)).map(k=>k.toString(16).padStart(2,"0")).join("")}const R=[{level:1,minHours:0,maxHours:50},{level:2,minHours:50,maxHours:150},{level:3,minHours:150,maxHours:300},{level:4,minHours:300,maxHours:600},{level:5,minHours:600,maxHours:1e3},{level:6,minHours:1e3,maxHours:1500},{level:7,minHours:1500,maxHours:1/0}];function kt(a){var n;const u=R.filter(m=>a>=m.minHours);return((n=u[u.length-1])==null?void 0:n.level)??1}function Dt({onManual:a}){return e.jsxs("div",{className:"extension-gate",children:[e.jsxs("div",{className:"extension-gate__inner",children:[e.jsxs("div",{className:"extension-gate__header",children:[e.jsx("h1",{children:"Dreaming Insights"}),e.jsx("p",{children:"Choose how you'd like to get started."}),e.jsx("a",{href:"/demo.html",className:"gate-demo-link",children:"See an example first →"}),e.jsx("a",{href:"/meta.html",className:"gate-demo-link",children:"Catalogue statistics →"}),e.jsx("a",{href:De,target:"_blank",rel:"noreferrer",className:"gate-coffee-link",children:"☕ Buy me a coffee"})]}),e.jsxs("div",{className:"extension-gate__options",children:[e.jsxs("div",{className:"gate-option gate-option--primary",children:[e.jsx("div",{className:"gate-option__icon",children:"🧩"}),e.jsx("h2",{children:"Use the extension"}),e.jsxs("p",{className:"gate-option__desc",children:["The ",e.jsx("strong",{children:"Dreaming Insights"})," Chrome extension fetches your data automatically whenever you visit your Dreaming Spanish or Dreaming French progress page. No manual steps required."]}),e.jsx("a",{className:"gate-option__btn gate-option__btn--primary",href:wt,target:"_blank",rel:"noreferrer",children:"Install Dreaming Insights →"}),e.jsx("p",{className:"gate-option__hint",children:"After installing, refresh this page."})]}),e.jsxs("div",{className:"gate-option",children:[e.jsx("div",{className:"gate-option__icon",children:"📋"}),e.jsx("h2",{children:"Import manually"}),e.jsx("p",{className:"gate-option__desc",children:"You can paste JSON directly from the Dreaming Spanish API. You'll need to be logged in and comfortable opening browser DevTools."}),e.jsxs("ol",{className:"gate-option__steps",children:[e.jsxs("li",{children:["Log in to Dreaming Spanish and open"," ",e.jsx("strong",{children:"https://app.dreaming.com/spanish/progress"})," (or your profile page)."]}),e.jsxs("li",{children:["Open DevTools (",e.jsx("kbd",{children:"F12"})," or ",e.jsx("kbd",{children:"⌥⌘I"}),") and go to the"," ",e.jsx("strong",{children:"Network"})," tab. Reload the page."]}),e.jsxs("li",{children:["Filter by ",e.jsx("code",{children:"watchedVideos"})," and copy the JSON response — this is the ",e.jsx("strong",{children:"required"})," data source."]}),e.jsxs("li",{children:["Optionally repeat for ",e.jsx("code",{children:"externalTimes"})," and"," ",e.jsx("code",{children:"dayWatchedTime"})," to unlock additional charts."]})]}),e.jsx("button",{className:"gate-option__btn gate-option__btn--secondary",onClick:a,children:"Continue with manual import →"})]})]})]}),e.jsxs("footer",{className:"site-footer",children:[e.jsx("a",{href:"https://forms.gle/F7FzqFdQ9yA5XgtJA",target:"_blank",rel:"noreferrer",children:"Contact / Support"}),e.jsx("span",{className:"site-footer__sep",children:"·"}),e.jsx("a",{href:"/privacy.html",target:"_blank",rel:"noreferrer",children:"Privacy Policy"}),e.jsx("span",{className:"site-footer__sep",children:"·"}),e.jsx("a",{href:"https://github.com/jcholyhead/ds-insights-web",target:"_blank",rel:"noreferrer",children:"Source Code"})]})]})}function ie({label:a,status:u}){const n=u==="valid";return e.jsxs("div",{className:`status-card ${n?"status-card--ok":"status-card--missing"}`,children:[e.jsx("span",{className:"status-icon",children:n?"✓":"¿"}),e.jsx("span",{className:"status-label",children:a})]})}function Ht(){const[a,u]=r.useState(()=>document.documentElement.dataset.dreamingInsights==="true"?"installed":"checking");r.useEffect(()=>{if(a==="installed")return;const t=c=>{var d;(d=c.detail)!=null&&d.installed&&u("installed")};document.addEventListener("dreaming-insights-ready",t);const o=setTimeout(()=>u(c=>c==="checking"?"not-installed":c),1500);return()=>{document.removeEventListener("dreaming-insights-ready",t),clearTimeout(o)}},[]);const[n,m]=r.useState(""),[p,k]=r.useState(""),[D,q]=r.useState("es"),[M,A]=r.useState(!1),L=r.useRef({}),_=r.useRef(null),U=r.useRef(!1),[b,He]=r.useState(""),[oe,le]=r.useState(!1),[s,ce]=r.useState(null),[w,W]=r.useState(1),[N,V]=r.useState(1),[de,he]=r.useState(""),[ue,me]=r.useState([]),[xe,ge]=r.useState([]),[T,Ie]=r.useState(()=>{try{return JSON.parse(localStorage.getItem(ke))}catch{return null}}),[O,X]=r.useState(null),[Pe,K]=r.useState(!1),[F,pe]=r.useState([]),[Re,Z]=r.useState(!1),[Ae,H]=r.useState("idle");async function fe(){var t,o;he(""),me([]),ge([]),ce(null),A(!0);try{let c;try{c=JSON.parse(n)}catch{throw new Error("Invalid JSON in the watched-videos field.")}const d=c.watchedVideos;if(!Array.isArray(d))throw new Error('watched-videos.json must have a top-level "watchedVideos" array.');const j=(t=c.language)==null?void 0:t.toLowerCase(),x=["es","fr"].includes(j??D)?j??D:"es";
1if(!L.current[x]){const i=await fetch(`/videos-${x}.json`);if(!i.ok)throw new Error(`Failed to load video catalogue (${x}): HTTP ${i.status}`);const y=await i.json();L.current[x]={videos:y.videos,guides:y.guides||[]}}const{videos:I,guides:te}=L.current[x];let g=[];if(p.trim()){let i;try{i=JSON.parse(p)}catch{throw new Error("Invalid JSON in the external-time field.")}if(!Array.isArray(i.externalTimes))throw new Error('external-time.json must have a top-level "externalTimes" array.');const y=Xe(i.externalTimes);y.warnings.length>0&&me(y.warnings),y.plottable&&(g=i.externalTimes)}const J=i=>Ct(i.externalVideoUrl||"")?i.externalVideoUrl:null;if(g.filter(i=>J(i)).length>0){let i={};try{const E=await fetch("/youtube-channels.json");E.ok&&(i=await E.json())}catch{}const y=[],P=new Set;g=g.map(E=>{const re=J(E);if(!re)return E;const $=Tt(re);return $?i[$]?{...E,description:i[$]}:(P.has($)||(P.add($),y.push(re)),E):E}),pe(y)}else pe([]);let C=[];if(b.trim()){let i;try{i=JSON.parse(b)}catch{throw new Error("Invalid JSON in the day watched time field.")}const y=Array.isArray(i)?i:i.dayWatchedTime??i.data??null;if(!Array.isArray(y))throw new Error('Day watched time data must be a JSON array (or an object with a "dayWatchedTime" array).');const P=Ke(y);P.warnings.length>0&&ge(P.warnings),P.plottable&&(C=y)}const v=new Map(I.map(i=>[i._id,i])),B=new Map(te.map(i=>[i.name,i.country])),Y=new Date,G=`${Y.getFullYear()}-${String(Y.getMonth()+1).padStart(2,"0")}-${String(Y.getDate()).padStart(2,"0")}`,f=Ze(C),se=Qe(v,d,g),S=et(se,f==null?void 0:f.points),z=tt(S),ae=st(S),l=at(S),{slices:Ye,totalHours:$e}=Te(v,d,g),ne=we(v,d),Me=Ne(v,d,B),{slices:We,totalHours:Ve}=Se(ne),be=_e(v,d,20),Je=Ce(S,z,ne,be),Be=nt(v,d,g),Ge=rt(v,d,g),ze=it(C,g),qe=ot(C,G),ve=(o=f==null?void 0:f.points)!=null&&o.length?f.points[f.points.length-1].cumulativeHours:S.length?S[S.length-1].cumulativeHours:0;ce({fullTimeline:S,videosById:v,difficultyData:z,appearances:ae,contentSlices:Ye,contentTotalHours:$e,guideCountryMap:B,countrySlices:Me,guideData:ne,guideSlices:We,guidesTotalHours:Ve,tagData:be,infoPanel:Je,exportRows:Be,summaryRows:Ge,allConsumptionPoints:l,hasExternalData:g.length>0,dailyPoints:(f==null?void 0:f.points)??[],dsVsExternalPoints:ze,dayStats:qe,totalHours:ve,rawWatchedVideos:d,rawExternalEntries:g,dayTimeEntries:C,dayTimeCount:C.length}),W(1),V(kt(ve))}catch(c){he(c.message)}finally{A(!1)}}const h=r.useMemo(()=>{if(!s)return null;const t=R.find(l=>l.level===w),o=R.find(l=>l.level===N),c=t.minHours,d=o.maxHours,j=l=>l>=c&&(d===1/0||l<d),x=s.fullTimeline.filter(l=>j(l.cumulativeHours)),I=x.filter(l=>l.videoId!=null).map(l=>({watched:!0,videoId:l.videoId})),te=x.filter(l=>l._ext!=null).map(l=>
1l._ext),g=we(s.videosById,I),{slices:J,totalHours:je}=Se(g),C=Ne(s.videosById,I,s.guideCountryMap),v=_e(s.videosById,I,20),{slices:B,totalHours:Y}=Te(s.videosById,I,te),G=s.difficultyData.filter(l=>j(l.x)),f=s.appearances.filter(l=>j(l.cumulativeHours)),se=s.allConsumptionPoints.filter(l=>j(l.cumulativeHours)),S=s.dailyPoints.filter(l=>j(l.cumulativeHours)),z=s.dsVsExternalPoints.filter(l=>j(l.cumulativeHours)),ae=Ce(x,G,g,v);return{...s,guideData:g,guideSlices:J,guidesTotalHours:je,countrySlices:C,tagData:v,contentSlices:B,contentTotalHours:Y,difficultyData:G,appearances:f,consumptionPoints:se,dailyPoints:S,dsVsExternalPoints:z,infoPanel:ae,xMin:c,xMax:x.length?x[x.length-1].cumulativeHours:c}},[s,w,N]);async function Le(){H("submitting");try{const t=await fetch(Nt,{method:"POST",headers:{"Content-Type":"application/json","X-Api-Key":Ee},body:JSON.stringify({urls:F})});if(!t.ok)throw new Error(`HTTP ${t.status}`);H("success"),setTimeout(()=>{var o;Z(!1),H("idle"),(o=_.current)==null||o.call(_)},1200)}catch{H("error")}}async function ye(t,o=null){X("uploading");try{const c=await Et(t.dayTimeEntries);if(!c)throw new Error("Could not compute hash");const d={hash:c,watchedVideos:t.rawWatchedVideos,externalTimes:t.rawExternalEntries,dayWatchedTime:t.dayTimeEntries};o&&(d.email=o);const j=await fetch(St,{method:"POST",headers:{"Content-Type":"application/json","X-Api-Key":Ee},body:JSON.stringify(d)});if(!j.ok)throw new Error(`HTTP ${j.status}`);const x={optedIn:!0,hash:c,lastUpload:new Date().toISOString()};Ie(x),localStorage.setItem(ke,JSON.stringify(x)),X("done")}catch(c){console.error("Research upload failed:",c),X("error")}}r.useEffect(()=>{if(!s||!(T!=null&&T.optedIn)||!s.dayStats||s.dayTimeCount<7)return;(Date.now()-new Date(T.lastUpload).getTime())/864e5>7&&ye(s)},[s]),r.useEffect(()=>{if(n.trim())try{const t=JSON.parse(n);Array.isArray(t==null?void 0:t.watchedVideos)&&localStorage.setItem("ds-insights-watched",n)}catch{}},[n]),r.useEffect(()=>{if(U.current||a!=="installed"||!n.trim())return;U.current=!0;const t=setTimeout(()=>_.current(),300);return()=>clearTimeout(t)},[a,n]);function Ue(t){t<w?W(t):t>N?V(t):t===w&&t===N||(t===w?W(t+1):t===N?V(t-1):(W(t),V(t)))}if(a==="not-installed")return e.jsx(Dt,{onManual:()=>{u("manual"),le(!0)}});if(a==="checking")return null;_.current=fe;const Oe=n.trim()&&!M;function Q(t,o){if(!t.trim())return"missing";try{const c=JSON.parse(t);return o(c)?"valid":"invalid"}catch{return"invalid"}}const ee={watched:Q(n,t=>Array.isArray(t==null?void 0:t.watchedVideos)),external:Q(p,t=>Array.isArray(t==null?void 0:t.externalTimes)),dayTime:Q(b,t=>Array.isArray(t)||Array.isArray(t==null?void 0:t.dayWatchedTime)||Array.isArray(t==null?void 0:t.data))},Fe=!!(s!=null&&s.dayStats&&(s==null?void 0:s.dayTimeCount)>=7);return e.jsxs(e.Fragment,{children:[e.jsx("header",{children:e.jsxs("div",{className:"header-inner",children:[e.jsxs("svg",{className:"header-icon",viewBox:"0 0 40 30",fill:"none","aria-hidden":"true",children:[e.jsx("rect",{x:"0",y:"20",width:"8",height:"10",rx:"2",fill:"white",opacity:"0.6"}),e.jsx("rect",{x:"11",y:"13",width:"8",height:"17",rx:"2",fill:"white",opacity:"0.75"}),e.jsx("rect",{x:"22",y:"6",width:"8",height:"24",rx:"2",fill:"white",opacity:"0.9"}),e.jsx("rect",{x:"33",y:"0",width:"7",height:"30",rx:"2",fill:"white"}),e.jsx("path",{d:"M4 19 L15 12 L26 5 L36.5 0.5",stroke:"white",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round",opacity:"0.45"})]}),e.jsxs("div",{children:[e.jsx("h1",{children:"Dreaming Insights"}),e.jsx("p",{children:"Your immersion journey, visualised"})]}),e.jsx("a",{href:"/meta.html",className:"coffee-btn",style:{marginRight:"auto",background:"rgba(255,255,255,0.15)",border:"1px solid rgba(255,255,255,0.35)"},children:"Catalogue stats"}),e.jsx("a",{href:De,target:"_blank",rel:"noreferrer",className:"coffee-btn",children:"☕ Buy me a coffee"})]})}),e.jsxs("main",{children:[e.jsxs("div",{className:"data-header",children:[e.jsxs("div",{className:"status-cards",children:[e.jsx(ie,{label:"Watched videos",status:ee.watched}),e.jsx(ie,{label:"External time",status:ee.external}),e.jsx(ie,{label:"Day watch time",status:ee.dayTime})]}),e.jsx("button",{className:"toggle-inputs-btn",onClick:()=>le(t=>!t),children:oe?"Hide inputs":"Manual input"})]}),e.jsxs("section",{className:"input-section",style:{display:oe?"flex":"none"},children:[e.jsxs("div",{className:"language-selector",children:[e.jsx("span",{className:"language-selector__label",children:"Language:"}),[{key:"es",label:"🇪🇸 Spanish"},{key:"fr",label:"🇫🇷 French"}].map(({key:t,label:o})=>e.jsx("button",{className:`lang-btn${D===t?" lang-btn--active":""}`,onClick:()=>q(t),children:o},t))]}),e.jsxs("div",{className:"input-row",children:[e.jsxs("div",{className:"input-group",children:[e.jsxs("label",{htmlFor:"watched-input",children:["watched-videos.json ",e.jsx("span",{className:"label-required",children:"required"})]}),e.jsx("textarea",{id:"watched-input",rows:12,placeholder:"Paste your watched-videos.json export here…",value:n,onChange:t=>m(t.target.value),spellCheck:!1}),e.jsxs("span",{className:"char-count",children:[n.length.toLocaleStr
1ing()," chars"]})]}),e.jsxs("div",{className:"input-group",children:[e.jsxs("label",{htmlFor:"external-input",children:["external-time.json ",e.jsx("span",{className:"label-optional",children:"optional"})]}),e.jsx("textarea",{id:"external-input",rows:12,placeholder:"Optional — paste your external-time.json export here. Leave blank to use DS watch history only.",value:p,onChange:t=>k(t.target.value),spellCheck:!1}),e.jsxs("span",{className:"char-count",children:[p.length.toLocaleString()," chars"]})]})]}),e.jsxs("div",{className:"input-group",children:[e.jsxs("label",{htmlFor:"daytime-input",children:["dayWatchedTime ",e.jsx("span",{className:"label-optional",children:"optional"})]}),e.jsx("textarea",{id:"daytime-input",rows:5,placeholder:"Optional — paste the dayWatchedTime JSON array here for a daily minutes chart with 7-day and 30-day rolling averages.",value:b,onChange:t=>He(t.target.value),spellCheck:!1}),e.jsxs("span",{className:"char-count",children:[b.length.toLocaleString()," chars"]})]})]}),de&&e.jsx("div",{className:"error",role:"alert",children:de}),ue.length>0&&e.jsxs("div",{className:"warning",role:"alert",children:[e.jsx("strong",{children:"External time data issues:"}),e.jsx("ul",{children:ue.map((t,o)=>e.jsx("li",{children:t},o))})]}),xe.length>0&&e.jsxs("div",{className:"warning",role:"alert",children:[e.jsx("strong",{children:"Day watched time data issues:"}),e.jsx("ul",{children:xe.map((t,o)=>e.jsx("li",{children:t},o))})]}),e.jsx("div",{className:"generate-row",children:e.jsx("button",{className:"generate-btn",onClick:fe,disabled:!Oe,children:M?"Processing…":"Generate Charts"})}),Fe&&e.jsx("div",{className:"research-cta",children:T!=null&&T.optedIn?e.jsxs("p",{className:"research-active",children:["✓ Contributing to Dreaming Insights research",O==="uploading"?" · Updating…":` · Last uploaded ${new Date(T.lastUpload).toLocaleDateString()}`,O==="error"&&" · Last upload failed"]}):e.jsxs(e.Fragment,{children:[e.jsx("button",{className:"research-btn",onClick:()=>K(!0),disabled:O==="uploading",children:O==="uploading"?"Uploading…":"Contribute to Dreaming Insights research"}),e.jsxs("p",{className:"research-desc",children:["Opt in to share your anonymised viewing data to help improve Dreaming Insights recommendations. Your data is stored securely and used for research purposes only.",O==="error"&&e.jsx("span",{className:"research-error",children:" Upload failed — please try again."})]})]})}),s&&e.jsxs("div",{className:"level-filter-bar",children:[e.jsx("span",{className:"level-filter__label",children:"Filter data by DS level"}),e.jsx("div",{className:"level-filter__buttons",children:R.map(t=>{const o=s.totalHours>=t.minHours,c=t.level>=w&&t.level<=N;return e.jsx("button",{className:["level-btn",c?"level-btn--selected":"",o?"":"level-btn--unachieved"].join(" ").trim(),onClick:()=>o&&Ue(t.level),disabled:!o,title:o?`Level ${t.level}: ${t.minHours}–${t.maxHours===1/0?"∞":t.maxHours}h`:`Not yet reached (requires ${t.minHours}h)`,children:t.level},t.level)})}),e.jsx("span",{className:"level-filter__range",children:(()=>{const t=R.find(d=>d.level===w),o=R.find(d=>d.level===N),c=o.maxHours===1/0?`${t.minHours}h+`:`${t.minHours}–${o.maxHours}h`;return w===N?`Level ${w} · ${c}`:`Levels ${w}–${N} · ${c}`})()})]}),F.length>0&&e.jsxs("div",{className:"warning yt-warning",children:[e.jsxs("strong",{children:["⚠️ ",F.length," YouTube video",F.length!==1?"s":""," couldn't be matched to a channel name"]})," ","— they appear as raw URLs in the charts."," ",e.jsx("button",{className:"yt-warning__btn",onClick:()=>{Z(!0),H("idle")},children:"Submit URLs for labelling"})]}),h&&e.jsxs(e.Fragment,{children:[e.jsx(ct,{info:h.infoPanel,dayStats:s.dayStats}),e.jsx(dt,{exportRows:s.exportRows,summaryRows:s.summaryRows}),e.jsxs("section",{className:"charts",children:[s.dayStats&&e.jsx(ht,{stats:s.dayStats}),h.dailyPoints.length>0&&e.jsx(ut,{points:h.dailyPoints}),e.jsx(mt,{difficultyData:h.difficultyData,appearances:h.appearances,consumptionPoints:h.consumptionPoints,hasExternalData:s.hasExternalData,xMin:h.xMin,xMax:h.xMax}),s.hasExternalData&&h.dsVsExternalPoints.length>0&&e.jsx(xt,{points:h.dsVsExternalPoints}),e.jsxs("div",{className:"chart-card",children:[e.jsxs("h2",{children:["Content Split by Time (",Math.round(h.contentTotalHours),"h total)"]}),e.jsx(gt,{slices:h.contentSlices,totalHours:h.contentTotalHours})]}),e.jsxs("div",{className:"chart-card",children:[e.jsxs("h2",{children:["Viewing by Guide (",Math.round(h.guidesTotalHours),"h)"]}),e.jsx(pt,{slices:h.guideSlices,totalHours:h.guidesTotalHours})]}),h.countrySlices.length>0&&e.jsxs("div",{className:"chart-card",children:[e.jsx("h2",{children:"Viewing by Country / Dialect"}),e.jsx(ft,{slices:h.countrySlices})]}),e.jsxs("div",{className:"chart-card",children:[e.jsx("h2",{children:"Hours Watched per Guide"}),e.jsx(yt,{guideData:h.guideData})]}),e.jsxs("div",{className:"chart-card",children:[e.jsx("h2",{children:"Top 20 Tags by Hours Watched"}),e.jsx(jt,{tagData:h.tagData})]})]})]})]}),e.jsxs("footer",{className:"site-footer",children:[e.jsx("a",{href:"https://forms.gle/F7FzqFdQ9yA5XgtJA",target:"_blank",rel:"noreferrer",children:"Contact / Support"}),e.jsx("span",{className:"site-footer__sep",children:"·"}),e.jsx("a",{href:"/privacy.html",target:"_blank",rel:"noreferrer",children:"Privacy Policy"}),e.jsx("span",{className:"site-footer__sep",children:"·"}),e.jsx("a",{href:"https://github.com/jcholyhead/ds-insights-web",target:"_blank",rel:"noreferrer",children:"Source Code"})]}),Pe&&e.jsx(bt,{charts:s,onClose:()=>K(!1),onConfirm:t=>{K(!1),ye(s,t)}}),Re&&e.jsx(vt,{urls:F,status:Ae,onConfirm:Le,onClose:()=>{Z(!1),H("idle")}})]})}lt(document.getElementById("root")).render(e.jsx(r.StrictMode,{children:e.jsx(Ht,{})}));

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.