@epic-web/workshop-app 6.77.0 → 6.77.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/client/assets/{_exerciseNumber-C1aZpAWV.js → _exerciseNumber-JuXb6JTN.js} +2 -2
- package/build/client/assets/{_exerciseNumber-C1aZpAWV.js.map → _exerciseNumber-JuXb6JTN.js.map} +1 -1
- package/build/client/assets/{_exerciseNumber_.finished-BICggZV1.js → _exerciseNumber_.finished-DD-1txyR.js} +2 -2
- package/build/client/assets/{_exerciseNumber_.finished-BICggZV1.js.map → _exerciseNumber_.finished-DD-1txyR.js.map} +1 -1
- package/build/client/assets/_extra-BfK-kKBL.js +2 -0
- package/build/client/assets/_extra-BfK-kKBL.js.map +1 -0
- package/build/client/assets/{_layout-BZI3FoQD.js → _layout-BsHVX3B_.js} +2 -2
- package/build/client/assets/{_layout-BZI3FoQD.js.map → _layout-BsHVX3B_.js.map} +1 -1
- package/build/client/assets/{_layout-CunOyAg4.js → _layout-CDyeQVKG.js} +2 -2
- package/build/client/assets/{_layout-CunOyAg4.js.map → _layout-CDyeQVKG.js.map} +1 -1
- package/build/client/assets/_layout-mVyOtxmC.js +2 -0
- package/build/client/assets/_layout-mVyOtxmC.js.map +1 -0
- package/build/client/assets/{account-C96cpeZR.js → account-BKXMYhfO.js} +2 -2
- package/build/client/assets/{account-C96cpeZR.js.map → account-BKXMYhfO.js.map} +1 -1
- package/build/client/assets/{app-CJniokic.js → app-AWsnszeo.js} +2 -2
- package/build/client/assets/{app-CJniokic.js.map → app-AWsnszeo.js.map} +1 -1
- package/build/client/assets/{cache-CAr50MIB.js → cache-CxMFS2A_.js} +2 -2
- package/build/client/assets/{cache-CAr50MIB.js.map → cache-CxMFS2A_.js.map} +1 -1
- package/build/client/assets/{db-DAnX-T3_.js → db-BZ-tFzMp.js} +2 -2
- package/build/client/assets/{db-DAnX-T3_.js.map → db-BZ-tFzMp.js.map} +1 -1
- package/build/client/assets/{diff-D8TUonC2.js → diff-0VO9C_Sf.js} +2 -2
- package/build/client/assets/{diff-D8TUonC2.js.map → diff-0VO9C_Sf.js.map} +1 -1
- package/build/client/assets/{diff-Col_iM3X.js → diff-CC_jaH50.js} +2 -2
- package/build/client/assets/{diff-Col_iM3X.js.map → diff-CC_jaH50.js.map} +1 -1
- package/build/client/assets/discord-BVfxP4Rd.js +2 -0
- package/build/client/assets/discord-BVfxP4Rd.js.map +1 -0
- package/build/client/assets/{discord-BJkw0IrB.js → discord-XhHqPI49.js} +2 -2
- package/build/client/assets/{discord-BJkw0IrB.js.map → discord-XhHqPI49.js.map} +1 -1
- package/build/client/assets/{epic-video-BJW6MU1i.js → epic-video-D4jBdSKs.js} +2 -2
- package/build/client/assets/{epic-video-BJW6MU1i.js.map → epic-video-D4jBdSKs.js.map} +1 -1
- package/build/client/assets/{epic-video-jtxu_0bl.js → epic-video-Ddc9rYLT.js} +2 -2
- package/build/client/assets/{epic-video-jtxu_0bl.js.map → epic-video-Ddc9rYLT.js.map} +1 -1
- package/build/client/assets/{finished-C2jP5uE7.js → finished-BSfM_f4M.js} +2 -2
- package/build/client/assets/{finished-C2jP5uE7.js.map → finished-BSfM_f4M.js.map} +1 -1
- package/build/client/assets/{index-CdzVFL-Z.js → index-BrVvTrwg.js} +2 -2
- package/build/client/assets/{index-CdzVFL-Z.js.map → index-BrVvTrwg.js.map} +1 -1
- package/build/client/assets/{index-Dpyv8N6m.js → index-CfPaTqRT.js} +2 -2
- package/build/client/assets/{index-Dpyv8N6m.js.map → index-CfPaTqRT.js.map} +1 -1
- package/build/client/assets/{index-X8or9u7t.js → index-CuCZuiBp.js} +2 -2
- package/build/client/assets/{index-X8or9u7t.js.map → index-CuCZuiBp.js.map} +1 -1
- package/build/client/assets/{index-KRgoKRWG.js → index-D0vH1MiQ.js} +2 -2
- package/build/client/assets/{index-KRgoKRWG.js.map → index-D0vH1MiQ.js.map} +1 -1
- package/build/client/assets/{index-BCQgVrao.js → index-DTC_5pri.js} +2 -2
- package/build/client/assets/{index-BCQgVrao.js.map → index-DTC_5pri.js.map} +1 -1
- package/build/client/assets/{launch-editor-D2exGfVu.js → launch-editor-0oPpbFQe.js} +2 -2
- package/build/client/assets/{launch-editor-D2exGfVu.js.map → launch-editor-0oPpbFQe.js.map} +1 -1
- package/build/client/assets/{loading-CDNzW5oO.js → loading-CaCCsk9k.js} +2 -2
- package/build/client/assets/{loading-CDNzW5oO.js.map → loading-CaCCsk9k.js.map} +1 -1
- package/build/client/assets/{login-mWjVXGbJ.js → login-w4y7RVYa.js} +2 -2
- package/build/client/assets/{login-mWjVXGbJ.js.map → login-w4y7RVYa.js.map} +1 -1
- package/build/client/assets/{manifest-dd28a0e5.js → manifest-a63db2d2.js} +1 -1
- package/build/client/assets/{mdx-DdpoOTHm.js → mdx-DwvWT4Ou.js} +2 -2
- package/build/client/assets/{mdx-DdpoOTHm.js.map → mdx-DwvWT4Ou.js.map} +1 -1
- package/build/client/assets/{onboarding-indicator-B-XR90_G.js → onboarding-indicator-BkeHYs2C.js} +2 -2
- package/build/client/assets/{onboarding-indicator-B-XR90_G.js.map → onboarding-indicator-BkeHYs2C.js.map} +1 -1
- package/build/client/assets/{online-DiNLkgTC.js → online-DVk-W8Cr.js} +2 -2
- package/build/client/assets/{online-DiNLkgTC.js.map → online-DVk-W8Cr.js.map} +1 -1
- package/build/client/assets/{playground-DmEAkxG1.js → playground-DG4B62WX.js} +2 -2
- package/build/client/assets/{playground-DmEAkxG1.js.map → playground-DG4B62WX.js.map} +1 -1
- package/build/client/assets/{playground-window-x2mQ5o1O.js → playground-window-CF8lTXXI.js} +2 -2
- package/build/client/assets/{playground-window-x2mQ5o1O.js.map → playground-window-CF8lTXXI.js.map} +1 -1
- package/build/client/assets/{preferences-B7ND1VS9.js → preferences-Czy5oqWs.js} +2 -2
- package/build/client/assets/{preferences-B7ND1VS9.js.map → preferences-Czy5oqWs.js.map} +1 -1
- package/build/client/assets/{presence-VCvV2mg7.js → presence-Brtc_BN7.js} +2 -2
- package/build/client/assets/{presence-VCvV2mg7.js.map → presence-Brtc_BN7.js.map} +1 -1
- package/build/client/assets/{preview-fhmjENlm.js → preview-Be-plbPz.js} +2 -2
- package/build/client/assets/{preview-fhmjENlm.js.map → preview-Be-plbPz.js.map} +1 -1
- package/build/client/assets/{product-CvyMpYD_.js → product-D4IgPJN5.js} +2 -2
- package/build/client/assets/{product-CvyMpYD_.js.map → product-D4IgPJN5.js.map} +1 -1
- package/build/client/assets/{progress-Bby-ybMA.js → progress-JDMkfyr3.js} +2 -2
- package/build/client/assets/{progress-Bby-ybMA.js.map → progress-JDMkfyr3.js.map} +1 -1
- package/build/client/assets/{revalidation-ws-BJWJviUX.js → revalidation-ws-iocj9Mrl.js} +2 -2
- package/build/client/assets/{revalidation-ws-BJWJviUX.js.map → revalidation-ws-iocj9Mrl.js.map} +1 -1
- package/build/client/assets/{root-B-MkiU1r.js → root-IJJBVEoj.js} +2 -2
- package/build/client/assets/{root-B-MkiU1r.js.map → root-IJJBVEoj.js.map} +1 -1
- package/build/client/assets/{root-loader-BOzEMapJ.js → root-loader-BmUqzUDN.js} +2 -2
- package/build/client/assets/{root-loader-BOzEMapJ.js.map → root-loader-BmUqzUDN.js.map} +1 -1
- package/build/client/assets/{set-playground-BSGwH9dH.js → set-playground-DO5tJu4h.js} +2 -2
- package/build/client/assets/{set-playground-BSGwH9dH.js.map → set-playground-DO5tJu4h.js.map} +1 -1
- package/build/client/assets/{test-BeEphi7e.js → test-C8NrA0Ls.js} +2 -2
- package/build/client/assets/{test-BeEphi7e.js.map → test-C8NrA0Ls.js.map} +1 -1
- package/build/client/assets/{tests-RWSslYc0.js → tests-Bko6qhsh.js} +2 -2
- package/build/client/assets/{tests-RWSslYc0.js.map → tests-Bko6qhsh.js.map} +1 -1
- package/build/client/assets/{user-BsPobzjB.js → user-CYXKquT7.js} +2 -2
- package/build/client/assets/{user-BsPobzjB.js.map → user-CYXKquT7.js.map} +1 -1
- package/build/client/assets/{version-f8qqYbyU.js → version-CwWSRwe5.js} +2 -2
- package/build/client/assets/{version-f8qqYbyU.js.map → version-CwWSRwe5.js.map} +1 -1
- package/build/client/assets/{workshop-config-Zfc8zU0x.js → workshop-config-BMWaKPZT.js} +2 -2
- package/build/client/assets/{workshop-config-Zfc8zU0x.js.map → workshop-config-BMWaKPZT.js.map} +1 -1
- package/build/server/index.js +7 -3
- package/build/server/index.js.map +1 -1
- package/package.json +3 -3
- package/build/client/assets/_extra-Dki7HdFp.js +0 -2
- package/build/client/assets/_extra-Dki7HdFp.js.map +0 -1
- package/build/client/assets/_layout-DTrIGRDO.js +0 -2
- package/build/client/assets/_layout-DTrIGRDO.js.map +0 -1
- package/build/client/assets/discord-r3m19sUM.js +0 -2
- package/build/client/assets/discord-r3m19sUM.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"online-
|
|
1
|
+
{"version":3,"file":"online-DVk-W8Cr.js","sources":["../../../app/utils/online.ts"],"sourcesContent":["import { useSyncExternalStore } from 'react'\nimport { useRequestInfo } from './root-loader.ts'\n\nfunction getSnapshot() {\n\treturn window.navigator.onLine\n}\n\nfunction subscribe(callback: () => void) {\n\twindow.addEventListener('online', callback)\n\twindow.addEventListener('offline', callback)\n\treturn () => {\n\t\twindow.removeEventListener('online', callback)\n\t\twindow.removeEventListener('offline', callback)\n\t}\n}\n\nexport function useIsOnline() {\n\tconst requestInfo = useRequestInfo()\n\treturn useSyncExternalStore(subscribe, getSnapshot, () => requestInfo.online)\n}\n"],"names":["getSnapshot","subscribe","callback","useIsOnline","requestInfo","useRequestInfo","useSyncExternalStore"],"mappings":"sFAGA,SAASA,GAAc,CACtB,OAAO,OAAO,UAAU,MACzB,CAEA,SAASC,EAAUC,EAAsB,CACxC,cAAO,iBAAiB,SAAUA,CAAQ,EAC1C,OAAO,iBAAiB,UAAWA,CAAQ,EACpC,IAAM,CACZ,OAAO,oBAAoB,SAAUA,CAAQ,EAC7C,OAAO,oBAAoB,UAAWA,CAAQ,CAC/C,CACD,CAEO,SAASC,GAAc,CAC7B,MAAMC,EAAcC,EAAA,EACpB,OAAOC,EAAAA,qBAAqBL,EAAWD,EAAa,IAAMI,EAAY,MAAM,CAC7E"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{j as e}from"./jsx-runtime-C5WNSv3b.js";import{r as c}from"./index-CqIc3cxq.js";import{A as ue,L as M}from"./chunk-EPOLDU6W-BCLmut3y.js";import{u as de,I as N,c as fe}from"./misc-W4055b-0.js";import{L as me}from"./loading-CDNzW5oO.js";import{D as xe,u as pe}from"./discord-BJkw0IrB.js";import{u as he}from"./online-DiNLkgTC.js";import{a as G,b as O,P as T,e as g,d as ve,u as K,o as ge,f as be,S as je}from"./tooltip-Tlsyx2YO.js";import{g as we,u as $}from"./index-CJDOQ1bl.js";import{S as Ne}from"./status-indicator-C6DiLYL5.js";import{t as Te}from"./index-DzdDahau.js";import{S as Ie}from"./set-playground-BSGwH9dH.js";import{P as Ce}from"./playground-window-x2mQ5o1O.js";import{P as ye}from"./preview-fhmjENlm.js";var _="rovingFocusGroup.onEntryFocus",Fe={bubbles:!1,cancelable:!0},I="RovingFocusGroup",[A,V,Re]=we(I),[Se,B]=G(I,[Re]),[Pe,Ee]=Se(I),z=c.forwardRef((t,s)=>e.jsx(A.Provider,{scope:t.__scopeRovingFocusGroup,children:e.jsx(A.Slot,{scope:t.__scopeRovingFocusGroup,children:e.jsx(_e,{...t,ref:s})})}));z.displayName=I;var _e=c.forwardRef((t,s)=>{const{__scopeRovingFocusGroup:r,orientation:n,loop:a=!1,dir:u,currentTabStopId:i,defaultCurrentTabStopId:m,onCurrentTabStopIdChange:v,onEntryFocus:x,preventScrollOnEntryFocus:o=!1,...l}=t,p=c.useRef(null),C=ve(s,p),y=$(u),[F,d]=K({prop:i,defaultProp:m??null,onChange:v,caller:I}),[b,S]=c.useState(!1),h=ge(x),j=V(r),P=c.useRef(!1),[oe,k]=c.useState(0);return c.useEffect(()=>{const f=p.current;if(f)return f.addEventListener(_,h),()=>f.removeEventListener(_,h)},[h]),e.jsx(Pe,{scope:r,orientation:n,dir:y,loop:a,currentTabStopId:F,onItemFocus:c.useCallback(f=>d(f),[d]),onItemShiftTab:c.useCallback(()=>S(!0),[]),onFocusableItemAdd:c.useCallback(()=>k(f=>f+1),[]),onFocusableItemRemove:c.useCallback(()=>k(f=>f-1),[]),children:e.jsx(T.div,{tabIndex:b||oe===0?-1:0,"data-orientation":n,...l,ref:C,style:{outline:"none",...t.style},onMouseDown:g(t.onMouseDown,()=>{P.current=!0}),onFocus:g(t.onFocus,f=>{const ae=!P.current;if(f.target===f.currentTarget&&ae&&!b){const L=new CustomEvent(_,Fe);if(f.currentTarget.dispatchEvent(L),!L.defaultPrevented){const E=j().filter(w=>w.focusable),ie=E.find(w=>w.active),ce=E.find(w=>w.id===F),le=[ie,ce,...E].filter(Boolean).map(w=>w.ref.current);Y(le,o)}}P.current=!1}),onBlur:g(t.onBlur,()=>S(!1))})})}),H="RovingFocusGroupItem",W=c.forwardRef((t,s)=>{const{__scopeRovingFocusGroup:r,focusable:n=!0,active:a=!1,tabStopId:u,children:i,...m}=t,v=O(),x=u||v,o=Ee(H,r),l=o.currentTabStopId===x,p=V(r),{onFocusableItemAdd:C,onFocusableItemRemove:y,currentTabStopId:F}=o;return c.useEffect(()=>{if(n)return C(),()=>y()},[n,C,y]),e.jsx(A.ItemSlot,{scope:r,id:x,focusable:n,active:a,children:e.jsx(T.span,{tabIndex:l?0:-1,"data-orientation":o.orientation,...m,ref:s,onMouseDown:g(t.onMouseDown,d=>{n?o.onItemFocus(x):d.preventDefault()}),onFocus:g(t.onFocus,()=>o.onItemFocus(x)),onKeyDown:g(t.onKeyDown,d=>{if(d.key==="Tab"&&d.shiftKey){o.onItemShiftTab();return}if(d.target!==d.currentTarget)return;const b=ke(d,o.orientation,o.dir);if(b!==void 0){if(d.metaKey||d.ctrlKey||d.altKey||d.shiftKey)return;d.preventDefault();let h=p().filter(j=>j.focusable).map(j=>j.ref.current);if(b==="last")h.reverse();else if(b==="prev"||b==="next"){b==="prev"&&h.reverse();const j=h.indexOf(d.currentTarget);h=o.loop?Le(h,j+1):h.slice(j+1)}setTimeout(()=>Y(h))}}),children:typeof i=="function"?i({isCurrentTabStop:l,hasTabStop:F!=null}):i})})});W.displayName=H;var Ae={ArrowLeft:"prev",ArrowUp:"prev",ArrowRight:"next",ArrowDown:"next",PageUp:"first",Home:"first",PageDown:"last",End:"last"};function De(t,s){return s!=="rtl"?t:t==="ArrowLeft"?"ArrowRight":t==="ArrowRight"?"ArrowLeft":t}function ke(t,s,r){const n=De(t.key,r);if(!(s==="vertical"&&["ArrowLeft","ArrowRight"].includes(n))&&!(s==="horizontal"&&["ArrowUp","ArrowDown"].includes(n)))return Ae[n]}function Y(t,s=!1){const r=document.activeElement;for(const n of t)if(n===r||(n.focus({preventScroll:s}),document.activeElement!==r))return}function Le(t,s){return t.map((r,n)=>t[(s+n)%t.length])}var Ue=z,Me=W,R="Tabs",[Ge]=G(R,[B]),q=B(),[Oe,D]=Ge(R),J=c.forwardRef((t,s)=>{const{__scopeTabs:r,value:n,onValueChange:a,defaultValue:u,orientation:i="horizontal",dir:m,activationMode:v="automatic",...x}=t,o=$(m),[l,p]=K({prop:n,onChange:a,defaultProp:u??"",caller:R});return e.jsx(Oe,{scope:r,baseId:O(),value:l,onValueChange:p,orientation:i,dir:o,activationMode:v,children:e.jsx(T.div,{dir:o,"data-orientation":i,...x,ref:s})})});J.displayName=R;var Q="TabsList",X=c.forwardRef((t,s)=>{const{__scopeTabs:r,loop:n=!0,...a}=t,u=D(Q,r),i=q(r);return e.jsx(Ue,{asChild:!0,...i,orientation:u.orientation,dir:u.dir,loop:n,children:e.jsx(T.div,{role:"tablist","aria-orientation":u.orientation,...a,ref:s})})});X.displayName=Q;var Z="TabsTrigger",ee=c.forwardRef((t,s)=>{const{__scopeTabs:r,value:n,disabled:a=!1,...u}=t,i=D(Z,r),m=q(r),v=re(i.baseId,n),x=ne(i.baseId,n),o=n===i.value;return e.jsx(Me,{asChild:!0,...m,focusable:!a,active:o,children:e.jsx(T.button,{type:"button",role:"tab","aria-selected":o,"aria-controls":x,"data-state":o?"active":"inactive","data-disabled":a?"":void 0,disabled:a,id:v,...u,ref:s,onMouseDown:g(t.onMouseDown,l=>{!a&&l.button===0&&l.ctrlKey===!1?i.onValueChange(n):l.preventDefault()}),onKeyDown:g(t.onKeyDown,l=>{[" ","Enter"].includes(l.key)&&i.onValueChange(n)}),onFocus:g(t.onFocus,()=>{const l=i.activationMode!=="manual";!o&&!a&&l&&i.onValueChange(n)})})})});ee.displayName=Z;var te="TabsContent",se=c.forwardRef((t,s)=>{const{__scopeTabs:r,value:n,forceMount:a,children:u,...i}=t,m=D(te,r),v=re(m.baseId,n),x=ne(m.baseId,n),o=n===m.value,l=c.useRef(o);return c.useEffect(()=>{const p=requestAnimationFrame(()=>l.current=!1);return()=>cancelAnimationFrame(p)},[]),e.jsx(be,{present:a||o,children:({present:p})=>e.jsx(T.div,{"data-state":o?"active":"inactive","data-orientation":m.orientation,role:"tabpanel","aria-labelledby":v,hidden:!p,id:x,tabIndex:0,...i,ref:s,style:{...t.style,animationDuration:l.current?"0s":void 0},children:p&&u})})});se.displayName=te;function re(t,s){return`${t}-trigger-${s}`}function ne(t,s){return`${t}-content-${s}`}var at=J,Ke=X,$e=ee,it=se;function ct({discordPostsPromise:t}){return e.jsxs("div",{className:"flex h-full w-full flex-col gap-4 pt-4",children:[e.jsx("div",{className:"text-center",children:e.jsx(xe,{})}),e.jsx("div",{className:"bg-accent scrollbar-thin scrollbar-thumb-scrollbar flex-1 overflow-y-scroll pb-4",children:e.jsx(Ve,{discordPostsPromise:t})})]})}function Ve({discordPostsPromise:t}){const s=pe(),r=de();return he()?e.jsxs("div",{className:"flex h-full flex-col items-center justify-between",children:[e.jsx(c.Suspense,{fallback:e.jsx("div",{className:"flex h-full w-full flex-col items-center justify-center",children:e.jsx(me,{children:"Loading Discord Posts"})}),children:e.jsx(ue,{resolve:t,errorElement:e.jsx("div",{className:"text-foreground-destructive",children:"There was a problem loading the discord posts"}),children:a=>e.jsx("ul",{className:"flex w-full flex-col gap-4 p-3 xl:p-12",children:a.map(u=>e.jsx("li",{className:"bg-background rounded-xl border transition-all duration-200 focus-within:-translate-y-1 focus-within:shadow-lg hover:-translate-y-1 hover:shadow-lg",children:e.jsx(Be,{thread:u})},u.id))})})}),e.jsx("div",{children:e.jsxs(M,{to:r&&!s.includes("oauth")?s.replace(/^https/,"discord"):s,target:s.includes("oauth")?void 0:"_blank",rel:"noreferrer noopener",onClick:r?a=>{a.preventDefault(),window.open(a.currentTarget.href,"_blank","noreferrer noopener")}:void 0,className:"flex items-center gap-2 p-2 text-xl hover:underline",children:["Create Post ",e.jsx(N,{name:"ExternalLink"})]})})]}):e.jsx("div",{className:"flex h-full flex-col items-center justify-between",children:e.jsx("div",{className:"text-foreground-destructive flex h-full w-full flex-col items-center justify-center",children:e.jsx(N,{name:"WifiNoConnection",size:"xl",children:"Unable to load discord messages when offline"})})})}function Be({thread:t}){const s=t.reactions.filter(r=>r.count);return e.jsx("div",{children:e.jsxs("div",{className:"flex flex-col gap-2 p-4",children:[e.jsxs("div",{className:"flex min-w-0 gap-4",children:[e.jsxs("div",{className:"flex min-w-0 flex-col gap-1",children:[t.tags.length?e.jsx("div",{className:"flex gap-2",children:t.tags.map(r=>e.jsxs("div",{className:"bg-accent flex items-center justify-center gap-1 rounded-full px-2 py-1 text-sm",children:[e.jsx("span",{className:"h-3 w-3 leading-3",children:e.jsx(U,{name:r.emojiName,url:r.emojiUrl})}),e.jsx("span",{children:r.name})]},`${r.name}-${r.emojiName??r.emojiUrl??"tag"}`))}):null,e.jsx("strong",{className:"text-xl font-bold",children:t.name}),e.jsxs("div",{className:"flex min-w-0 flex-col gap-1",children:[e.jsxs("div",{className:"flex items-center gap-1",children:[t.authorAvatarUrl?e.jsx("img",{src:t.authorAvatarUrl,alt:"",className:"h-6 w-6 rounded-full"}):null,e.jsxs("span",{children:[e.jsx("span",{className:"font-bold",style:t.authorHexAccentColor?{color:t.authorHexAccentColor}:{},children:t.authorDisplayName}),":"," "]})]}),e.jsx("span",{className:"text-muted-foreground line-clamp-4 min-w-0",children:t.messagePreview})]})]}),t.previewImageUrl?e.jsx("img",{src:t.previewImageUrl,alt:"",className:"h-28 w-28 rounded-lg object-cover"}):null]}),e.jsxs("div",{className:"flex justify-between",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("span",{children:s.length?e.jsx("ul",{className:"flex items-center gap-2",children:s.map((r,n)=>e.jsxs("li",{className:"border-info/60 bg-info/10 text-info flex items-center gap-1 rounded-md border px-[5px] py-[0.5px] text-sm",children:[e.jsx("span",{className:"h-3 w-3 leading-3",children:e.jsx(U,{name:r.emojiName,url:r.emojiUrl})}),e.jsx("span",{children:r.count})]},`${r.emojiName??r.emojiUrl??"reaction"}-${n}`))}):null}),e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx(N,{name:"Chat"})," ",t.messageCount]}),` · ${t.lastUpdatedDisplay}`]})]}),e.jsxs("span",{className:"flex items-center gap-4",children:[e.jsx("a",{href:t.link.replace(/^https/,"discord"),children:e.jsx(N,{name:"Discord"})}),e.jsx("a",{href:t.link,target:"_blank",rel:"noreferrer noopener",children:e.jsx(N,{name:"ExternalLink"})})]})]})]})})}function U({name:t,url:s}){return s?e.jsx("img",{src:s,alt:t,className:"h-full w-full"}):t||null}function lt(t,s,r){const n=new URLSearchParams(t);return s===r?n.delete("preview"):n.set("preview",s),n}function ut({tabs:t}){return e.jsx(Ke,{className:"scrollbar-thin scrollbar-thumb-scrollbar h-14 min-h-14 overflow-x-auto border-b whitespace-nowrap",children:t.map(s=>e.jsx($e,{value:s.id,hidden:s.hidden,asChild:!0,children:e.jsx(M,{id:`${s.id}-tab`,className:fe("clip-path-button radix-state-active:z-10 radix-state-active:bg-foreground radix-state-active:text-background radix-state-active:hover:bg-foreground/80 radix-state-active:hover:text-background/80 radix-state-inactive:hover:bg-foreground/20 radix-state-inactive:hover:text-foreground/80 focus:bg-foreground/80 focus:text-background/80 relative h-full px-6 py-4 font-mono text-sm uppercase outline-none",s.hidden?"hidden":"inline-block"),preventScrollReset:!0,prefetch:"intent",onClick:s.onClick,to:s.to,children:e.jsxs("span",{className:"flex items-center gap-2",children:[s.status?e.jsx(Ne,{status:s.status}):null,e.jsx("span",{children:s.label})]})})},s.id))})}function dt({appInfo:t,inBrowserBrowserRef:s,problemAppName:r,allApps:n,isUpToDate:a}){return e.jsx(Ce,{playgroundAppName:t?.appName,problemAppName:r,allApps:n,isUpToDate:a,children:t?.dev?.type==="none"?e.jsxs("div",{className:"flex h-full flex-col items-center justify-center gap-4",children:[e.jsx("div",{className:"text-secondary-foreground text-2xl",children:"Non-UI exercise"}),e.jsxs("div",{className:"text-secondary-foreground max-w-md text-center text-balance",children:["This exercise has no application or other UI associated with it."," ",e.jsx("br",{}),"Navigate to"," ",e.jsx(je,{content:t.fullPath,children:e.jsxs("span",{className:"inline-flex cursor-pointer items-center gap-1.5 underline",onClick:()=>{navigator.clipboard.writeText(t.fullPath),Te.success("Copied playground path to clipboard")},children:["the playground directory",e.jsx(N,{name:"Copy",size:"sm"})]})})," ","in your editor and follow the exercise instructions to complete it."]})]}):t?e.jsx(ye,{id:t.appName,appInfo:t,inBrowserBrowserRef:s}):e.jsxs("div",{className:"flex flex-col justify-center gap-2",children:[e.jsx("p",{children:"Please set the playground first"}),r?e.jsx(Ie,{appName:r}):null]})})}export{it as C,ct as D,ut as P,at as R,dt as a,lt as g};
|
|
2
|
-
//# sourceMappingURL=playground-
|
|
1
|
+
import{j as e}from"./jsx-runtime-C5WNSv3b.js";import{r as c}from"./index-CqIc3cxq.js";import{A as ue,L as M}from"./chunk-EPOLDU6W-BCLmut3y.js";import{u as de,I as N,c as fe}from"./misc-W4055b-0.js";import{L as me}from"./loading-CaCCsk9k.js";import{D as xe,u as pe}from"./discord-XhHqPI49.js";import{u as he}from"./online-DVk-W8Cr.js";import{a as G,b as O,P as T,e as g,d as ve,u as K,o as ge,f as be,S as je}from"./tooltip-Tlsyx2YO.js";import{g as we,u as $}from"./index-CJDOQ1bl.js";import{S as Ne}from"./status-indicator-C6DiLYL5.js";import{t as Te}from"./index-DzdDahau.js";import{S as Ie}from"./set-playground-DO5tJu4h.js";import{P as Ce}from"./playground-window-CF8lTXXI.js";import{P as ye}from"./preview-Be-plbPz.js";var _="rovingFocusGroup.onEntryFocus",Fe={bubbles:!1,cancelable:!0},I="RovingFocusGroup",[A,V,Re]=we(I),[Se,B]=G(I,[Re]),[Pe,Ee]=Se(I),z=c.forwardRef((t,s)=>e.jsx(A.Provider,{scope:t.__scopeRovingFocusGroup,children:e.jsx(A.Slot,{scope:t.__scopeRovingFocusGroup,children:e.jsx(_e,{...t,ref:s})})}));z.displayName=I;var _e=c.forwardRef((t,s)=>{const{__scopeRovingFocusGroup:r,orientation:n,loop:a=!1,dir:u,currentTabStopId:i,defaultCurrentTabStopId:m,onCurrentTabStopIdChange:v,onEntryFocus:x,preventScrollOnEntryFocus:o=!1,...l}=t,p=c.useRef(null),C=ve(s,p),y=$(u),[F,d]=K({prop:i,defaultProp:m??null,onChange:v,caller:I}),[b,S]=c.useState(!1),h=ge(x),j=V(r),P=c.useRef(!1),[oe,k]=c.useState(0);return c.useEffect(()=>{const f=p.current;if(f)return f.addEventListener(_,h),()=>f.removeEventListener(_,h)},[h]),e.jsx(Pe,{scope:r,orientation:n,dir:y,loop:a,currentTabStopId:F,onItemFocus:c.useCallback(f=>d(f),[d]),onItemShiftTab:c.useCallback(()=>S(!0),[]),onFocusableItemAdd:c.useCallback(()=>k(f=>f+1),[]),onFocusableItemRemove:c.useCallback(()=>k(f=>f-1),[]),children:e.jsx(T.div,{tabIndex:b||oe===0?-1:0,"data-orientation":n,...l,ref:C,style:{outline:"none",...t.style},onMouseDown:g(t.onMouseDown,()=>{P.current=!0}),onFocus:g(t.onFocus,f=>{const ae=!P.current;if(f.target===f.currentTarget&&ae&&!b){const L=new CustomEvent(_,Fe);if(f.currentTarget.dispatchEvent(L),!L.defaultPrevented){const E=j().filter(w=>w.focusable),ie=E.find(w=>w.active),ce=E.find(w=>w.id===F),le=[ie,ce,...E].filter(Boolean).map(w=>w.ref.current);Y(le,o)}}P.current=!1}),onBlur:g(t.onBlur,()=>S(!1))})})}),H="RovingFocusGroupItem",W=c.forwardRef((t,s)=>{const{__scopeRovingFocusGroup:r,focusable:n=!0,active:a=!1,tabStopId:u,children:i,...m}=t,v=O(),x=u||v,o=Ee(H,r),l=o.currentTabStopId===x,p=V(r),{onFocusableItemAdd:C,onFocusableItemRemove:y,currentTabStopId:F}=o;return c.useEffect(()=>{if(n)return C(),()=>y()},[n,C,y]),e.jsx(A.ItemSlot,{scope:r,id:x,focusable:n,active:a,children:e.jsx(T.span,{tabIndex:l?0:-1,"data-orientation":o.orientation,...m,ref:s,onMouseDown:g(t.onMouseDown,d=>{n?o.onItemFocus(x):d.preventDefault()}),onFocus:g(t.onFocus,()=>o.onItemFocus(x)),onKeyDown:g(t.onKeyDown,d=>{if(d.key==="Tab"&&d.shiftKey){o.onItemShiftTab();return}if(d.target!==d.currentTarget)return;const b=ke(d,o.orientation,o.dir);if(b!==void 0){if(d.metaKey||d.ctrlKey||d.altKey||d.shiftKey)return;d.preventDefault();let h=p().filter(j=>j.focusable).map(j=>j.ref.current);if(b==="last")h.reverse();else if(b==="prev"||b==="next"){b==="prev"&&h.reverse();const j=h.indexOf(d.currentTarget);h=o.loop?Le(h,j+1):h.slice(j+1)}setTimeout(()=>Y(h))}}),children:typeof i=="function"?i({isCurrentTabStop:l,hasTabStop:F!=null}):i})})});W.displayName=H;var Ae={ArrowLeft:"prev",ArrowUp:"prev",ArrowRight:"next",ArrowDown:"next",PageUp:"first",Home:"first",PageDown:"last",End:"last"};function De(t,s){return s!=="rtl"?t:t==="ArrowLeft"?"ArrowRight":t==="ArrowRight"?"ArrowLeft":t}function ke(t,s,r){const n=De(t.key,r);if(!(s==="vertical"&&["ArrowLeft","ArrowRight"].includes(n))&&!(s==="horizontal"&&["ArrowUp","ArrowDown"].includes(n)))return Ae[n]}function Y(t,s=!1){const r=document.activeElement;for(const n of t)if(n===r||(n.focus({preventScroll:s}),document.activeElement!==r))return}function Le(t,s){return t.map((r,n)=>t[(s+n)%t.length])}var Ue=z,Me=W,R="Tabs",[Ge]=G(R,[B]),q=B(),[Oe,D]=Ge(R),J=c.forwardRef((t,s)=>{const{__scopeTabs:r,value:n,onValueChange:a,defaultValue:u,orientation:i="horizontal",dir:m,activationMode:v="automatic",...x}=t,o=$(m),[l,p]=K({prop:n,onChange:a,defaultProp:u??"",caller:R});return e.jsx(Oe,{scope:r,baseId:O(),value:l,onValueChange:p,orientation:i,dir:o,activationMode:v,children:e.jsx(T.div,{dir:o,"data-orientation":i,...x,ref:s})})});J.displayName=R;var Q="TabsList",X=c.forwardRef((t,s)=>{const{__scopeTabs:r,loop:n=!0,...a}=t,u=D(Q,r),i=q(r);return e.jsx(Ue,{asChild:!0,...i,orientation:u.orientation,dir:u.dir,loop:n,children:e.jsx(T.div,{role:"tablist","aria-orientation":u.orientation,...a,ref:s})})});X.displayName=Q;var Z="TabsTrigger",ee=c.forwardRef((t,s)=>{const{__scopeTabs:r,value:n,disabled:a=!1,...u}=t,i=D(Z,r),m=q(r),v=re(i.baseId,n),x=ne(i.baseId,n),o=n===i.value;return e.jsx(Me,{asChild:!0,...m,focusable:!a,active:o,children:e.jsx(T.button,{type:"button",role:"tab","aria-selected":o,"aria-controls":x,"data-state":o?"active":"inactive","data-disabled":a?"":void 0,disabled:a,id:v,...u,ref:s,onMouseDown:g(t.onMouseDown,l=>{!a&&l.button===0&&l.ctrlKey===!1?i.onValueChange(n):l.preventDefault()}),onKeyDown:g(t.onKeyDown,l=>{[" ","Enter"].includes(l.key)&&i.onValueChange(n)}),onFocus:g(t.onFocus,()=>{const l=i.activationMode!=="manual";!o&&!a&&l&&i.onValueChange(n)})})})});ee.displayName=Z;var te="TabsContent",se=c.forwardRef((t,s)=>{const{__scopeTabs:r,value:n,forceMount:a,children:u,...i}=t,m=D(te,r),v=re(m.baseId,n),x=ne(m.baseId,n),o=n===m.value,l=c.useRef(o);return c.useEffect(()=>{const p=requestAnimationFrame(()=>l.current=!1);return()=>cancelAnimationFrame(p)},[]),e.jsx(be,{present:a||o,children:({present:p})=>e.jsx(T.div,{"data-state":o?"active":"inactive","data-orientation":m.orientation,role:"tabpanel","aria-labelledby":v,hidden:!p,id:x,tabIndex:0,...i,ref:s,style:{...t.style,animationDuration:l.current?"0s":void 0},children:p&&u})})});se.displayName=te;function re(t,s){return`${t}-trigger-${s}`}function ne(t,s){return`${t}-content-${s}`}var at=J,Ke=X,$e=ee,it=se;function ct({discordPostsPromise:t}){return e.jsxs("div",{className:"flex h-full w-full flex-col gap-4 pt-4",children:[e.jsx("div",{className:"text-center",children:e.jsx(xe,{})}),e.jsx("div",{className:"bg-accent scrollbar-thin scrollbar-thumb-scrollbar flex-1 overflow-y-scroll pb-4",children:e.jsx(Ve,{discordPostsPromise:t})})]})}function Ve({discordPostsPromise:t}){const s=pe(),r=de();return he()?e.jsxs("div",{className:"flex h-full flex-col items-center justify-between",children:[e.jsx(c.Suspense,{fallback:e.jsx("div",{className:"flex h-full w-full flex-col items-center justify-center",children:e.jsx(me,{children:"Loading Discord Posts"})}),children:e.jsx(ue,{resolve:t,errorElement:e.jsx("div",{className:"text-foreground-destructive",children:"There was a problem loading the discord posts"}),children:a=>e.jsx("ul",{className:"flex w-full flex-col gap-4 p-3 xl:p-12",children:a.map(u=>e.jsx("li",{className:"bg-background rounded-xl border transition-all duration-200 focus-within:-translate-y-1 focus-within:shadow-lg hover:-translate-y-1 hover:shadow-lg",children:e.jsx(Be,{thread:u})},u.id))})})}),e.jsx("div",{children:e.jsxs(M,{to:r&&!s.includes("oauth")?s.replace(/^https/,"discord"):s,target:s.includes("oauth")?void 0:"_blank",rel:"noreferrer noopener",onClick:r?a=>{a.preventDefault(),window.open(a.currentTarget.href,"_blank","noreferrer noopener")}:void 0,className:"flex items-center gap-2 p-2 text-xl hover:underline",children:["Create Post ",e.jsx(N,{name:"ExternalLink"})]})})]}):e.jsx("div",{className:"flex h-full flex-col items-center justify-between",children:e.jsx("div",{className:"text-foreground-destructive flex h-full w-full flex-col items-center justify-center",children:e.jsx(N,{name:"WifiNoConnection",size:"xl",children:"Unable to load discord messages when offline"})})})}function Be({thread:t}){const s=t.reactions.filter(r=>r.count);return e.jsx("div",{children:e.jsxs("div",{className:"flex flex-col gap-2 p-4",children:[e.jsxs("div",{className:"flex min-w-0 gap-4",children:[e.jsxs("div",{className:"flex min-w-0 flex-col gap-1",children:[t.tags.length?e.jsx("div",{className:"flex gap-2",children:t.tags.map(r=>e.jsxs("div",{className:"bg-accent flex items-center justify-center gap-1 rounded-full px-2 py-1 text-sm",children:[e.jsx("span",{className:"h-3 w-3 leading-3",children:e.jsx(U,{name:r.emojiName,url:r.emojiUrl})}),e.jsx("span",{children:r.name})]},`${r.name}-${r.emojiName??r.emojiUrl??"tag"}`))}):null,e.jsx("strong",{className:"text-xl font-bold",children:t.name}),e.jsxs("div",{className:"flex min-w-0 flex-col gap-1",children:[e.jsxs("div",{className:"flex items-center gap-1",children:[t.authorAvatarUrl?e.jsx("img",{src:t.authorAvatarUrl,alt:"",className:"h-6 w-6 rounded-full"}):null,e.jsxs("span",{children:[e.jsx("span",{className:"font-bold",style:t.authorHexAccentColor?{color:t.authorHexAccentColor}:{},children:t.authorDisplayName}),":"," "]})]}),e.jsx("span",{className:"text-muted-foreground line-clamp-4 min-w-0",children:t.messagePreview})]})]}),t.previewImageUrl?e.jsx("img",{src:t.previewImageUrl,alt:"",className:"h-28 w-28 rounded-lg object-cover"}):null]}),e.jsxs("div",{className:"flex justify-between",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("span",{children:s.length?e.jsx("ul",{className:"flex items-center gap-2",children:s.map((r,n)=>e.jsxs("li",{className:"border-info/60 bg-info/10 text-info flex items-center gap-1 rounded-md border px-[5px] py-[0.5px] text-sm",children:[e.jsx("span",{className:"h-3 w-3 leading-3",children:e.jsx(U,{name:r.emojiName,url:r.emojiUrl})}),e.jsx("span",{children:r.count})]},`${r.emojiName??r.emojiUrl??"reaction"}-${n}`))}):null}),e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx(N,{name:"Chat"})," ",t.messageCount]}),` · ${t.lastUpdatedDisplay}`]})]}),e.jsxs("span",{className:"flex items-center gap-4",children:[e.jsx("a",{href:t.link.replace(/^https/,"discord"),children:e.jsx(N,{name:"Discord"})}),e.jsx("a",{href:t.link,target:"_blank",rel:"noreferrer noopener",children:e.jsx(N,{name:"ExternalLink"})})]})]})]})})}function U({name:t,url:s}){return s?e.jsx("img",{src:s,alt:t,className:"h-full w-full"}):t||null}function lt(t,s,r){const n=new URLSearchParams(t);return s===r?n.delete("preview"):n.set("preview",s),n}function ut({tabs:t}){return e.jsx(Ke,{className:"scrollbar-thin scrollbar-thumb-scrollbar h-14 min-h-14 overflow-x-auto border-b whitespace-nowrap",children:t.map(s=>e.jsx($e,{value:s.id,hidden:s.hidden,asChild:!0,children:e.jsx(M,{id:`${s.id}-tab`,className:fe("clip-path-button radix-state-active:z-10 radix-state-active:bg-foreground radix-state-active:text-background radix-state-active:hover:bg-foreground/80 radix-state-active:hover:text-background/80 radix-state-inactive:hover:bg-foreground/20 radix-state-inactive:hover:text-foreground/80 focus:bg-foreground/80 focus:text-background/80 relative h-full px-6 py-4 font-mono text-sm uppercase outline-none",s.hidden?"hidden":"inline-block"),preventScrollReset:!0,prefetch:"intent",onClick:s.onClick,to:s.to,children:e.jsxs("span",{className:"flex items-center gap-2",children:[s.status?e.jsx(Ne,{status:s.status}):null,e.jsx("span",{children:s.label})]})})},s.id))})}function dt({appInfo:t,inBrowserBrowserRef:s,problemAppName:r,allApps:n,isUpToDate:a}){return e.jsx(Ce,{playgroundAppName:t?.appName,problemAppName:r,allApps:n,isUpToDate:a,children:t?.dev?.type==="none"?e.jsxs("div",{className:"flex h-full flex-col items-center justify-center gap-4",children:[e.jsx("div",{className:"text-secondary-foreground text-2xl",children:"Non-UI exercise"}),e.jsxs("div",{className:"text-secondary-foreground max-w-md text-center text-balance",children:["This exercise has no application or other UI associated with it."," ",e.jsx("br",{}),"Navigate to"," ",e.jsx(je,{content:t.fullPath,children:e.jsxs("span",{className:"inline-flex cursor-pointer items-center gap-1.5 underline",onClick:()=>{navigator.clipboard.writeText(t.fullPath),Te.success("Copied playground path to clipboard")},children:["the playground directory",e.jsx(N,{name:"Copy",size:"sm"})]})})," ","in your editor and follow the exercise instructions to complete it."]})]}):t?e.jsx(ye,{id:t.appName,appInfo:t,inBrowserBrowserRef:s}):e.jsxs("div",{className:"flex flex-col justify-center gap-2",children:[e.jsx("p",{children:"Please set the playground first"}),r?e.jsx(Ie,{appName:r}):null]})})}export{it as C,ct as D,ut as P,at as R,dt as a,lt as g};
|
|
2
|
+
//# sourceMappingURL=playground-DG4B62WX.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playground-DmEAkxG1.js","sources":["../../../../../node_modules/@radix-ui/react-roving-focus/dist/index.mjs","../../../../../node_modules/@radix-ui/react-tabs/dist/index.mjs","../../../app/components/discord-chat.tsx","../../../app/components/preview-tabs.tsx","../../../app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/__shared/playground.tsx"],"sourcesContent":["\"use client\";\n\n// src/roving-focus-group.tsx\nimport * as React from \"react\";\nimport { composeEventHandlers } from \"@radix-ui/primitive\";\nimport { createCollection } from \"@radix-ui/react-collection\";\nimport { useComposedRefs } from \"@radix-ui/react-compose-refs\";\nimport { createContextScope } from \"@radix-ui/react-context\";\nimport { useId } from \"@radix-ui/react-id\";\nimport { Primitive } from \"@radix-ui/react-primitive\";\nimport { useCallbackRef } from \"@radix-ui/react-use-callback-ref\";\nimport { useControllableState } from \"@radix-ui/react-use-controllable-state\";\nimport { useDirection } from \"@radix-ui/react-direction\";\nimport { jsx } from \"react/jsx-runtime\";\nvar ENTRY_FOCUS = \"rovingFocusGroup.onEntryFocus\";\nvar EVENT_OPTIONS = { bubbles: false, cancelable: true };\nvar GROUP_NAME = \"RovingFocusGroup\";\nvar [Collection, useCollection, createCollectionScope] = createCollection(GROUP_NAME);\nvar [createRovingFocusGroupContext, createRovingFocusGroupScope] = createContextScope(\n GROUP_NAME,\n [createCollectionScope]\n);\nvar [RovingFocusProvider, useRovingFocusContext] = createRovingFocusGroupContext(GROUP_NAME);\nvar RovingFocusGroup = React.forwardRef(\n (props, forwardedRef) => {\n return /* @__PURE__ */ jsx(Collection.Provider, { scope: props.__scopeRovingFocusGroup, children: /* @__PURE__ */ jsx(Collection.Slot, { scope: props.__scopeRovingFocusGroup, children: /* @__PURE__ */ jsx(RovingFocusGroupImpl, { ...props, ref: forwardedRef }) }) });\n }\n);\nRovingFocusGroup.displayName = GROUP_NAME;\nvar RovingFocusGroupImpl = React.forwardRef((props, forwardedRef) => {\n const {\n __scopeRovingFocusGroup,\n orientation,\n loop = false,\n dir,\n currentTabStopId: currentTabStopIdProp,\n defaultCurrentTabStopId,\n onCurrentTabStopIdChange,\n onEntryFocus,\n preventScrollOnEntryFocus = false,\n ...groupProps\n } = props;\n const ref = React.useRef(null);\n const composedRefs = useComposedRefs(forwardedRef, ref);\n const direction = useDirection(dir);\n const [currentTabStopId, setCurrentTabStopId] = useControllableState({\n prop: currentTabStopIdProp,\n defaultProp: defaultCurrentTabStopId ?? null,\n onChange: onCurrentTabStopIdChange,\n caller: GROUP_NAME\n });\n const [isTabbingBackOut, setIsTabbingBackOut] = React.useState(false);\n const handleEntryFocus = useCallbackRef(onEntryFocus);\n const getItems = useCollection(__scopeRovingFocusGroup);\n const isClickFocusRef = React.useRef(false);\n const [focusableItemsCount, setFocusableItemsCount] = React.useState(0);\n React.useEffect(() => {\n const node = ref.current;\n if (node) {\n node.addEventListener(ENTRY_FOCUS, handleEntryFocus);\n return () => node.removeEventListener(ENTRY_FOCUS, handleEntryFocus);\n }\n }, [handleEntryFocus]);\n return /* @__PURE__ */ jsx(\n RovingFocusProvider,\n {\n scope: __scopeRovingFocusGroup,\n orientation,\n dir: direction,\n loop,\n currentTabStopId,\n onItemFocus: React.useCallback(\n (tabStopId) => setCurrentTabStopId(tabStopId),\n [setCurrentTabStopId]\n ),\n onItemShiftTab: React.useCallback(() => setIsTabbingBackOut(true), []),\n onFocusableItemAdd: React.useCallback(\n () => setFocusableItemsCount((prevCount) => prevCount + 1),\n []\n ),\n onFocusableItemRemove: React.useCallback(\n () => setFocusableItemsCount((prevCount) => prevCount - 1),\n []\n ),\n children: /* @__PURE__ */ jsx(\n Primitive.div,\n {\n tabIndex: isTabbingBackOut || focusableItemsCount === 0 ? -1 : 0,\n \"data-orientation\": orientation,\n ...groupProps,\n ref: composedRefs,\n style: { outline: \"none\", ...props.style },\n onMouseDown: composeEventHandlers(props.onMouseDown, () => {\n isClickFocusRef.current = true;\n }),\n onFocus: composeEventHandlers(props.onFocus, (event) => {\n const isKeyboardFocus = !isClickFocusRef.current;\n if (event.target === event.currentTarget && isKeyboardFocus && !isTabbingBackOut) {\n const entryFocusEvent = new CustomEvent(ENTRY_FOCUS, EVENT_OPTIONS);\n event.currentTarget.dispatchEvent(entryFocusEvent);\n if (!entryFocusEvent.defaultPrevented) {\n const items = getItems().filter((item) => item.focusable);\n const activeItem = items.find((item) => item.active);\n const currentItem = items.find((item) => item.id === currentTabStopId);\n const candidateItems = [activeItem, currentItem, ...items].filter(\n Boolean\n );\n const candidateNodes = candidateItems.map((item) => item.ref.current);\n focusFirst(candidateNodes, preventScrollOnEntryFocus);\n }\n }\n isClickFocusRef.current = false;\n }),\n onBlur: composeEventHandlers(props.onBlur, () => setIsTabbingBackOut(false))\n }\n )\n }\n );\n});\nvar ITEM_NAME = \"RovingFocusGroupItem\";\nvar RovingFocusGroupItem = React.forwardRef(\n (props, forwardedRef) => {\n const {\n __scopeRovingFocusGroup,\n focusable = true,\n active = false,\n tabStopId,\n children,\n ...itemProps\n } = props;\n const autoId = useId();\n const id = tabStopId || autoId;\n const context = useRovingFocusContext(ITEM_NAME, __scopeRovingFocusGroup);\n const isCurrentTabStop = context.currentTabStopId === id;\n const getItems = useCollection(__scopeRovingFocusGroup);\n const { onFocusableItemAdd, onFocusableItemRemove, currentTabStopId } = context;\n React.useEffect(() => {\n if (focusable) {\n onFocusableItemAdd();\n return () => onFocusableItemRemove();\n }\n }, [focusable, onFocusableItemAdd, onFocusableItemRemove]);\n return /* @__PURE__ */ jsx(\n Collection.ItemSlot,\n {\n scope: __scopeRovingFocusGroup,\n id,\n focusable,\n active,\n children: /* @__PURE__ */ jsx(\n Primitive.span,\n {\n tabIndex: isCurrentTabStop ? 0 : -1,\n \"data-orientation\": context.orientation,\n ...itemProps,\n ref: forwardedRef,\n onMouseDown: composeEventHandlers(props.onMouseDown, (event) => {\n if (!focusable) event.preventDefault();\n else context.onItemFocus(id);\n }),\n onFocus: composeEventHandlers(props.onFocus, () => context.onItemFocus(id)),\n onKeyDown: composeEventHandlers(props.onKeyDown, (event) => {\n if (event.key === \"Tab\" && event.shiftKey) {\n context.onItemShiftTab();\n return;\n }\n if (event.target !== event.currentTarget) return;\n const focusIntent = getFocusIntent(event, context.orientation, context.dir);\n if (focusIntent !== void 0) {\n if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;\n event.preventDefault();\n const items = getItems().filter((item) => item.focusable);\n let candidateNodes = items.map((item) => item.ref.current);\n if (focusIntent === \"last\") candidateNodes.reverse();\n else if (focusIntent === \"prev\" || focusIntent === \"next\") {\n if (focusIntent === \"prev\") candidateNodes.reverse();\n const currentIndex = candidateNodes.indexOf(event.currentTarget);\n candidateNodes = context.loop ? wrapArray(candidateNodes, currentIndex + 1) : candidateNodes.slice(currentIndex + 1);\n }\n setTimeout(() => focusFirst(candidateNodes));\n }\n }),\n children: typeof children === \"function\" ? children({ isCurrentTabStop, hasTabStop: currentTabStopId != null }) : children\n }\n )\n }\n );\n }\n);\nRovingFocusGroupItem.displayName = ITEM_NAME;\nvar MAP_KEY_TO_FOCUS_INTENT = {\n ArrowLeft: \"prev\",\n ArrowUp: \"prev\",\n ArrowRight: \"next\",\n ArrowDown: \"next\",\n PageUp: \"first\",\n Home: \"first\",\n PageDown: \"last\",\n End: \"last\"\n};\nfunction getDirectionAwareKey(key, dir) {\n if (dir !== \"rtl\") return key;\n return key === \"ArrowLeft\" ? \"ArrowRight\" : key === \"ArrowRight\" ? \"ArrowLeft\" : key;\n}\nfunction getFocusIntent(event, orientation, dir) {\n const key = getDirectionAwareKey(event.key, dir);\n if (orientation === \"vertical\" && [\"ArrowLeft\", \"ArrowRight\"].includes(key)) return void 0;\n if (orientation === \"horizontal\" && [\"ArrowUp\", \"ArrowDown\"].includes(key)) return void 0;\n return MAP_KEY_TO_FOCUS_INTENT[key];\n}\nfunction focusFirst(candidates, preventScroll = false) {\n const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;\n for (const candidate of candidates) {\n if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;\n candidate.focus({ preventScroll });\n if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;\n }\n}\nfunction wrapArray(array, startIndex) {\n return array.map((_, index) => array[(startIndex + index) % array.length]);\n}\nvar Root = RovingFocusGroup;\nvar Item = RovingFocusGroupItem;\nexport {\n Item,\n Root,\n RovingFocusGroup,\n RovingFocusGroupItem,\n createRovingFocusGroupScope\n};\n//# sourceMappingURL=index.mjs.map\n","\"use client\";\n\n// src/tabs.tsx\nimport * as React from \"react\";\nimport { composeEventHandlers } from \"@radix-ui/primitive\";\nimport { createContextScope } from \"@radix-ui/react-context\";\nimport { createRovingFocusGroupScope } from \"@radix-ui/react-roving-focus\";\nimport { Presence } from \"@radix-ui/react-presence\";\nimport { Primitive } from \"@radix-ui/react-primitive\";\nimport * as RovingFocusGroup from \"@radix-ui/react-roving-focus\";\nimport { useDirection } from \"@radix-ui/react-direction\";\nimport { useControllableState } from \"@radix-ui/react-use-controllable-state\";\nimport { useId } from \"@radix-ui/react-id\";\nimport { jsx } from \"react/jsx-runtime\";\nvar TABS_NAME = \"Tabs\";\nvar [createTabsContext, createTabsScope] = createContextScope(TABS_NAME, [\n createRovingFocusGroupScope\n]);\nvar useRovingFocusGroupScope = createRovingFocusGroupScope();\nvar [TabsProvider, useTabsContext] = createTabsContext(TABS_NAME);\nvar Tabs = React.forwardRef(\n (props, forwardedRef) => {\n const {\n __scopeTabs,\n value: valueProp,\n onValueChange,\n defaultValue,\n orientation = \"horizontal\",\n dir,\n activationMode = \"automatic\",\n ...tabsProps\n } = props;\n const direction = useDirection(dir);\n const [value, setValue] = useControllableState({\n prop: valueProp,\n onChange: onValueChange,\n defaultProp: defaultValue ?? \"\",\n caller: TABS_NAME\n });\n return /* @__PURE__ */ jsx(\n TabsProvider,\n {\n scope: __scopeTabs,\n baseId: useId(),\n value,\n onValueChange: setValue,\n orientation,\n dir: direction,\n activationMode,\n children: /* @__PURE__ */ jsx(\n Primitive.div,\n {\n dir: direction,\n \"data-orientation\": orientation,\n ...tabsProps,\n ref: forwardedRef\n }\n )\n }\n );\n }\n);\nTabs.displayName = TABS_NAME;\nvar TAB_LIST_NAME = \"TabsList\";\nvar TabsList = React.forwardRef(\n (props, forwardedRef) => {\n const { __scopeTabs, loop = true, ...listProps } = props;\n const context = useTabsContext(TAB_LIST_NAME, __scopeTabs);\n const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeTabs);\n return /* @__PURE__ */ jsx(\n RovingFocusGroup.Root,\n {\n asChild: true,\n ...rovingFocusGroupScope,\n orientation: context.orientation,\n dir: context.dir,\n loop,\n children: /* @__PURE__ */ jsx(\n Primitive.div,\n {\n role: \"tablist\",\n \"aria-orientation\": context.orientation,\n ...listProps,\n ref: forwardedRef\n }\n )\n }\n );\n }\n);\nTabsList.displayName = TAB_LIST_NAME;\nvar TRIGGER_NAME = \"TabsTrigger\";\nvar TabsTrigger = React.forwardRef(\n (props, forwardedRef) => {\n const { __scopeTabs, value, disabled = false, ...triggerProps } = props;\n const context = useTabsContext(TRIGGER_NAME, __scopeTabs);\n const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeTabs);\n const triggerId = makeTriggerId(context.baseId, value);\n const contentId = makeContentId(context.baseId, value);\n const isSelected = value === context.value;\n return /* @__PURE__ */ jsx(\n RovingFocusGroup.Item,\n {\n asChild: true,\n ...rovingFocusGroupScope,\n focusable: !disabled,\n active: isSelected,\n children: /* @__PURE__ */ jsx(\n Primitive.button,\n {\n type: \"button\",\n role: \"tab\",\n \"aria-selected\": isSelected,\n \"aria-controls\": contentId,\n \"data-state\": isSelected ? \"active\" : \"inactive\",\n \"data-disabled\": disabled ? \"\" : void 0,\n disabled,\n id: triggerId,\n ...triggerProps,\n ref: forwardedRef,\n onMouseDown: composeEventHandlers(props.onMouseDown, (event) => {\n if (!disabled && event.button === 0 && event.ctrlKey === false) {\n context.onValueChange(value);\n } else {\n event.preventDefault();\n }\n }),\n onKeyDown: composeEventHandlers(props.onKeyDown, (event) => {\n if ([\" \", \"Enter\"].includes(event.key)) context.onValueChange(value);\n }),\n onFocus: composeEventHandlers(props.onFocus, () => {\n const isAutomaticActivation = context.activationMode !== \"manual\";\n if (!isSelected && !disabled && isAutomaticActivation) {\n context.onValueChange(value);\n }\n })\n }\n )\n }\n );\n }\n);\nTabsTrigger.displayName = TRIGGER_NAME;\nvar CONTENT_NAME = \"TabsContent\";\nvar TabsContent = React.forwardRef(\n (props, forwardedRef) => {\n const { __scopeTabs, value, forceMount, children, ...contentProps } = props;\n const context = useTabsContext(CONTENT_NAME, __scopeTabs);\n const triggerId = makeTriggerId(context.baseId, value);\n const contentId = makeContentId(context.baseId, value);\n const isSelected = value === context.value;\n const isMountAnimationPreventedRef = React.useRef(isSelected);\n React.useEffect(() => {\n const rAF = requestAnimationFrame(() => isMountAnimationPreventedRef.current = false);\n return () => cancelAnimationFrame(rAF);\n }, []);\n return /* @__PURE__ */ jsx(Presence, { present: forceMount || isSelected, children: ({ present }) => /* @__PURE__ */ jsx(\n Primitive.div,\n {\n \"data-state\": isSelected ? \"active\" : \"inactive\",\n \"data-orientation\": context.orientation,\n role: \"tabpanel\",\n \"aria-labelledby\": triggerId,\n hidden: !present,\n id: contentId,\n tabIndex: 0,\n ...contentProps,\n ref: forwardedRef,\n style: {\n ...props.style,\n animationDuration: isMountAnimationPreventedRef.current ? \"0s\" : void 0\n },\n children: present && children\n }\n ) });\n }\n);\nTabsContent.displayName = CONTENT_NAME;\nfunction makeTriggerId(baseId, value) {\n return `${baseId}-trigger-${value}`;\n}\nfunction makeContentId(baseId, value) {\n return `${baseId}-content-${value}`;\n}\nvar Root2 = Tabs;\nvar List = TabsList;\nvar Trigger = TabsTrigger;\nvar Content = TabsContent;\nexport {\n Content,\n List,\n Root2 as Root,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Trigger,\n createTabsScope\n};\n//# sourceMappingURL=index.mjs.map\n","import * as React from 'react'\nimport { Await, Link } from 'react-router'\nimport { Icon } from '#app/components/icons.tsx'\nimport { Loading } from '#app/components/loading.tsx'\nimport { DiscordCTA, useDiscordCTALink } from '#app/routes/_app+/discord.tsx'\nimport { useAltDown } from '#app/utils/misc.tsx'\nimport { useIsOnline } from '#app/utils/online.ts'\n\ntype EmojiData = {\n\temojiName?: string\n\temojiUrl?: string\n}\n\ntype DiscordTag = { name: string } & EmojiData\ntype DiscordReaction = { count: number } & EmojiData\n\nexport type DiscordThread = {\n\tid: string\n\ttags: DiscordTag[]\n\tname: string\n\tlink: string\n\tauthorDisplayName: string\n\tauthorHexAccentColor?: string | null\n\tauthorAvatarUrl: string | null\n\tmessagePreview: string\n\tmessageCount: number\n\tlastUpdated: string\n\tlastUpdatedDisplay: string\n\tpreviewImageUrl: string | null\n\treactions: DiscordReaction[]\n}\n\nexport function DiscordChat({\n\tdiscordPostsPromise,\n}: {\n\tdiscordPostsPromise: Promise<DiscordThread[]>\n}) {\n\treturn (\n\t\t<div className=\"flex h-full w-full flex-col gap-4 pt-4\">\n\t\t\t<div className=\"text-center\">\n\t\t\t\t<DiscordCTA />\n\t\t\t</div>\n\t\t\t<div className=\"bg-accent scrollbar-thin scrollbar-thumb-scrollbar flex-1 overflow-y-scroll pb-4\">\n\t\t\t\t<DiscordPosts discordPostsPromise={discordPostsPromise} />\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nfunction DiscordPosts({\n\tdiscordPostsPromise,\n}: {\n\tdiscordPostsPromise: Promise<DiscordThread[]>\n}) {\n\tconst ctaLink = useDiscordCTALink()\n\tconst altDown = useAltDown()\n\tconst isOnline = useIsOnline()\n\tif (!isOnline) {\n\t\treturn (\n\t\t\t<div className=\"flex h-full flex-col items-center justify-between\">\n\t\t\t\t<div className=\"text-foreground-destructive flex h-full w-full flex-col items-center justify-center\">\n\t\t\t\t\t<Icon name=\"WifiNoConnection\" size=\"xl\">\n\t\t\t\t\t\tUnable to load discord messages when offline\n\t\t\t\t\t</Icon>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t)\n\t}\n\treturn (\n\t\t<div className=\"flex h-full flex-col items-center justify-between\">\n\t\t\t<React.Suspense\n\t\t\t\tfallback={\n\t\t\t\t\t<div className=\"flex h-full w-full flex-col items-center justify-center\">\n\t\t\t\t\t\t<Loading>Loading Discord Posts</Loading>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t>\n\t\t\t\t<Await\n\t\t\t\t\tresolve={discordPostsPromise}\n\t\t\t\t\terrorElement={\n\t\t\t\t\t\t<div className=\"text-foreground-destructive\">\n\t\t\t\t\t\t\tThere was a problem loading the discord posts\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t>\n\t\t\t\t\t{(posts) => (\n\t\t\t\t\t\t<ul className=\"flex w-full flex-col gap-4 p-3 xl:p-12\">\n\t\t\t\t\t\t\t{posts.map((post) => (\n\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\tkey={post.id}\n\t\t\t\t\t\t\t\t\tclassName=\"bg-background rounded-xl border transition-all duration-200 focus-within:-translate-y-1 focus-within:shadow-lg hover:-translate-y-1 hover:shadow-lg\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<DiscordPost thread={post} />\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t)}\n\t\t\t\t</Await>\n\t\t\t</React.Suspense>\n\t\t\t<div>\n\t\t\t\t<Link\n\t\t\t\t\tto={\n\t\t\t\t\t\taltDown && !ctaLink.includes('oauth')\n\t\t\t\t\t\t\t? ctaLink.replace(/^https/, 'discord')\n\t\t\t\t\t\t\t: ctaLink\n\t\t\t\t\t}\n\t\t\t\t\ttarget={ctaLink.includes('oauth') ? undefined : '_blank'}\n\t\t\t\t\trel=\"noreferrer noopener\"\n\t\t\t\t\tonClick={\n\t\t\t\t\t\taltDown\n\t\t\t\t\t\t\t? (event) => {\n\t\t\t\t\t\t\t\t\tevent.preventDefault()\n\t\t\t\t\t\t\t\t\twindow.open(\n\t\t\t\t\t\t\t\t\t\tevent.currentTarget.href,\n\t\t\t\t\t\t\t\t\t\t'_blank',\n\t\t\t\t\t\t\t\t\t\t'noreferrer noopener',\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t}\n\t\t\t\t\tclassName=\"flex items-center gap-2 p-2 text-xl hover:underline\"\n\t\t\t\t>\n\t\t\t\t\tCreate Post <Icon name=\"ExternalLink\" />\n\t\t\t\t</Link>\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nfunction DiscordPost({ thread }: { thread: DiscordThread }) {\n\tconst reactionsWithCounts = thread.reactions.filter(\n\t\t(reaction) => reaction.count,\n\t)\n\n\treturn (\n\t\t<div>\n\t\t\t<div className=\"flex flex-col gap-2 p-4\">\n\t\t\t\t<div className=\"flex min-w-0 gap-4\">\n\t\t\t\t\t<div className=\"flex min-w-0 flex-col gap-1\">\n\t\t\t\t\t\t{thread.tags.length ? (\n\t\t\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t\t\t{thread.tags.map((tag) => (\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tkey={`${tag.name}-${tag.emojiName ?? tag.emojiUrl ?? 'tag'}`}\n\t\t\t\t\t\t\t\t\t\tclassName=\"bg-accent flex items-center justify-center gap-1 rounded-full px-2 py-1 text-sm\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<span className=\"h-3 w-3 leading-3\">\n\t\t\t\t\t\t\t\t\t\t\t<Emoji name={tag.emojiName} url={tag.emojiUrl} />\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t<span>{tag.name}</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t<strong className=\"text-xl font-bold\">{thread.name}</strong>\n\t\t\t\t\t\t<div className=\"flex min-w-0 flex-col gap-1\">\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-1\">\n\t\t\t\t\t\t\t\t{thread.authorAvatarUrl ? (\n\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\tsrc={thread.authorAvatarUrl}\n\t\t\t\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\t\t\t\tclassName=\"h-6 w-6 rounded-full\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\tclassName=\"font-bold\"\n\t\t\t\t\t\t\t\t\t\tstyle={\n\t\t\t\t\t\t\t\t\t\t\tthread.authorHexAccentColor\n\t\t\t\t\t\t\t\t\t\t\t\t? { color: thread.authorHexAccentColor }\n\t\t\t\t\t\t\t\t\t\t\t\t: {}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{thread.authorDisplayName}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t:{' '}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<span className=\"text-muted-foreground line-clamp-4 min-w-0\">\n\t\t\t\t\t\t\t\t{thread.messagePreview}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t{thread.previewImageUrl ? (\n\t\t\t\t\t\t<img\n\t\t\t\t\t\t\tsrc={thread.previewImageUrl}\n\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\tclassName=\"h-28 w-28 rounded-lg object-cover\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\n\t\t\t\t<div className=\"flex justify-between\">\n\t\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t{reactionsWithCounts.length ? (\n\t\t\t\t\t\t\t\t<ul className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t{reactionsWithCounts.map((reaction, index) => (\n\t\t\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\t\t\tkey={`${reaction.emojiName ?? reaction.emojiUrl ?? 'reaction'}-${index}`}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"border-info/60 bg-info/10 text-info flex items-center gap-1 rounded-md border px-[5px] py-[0.5px] text-sm\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"h-3 w-3 leading-3\">\n\t\t\t\t\t\t\t\t\t\t\t\t<Emoji\n\t\t\t\t\t\t\t\t\t\t\t\t\tname={reaction.emojiName}\n\t\t\t\t\t\t\t\t\t\t\t\t\turl={reaction.emojiUrl}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t<span>{reaction.count}</span>\n\t\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"flex items-center gap-1\">\n\t\t\t\t\t\t\t<span className=\"inline-flex items-center gap-1\">\n\t\t\t\t\t\t\t\t<Icon name=\"Chat\" /> {thread.messageCount}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t{` · ${thread.lastUpdatedDisplay}`}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<span className=\"flex items-center gap-4\">\n\t\t\t\t\t\t<a href={thread.link.replace(/^https/, 'discord')}>\n\t\t\t\t\t\t\t<Icon name=\"Discord\" />\n\t\t\t\t\t\t</a>\n\t\t\t\t\t\t<a href={thread.link} target=\"_blank\" rel=\"noreferrer noopener\">\n\t\t\t\t\t\t\t<Icon name=\"ExternalLink\" />\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nfunction Emoji({ name, url }: { name?: string; url?: string }) {\n\treturn url ? (\n\t\t<img src={url} alt={name} className=\"h-full w-full\" />\n\t) : name ? (\n\t\tname\n\t) : null\n}\n","import * as Tabs from '@radix-ui/react-tabs'\nimport * as React from 'react'\nimport { Link } from 'react-router'\nimport { StatusIndicator } from '#app/components/status-indicator.tsx'\nimport { cn } from '#app/utils/misc.tsx'\n\nexport type PreviewTab = {\n\tid: string\n\tlabel: string\n\tto: string\n\thidden?: boolean\n\tstatus?: 'running' | 'passed' | 'failed' | null\n\tonClick?: React.MouseEventHandler<HTMLAnchorElement>\n}\n\nexport function getPreviewSearchParams(\n\tsearchParams: URLSearchParams,\n\tpreviewValue: string,\n\tdefaultValue: string,\n) {\n\tconst next = new URLSearchParams(searchParams)\n\t// Keep URLs clean by omitting the preview param for the default tab.\n\tif (previewValue === defaultValue) {\n\t\tnext.delete('preview')\n\t} else {\n\t\tnext.set('preview', previewValue)\n\t}\n\treturn next\n}\n\nexport function PreviewTabsList({ tabs }: { tabs: PreviewTab[] }) {\n\treturn (\n\t\t<Tabs.List className=\"scrollbar-thin scrollbar-thumb-scrollbar h-14 min-h-14 overflow-x-auto border-b whitespace-nowrap\">\n\t\t\t{tabs.map((tab) => (\n\t\t\t\t<Tabs.Trigger key={tab.id} value={tab.id} hidden={tab.hidden} asChild>\n\t\t\t\t\t<Link\n\t\t\t\t\t\tid={`${tab.id}-tab`}\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t'clip-path-button radix-state-active:z-10 radix-state-active:bg-foreground radix-state-active:text-background radix-state-active:hover:bg-foreground/80 radix-state-active:hover:text-background/80 radix-state-inactive:hover:bg-foreground/20 radix-state-inactive:hover:text-foreground/80 focus:bg-foreground/80 focus:text-background/80 relative h-full px-6 py-4 font-mono text-sm uppercase outline-none',\n\t\t\t\t\t\t\ttab.hidden ? 'hidden' : 'inline-block',\n\t\t\t\t\t\t)}\n\t\t\t\t\t\tpreventScrollReset\n\t\t\t\t\t\tprefetch=\"intent\"\n\t\t\t\t\t\tonClick={tab.onClick}\n\t\t\t\t\t\tto={tab.to}\n\t\t\t\t\t>\n\t\t\t\t\t\t<span className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t{tab.status ? <StatusIndicator status={tab.status} /> : null}\n\t\t\t\t\t\t\t<span>{tab.label}</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</Link>\n\t\t\t\t</Tabs.Trigger>\n\t\t\t))}\n\t\t</Tabs.List>\n\t)\n}\n","import { toast as showToast } from 'sonner'\nimport { Icon } from '#app/components/icons.tsx'\nimport { type InBrowserBrowserRef } from '#app/components/in-browser-browser'\nimport { SimpleTooltip } from '#app/components/ui/tooltip'\nimport { SetAppToPlayground } from '#app/routes/set-playground'\nimport { PlaygroundWindow } from './playground-window'\nimport { Preview } from './preview'\n\nexport function Playground({\n\tappInfo: playgroundAppInfo,\n\tinBrowserBrowserRef,\n\tproblemAppName,\n\tallApps,\n\tisUpToDate,\n}: {\n\tappInfo: Parameters<typeof Preview>['0']['appInfo'] | null\n\tinBrowserBrowserRef: React.RefObject<InBrowserBrowserRef | null>\n\tproblemAppName?: string\n\tallApps: Array<{ name: string; displayName: string }>\n\tisUpToDate: boolean\n}) {\n\treturn (\n\t\t<PlaygroundWindow\n\t\t\tplaygroundAppName={playgroundAppInfo?.appName}\n\t\t\tproblemAppName={problemAppName}\n\t\t\tallApps={allApps}\n\t\t\tisUpToDate={isUpToDate}\n\t\t>\n\t\t\t{playgroundAppInfo?.dev?.type === 'none' ? (\n\t\t\t\t<div className=\"flex h-full flex-col items-center justify-center gap-4\">\n\t\t\t\t\t<div className=\"text-secondary-foreground text-2xl\">\n\t\t\t\t\t\tNon-UI exercise\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"text-secondary-foreground max-w-md text-center text-balance\">\n\t\t\t\t\t\tThis exercise has no application or other UI associated with it.{' '}\n\t\t\t\t\t\t<br />\n\t\t\t\t\t\tNavigate to{' '}\n\t\t\t\t\t\t<SimpleTooltip content={playgroundAppInfo.fullPath}>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclassName=\"inline-flex cursor-pointer items-center gap-1.5 underline\"\n\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\tvoid navigator.clipboard.writeText(playgroundAppInfo.fullPath)\n\t\t\t\t\t\t\t\t\tshowToast.success('Copied playground path to clipboard')\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tthe playground directory\n\t\t\t\t\t\t\t\t<Icon name=\"Copy\" size=\"sm\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</SimpleTooltip>{' '}\n\t\t\t\t\t\tin your editor and follow the exercise instructions to complete it.\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t) : playgroundAppInfo ? (\n\t\t\t\t<Preview\n\t\t\t\t\tid={playgroundAppInfo.appName}\n\t\t\t\t\tappInfo={playgroundAppInfo}\n\t\t\t\t\tinBrowserBrowserRef={inBrowserBrowserRef}\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<div className=\"flex flex-col justify-center gap-2\">\n\t\t\t\t\t<p>Please set the playground first</p>\n\t\t\t\t\t{problemAppName ? (\n\t\t\t\t\t\t<SetAppToPlayground appName={problemAppName} />\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</PlaygroundWindow>\n\t)\n}\n"],"names":["ENTRY_FOCUS","EVENT_OPTIONS","GROUP_NAME","Collection","useCollection","createCollectionScope","createCollection","createRovingFocusGroupContext","createRovingFocusGroupScope","createContextScope","RovingFocusProvider","useRovingFocusContext","RovingFocusGroup","React.forwardRef","props","forwardedRef","jsx","RovingFocusGroupImpl","__scopeRovingFocusGroup","orientation","loop","dir","currentTabStopIdProp","defaultCurrentTabStopId","onCurrentTabStopIdChange","onEntryFocus","preventScrollOnEntryFocus","groupProps","ref","React.useRef","composedRefs","useComposedRefs","direction","useDirection","currentTabStopId","setCurrentTabStopId","useControllableState","isTabbingBackOut","setIsTabbingBackOut","React.useState","handleEntryFocus","useCallbackRef","getItems","isClickFocusRef","focusableItemsCount","setFocusableItemsCount","React.useEffect","node","React.useCallback","tabStopId","prevCount","Primitive","composeEventHandlers","event","isKeyboardFocus","entryFocusEvent","items","item","activeItem","currentItem","candidateNodes","focusFirst","ITEM_NAME","RovingFocusGroupItem","focusable","active","children","itemProps","autoId","useId","id","context","isCurrentTabStop","onFocusableItemAdd","onFocusableItemRemove","focusIntent","getFocusIntent","currentIndex","wrapArray","MAP_KEY_TO_FOCUS_INTENT","getDirectionAwareKey","key","candidates","preventScroll","PREVIOUSLY_FOCUSED_ELEMENT","candidate","array","startIndex","_","index","Root","Item","TABS_NAME","createTabsContext","useRovingFocusGroupScope","TabsProvider","useTabsContext","Tabs","__scopeTabs","valueProp","onValueChange","defaultValue","activationMode","tabsProps","value","setValue","TAB_LIST_NAME","TabsList","listProps","rovingFocusGroupScope","RovingFocusGroup.Root","TRIGGER_NAME","TabsTrigger","disabled","triggerProps","triggerId","makeTriggerId","contentId","makeContentId","isSelected","RovingFocusGroup.Item","isAutomaticActivation","CONTENT_NAME","TabsContent","forceMount","contentProps","isMountAnimationPreventedRef","rAF","Presence","present","baseId","Root2","List","Trigger","Content","DiscordChat","discordPostsPromise","jsxs","DiscordCTA","DiscordPosts","ctaLink","useDiscordCTALink","altDown","useAltDown","useIsOnline","React.Suspense","Loading","Await","posts","post","DiscordPost","Link","Icon","thread","reactionsWithCounts","reaction","tag","Emoji","name","url","getPreviewSearchParams","searchParams","previewValue","next","PreviewTabsList","tabs","Tabs.List","tab","Tabs.Trigger","cn","StatusIndicator","Playground","playgroundAppInfo","inBrowserBrowserRef","problemAppName","allApps","isUpToDate","PlaygroundWindow","SimpleTooltip","showToast","Preview","SetAppToPlayground"],"mappings":"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","x_google_ignoreList":[0,1]}
|
|
1
|
+
{"version":3,"file":"playground-DG4B62WX.js","sources":["../../../../../node_modules/@radix-ui/react-roving-focus/dist/index.mjs","../../../../../node_modules/@radix-ui/react-tabs/dist/index.mjs","../../../app/components/discord-chat.tsx","../../../app/components/preview-tabs.tsx","../../../app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/__shared/playground.tsx"],"sourcesContent":["\"use client\";\n\n// src/roving-focus-group.tsx\nimport * as React from \"react\";\nimport { composeEventHandlers } from \"@radix-ui/primitive\";\nimport { createCollection } from \"@radix-ui/react-collection\";\nimport { useComposedRefs } from \"@radix-ui/react-compose-refs\";\nimport { createContextScope } from \"@radix-ui/react-context\";\nimport { useId } from \"@radix-ui/react-id\";\nimport { Primitive } from \"@radix-ui/react-primitive\";\nimport { useCallbackRef } from \"@radix-ui/react-use-callback-ref\";\nimport { useControllableState } from \"@radix-ui/react-use-controllable-state\";\nimport { useDirection } from \"@radix-ui/react-direction\";\nimport { jsx } from \"react/jsx-runtime\";\nvar ENTRY_FOCUS = \"rovingFocusGroup.onEntryFocus\";\nvar EVENT_OPTIONS = { bubbles: false, cancelable: true };\nvar GROUP_NAME = \"RovingFocusGroup\";\nvar [Collection, useCollection, createCollectionScope] = createCollection(GROUP_NAME);\nvar [createRovingFocusGroupContext, createRovingFocusGroupScope] = createContextScope(\n GROUP_NAME,\n [createCollectionScope]\n);\nvar [RovingFocusProvider, useRovingFocusContext] = createRovingFocusGroupContext(GROUP_NAME);\nvar RovingFocusGroup = React.forwardRef(\n (props, forwardedRef) => {\n return /* @__PURE__ */ jsx(Collection.Provider, { scope: props.__scopeRovingFocusGroup, children: /* @__PURE__ */ jsx(Collection.Slot, { scope: props.__scopeRovingFocusGroup, children: /* @__PURE__ */ jsx(RovingFocusGroupImpl, { ...props, ref: forwardedRef }) }) });\n }\n);\nRovingFocusGroup.displayName = GROUP_NAME;\nvar RovingFocusGroupImpl = React.forwardRef((props, forwardedRef) => {\n const {\n __scopeRovingFocusGroup,\n orientation,\n loop = false,\n dir,\n currentTabStopId: currentTabStopIdProp,\n defaultCurrentTabStopId,\n onCurrentTabStopIdChange,\n onEntryFocus,\n preventScrollOnEntryFocus = false,\n ...groupProps\n } = props;\n const ref = React.useRef(null);\n const composedRefs = useComposedRefs(forwardedRef, ref);\n const direction = useDirection(dir);\n const [currentTabStopId, setCurrentTabStopId] = useControllableState({\n prop: currentTabStopIdProp,\n defaultProp: defaultCurrentTabStopId ?? null,\n onChange: onCurrentTabStopIdChange,\n caller: GROUP_NAME\n });\n const [isTabbingBackOut, setIsTabbingBackOut] = React.useState(false);\n const handleEntryFocus = useCallbackRef(onEntryFocus);\n const getItems = useCollection(__scopeRovingFocusGroup);\n const isClickFocusRef = React.useRef(false);\n const [focusableItemsCount, setFocusableItemsCount] = React.useState(0);\n React.useEffect(() => {\n const node = ref.current;\n if (node) {\n node.addEventListener(ENTRY_FOCUS, handleEntryFocus);\n return () => node.removeEventListener(ENTRY_FOCUS, handleEntryFocus);\n }\n }, [handleEntryFocus]);\n return /* @__PURE__ */ jsx(\n RovingFocusProvider,\n {\n scope: __scopeRovingFocusGroup,\n orientation,\n dir: direction,\n loop,\n currentTabStopId,\n onItemFocus: React.useCallback(\n (tabStopId) => setCurrentTabStopId(tabStopId),\n [setCurrentTabStopId]\n ),\n onItemShiftTab: React.useCallback(() => setIsTabbingBackOut(true), []),\n onFocusableItemAdd: React.useCallback(\n () => setFocusableItemsCount((prevCount) => prevCount + 1),\n []\n ),\n onFocusableItemRemove: React.useCallback(\n () => setFocusableItemsCount((prevCount) => prevCount - 1),\n []\n ),\n children: /* @__PURE__ */ jsx(\n Primitive.div,\n {\n tabIndex: isTabbingBackOut || focusableItemsCount === 0 ? -1 : 0,\n \"data-orientation\": orientation,\n ...groupProps,\n ref: composedRefs,\n style: { outline: \"none\", ...props.style },\n onMouseDown: composeEventHandlers(props.onMouseDown, () => {\n isClickFocusRef.current = true;\n }),\n onFocus: composeEventHandlers(props.onFocus, (event) => {\n const isKeyboardFocus = !isClickFocusRef.current;\n if (event.target === event.currentTarget && isKeyboardFocus && !isTabbingBackOut) {\n const entryFocusEvent = new CustomEvent(ENTRY_FOCUS, EVENT_OPTIONS);\n event.currentTarget.dispatchEvent(entryFocusEvent);\n if (!entryFocusEvent.defaultPrevented) {\n const items = getItems().filter((item) => item.focusable);\n const activeItem = items.find((item) => item.active);\n const currentItem = items.find((item) => item.id === currentTabStopId);\n const candidateItems = [activeItem, currentItem, ...items].filter(\n Boolean\n );\n const candidateNodes = candidateItems.map((item) => item.ref.current);\n focusFirst(candidateNodes, preventScrollOnEntryFocus);\n }\n }\n isClickFocusRef.current = false;\n }),\n onBlur: composeEventHandlers(props.onBlur, () => setIsTabbingBackOut(false))\n }\n )\n }\n );\n});\nvar ITEM_NAME = \"RovingFocusGroupItem\";\nvar RovingFocusGroupItem = React.forwardRef(\n (props, forwardedRef) => {\n const {\n __scopeRovingFocusGroup,\n focusable = true,\n active = false,\n tabStopId,\n children,\n ...itemProps\n } = props;\n const autoId = useId();\n const id = tabStopId || autoId;\n const context = useRovingFocusContext(ITEM_NAME, __scopeRovingFocusGroup);\n const isCurrentTabStop = context.currentTabStopId === id;\n const getItems = useCollection(__scopeRovingFocusGroup);\n const { onFocusableItemAdd, onFocusableItemRemove, currentTabStopId } = context;\n React.useEffect(() => {\n if (focusable) {\n onFocusableItemAdd();\n return () => onFocusableItemRemove();\n }\n }, [focusable, onFocusableItemAdd, onFocusableItemRemove]);\n return /* @__PURE__ */ jsx(\n Collection.ItemSlot,\n {\n scope: __scopeRovingFocusGroup,\n id,\n focusable,\n active,\n children: /* @__PURE__ */ jsx(\n Primitive.span,\n {\n tabIndex: isCurrentTabStop ? 0 : -1,\n \"data-orientation\": context.orientation,\n ...itemProps,\n ref: forwardedRef,\n onMouseDown: composeEventHandlers(props.onMouseDown, (event) => {\n if (!focusable) event.preventDefault();\n else context.onItemFocus(id);\n }),\n onFocus: composeEventHandlers(props.onFocus, () => context.onItemFocus(id)),\n onKeyDown: composeEventHandlers(props.onKeyDown, (event) => {\n if (event.key === \"Tab\" && event.shiftKey) {\n context.onItemShiftTab();\n return;\n }\n if (event.target !== event.currentTarget) return;\n const focusIntent = getFocusIntent(event, context.orientation, context.dir);\n if (focusIntent !== void 0) {\n if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;\n event.preventDefault();\n const items = getItems().filter((item) => item.focusable);\n let candidateNodes = items.map((item) => item.ref.current);\n if (focusIntent === \"last\") candidateNodes.reverse();\n else if (focusIntent === \"prev\" || focusIntent === \"next\") {\n if (focusIntent === \"prev\") candidateNodes.reverse();\n const currentIndex = candidateNodes.indexOf(event.currentTarget);\n candidateNodes = context.loop ? wrapArray(candidateNodes, currentIndex + 1) : candidateNodes.slice(currentIndex + 1);\n }\n setTimeout(() => focusFirst(candidateNodes));\n }\n }),\n children: typeof children === \"function\" ? children({ isCurrentTabStop, hasTabStop: currentTabStopId != null }) : children\n }\n )\n }\n );\n }\n);\nRovingFocusGroupItem.displayName = ITEM_NAME;\nvar MAP_KEY_TO_FOCUS_INTENT = {\n ArrowLeft: \"prev\",\n ArrowUp: \"prev\",\n ArrowRight: \"next\",\n ArrowDown: \"next\",\n PageUp: \"first\",\n Home: \"first\",\n PageDown: \"last\",\n End: \"last\"\n};\nfunction getDirectionAwareKey(key, dir) {\n if (dir !== \"rtl\") return key;\n return key === \"ArrowLeft\" ? \"ArrowRight\" : key === \"ArrowRight\" ? \"ArrowLeft\" : key;\n}\nfunction getFocusIntent(event, orientation, dir) {\n const key = getDirectionAwareKey(event.key, dir);\n if (orientation === \"vertical\" && [\"ArrowLeft\", \"ArrowRight\"].includes(key)) return void 0;\n if (orientation === \"horizontal\" && [\"ArrowUp\", \"ArrowDown\"].includes(key)) return void 0;\n return MAP_KEY_TO_FOCUS_INTENT[key];\n}\nfunction focusFirst(candidates, preventScroll = false) {\n const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;\n for (const candidate of candidates) {\n if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;\n candidate.focus({ preventScroll });\n if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;\n }\n}\nfunction wrapArray(array, startIndex) {\n return array.map((_, index) => array[(startIndex + index) % array.length]);\n}\nvar Root = RovingFocusGroup;\nvar Item = RovingFocusGroupItem;\nexport {\n Item,\n Root,\n RovingFocusGroup,\n RovingFocusGroupItem,\n createRovingFocusGroupScope\n};\n//# sourceMappingURL=index.mjs.map\n","\"use client\";\n\n// src/tabs.tsx\nimport * as React from \"react\";\nimport { composeEventHandlers } from \"@radix-ui/primitive\";\nimport { createContextScope } from \"@radix-ui/react-context\";\nimport { createRovingFocusGroupScope } from \"@radix-ui/react-roving-focus\";\nimport { Presence } from \"@radix-ui/react-presence\";\nimport { Primitive } from \"@radix-ui/react-primitive\";\nimport * as RovingFocusGroup from \"@radix-ui/react-roving-focus\";\nimport { useDirection } from \"@radix-ui/react-direction\";\nimport { useControllableState } from \"@radix-ui/react-use-controllable-state\";\nimport { useId } from \"@radix-ui/react-id\";\nimport { jsx } from \"react/jsx-runtime\";\nvar TABS_NAME = \"Tabs\";\nvar [createTabsContext, createTabsScope] = createContextScope(TABS_NAME, [\n createRovingFocusGroupScope\n]);\nvar useRovingFocusGroupScope = createRovingFocusGroupScope();\nvar [TabsProvider, useTabsContext] = createTabsContext(TABS_NAME);\nvar Tabs = React.forwardRef(\n (props, forwardedRef) => {\n const {\n __scopeTabs,\n value: valueProp,\n onValueChange,\n defaultValue,\n orientation = \"horizontal\",\n dir,\n activationMode = \"automatic\",\n ...tabsProps\n } = props;\n const direction = useDirection(dir);\n const [value, setValue] = useControllableState({\n prop: valueProp,\n onChange: onValueChange,\n defaultProp: defaultValue ?? \"\",\n caller: TABS_NAME\n });\n return /* @__PURE__ */ jsx(\n TabsProvider,\n {\n scope: __scopeTabs,\n baseId: useId(),\n value,\n onValueChange: setValue,\n orientation,\n dir: direction,\n activationMode,\n children: /* @__PURE__ */ jsx(\n Primitive.div,\n {\n dir: direction,\n \"data-orientation\": orientation,\n ...tabsProps,\n ref: forwardedRef\n }\n )\n }\n );\n }\n);\nTabs.displayName = TABS_NAME;\nvar TAB_LIST_NAME = \"TabsList\";\nvar TabsList = React.forwardRef(\n (props, forwardedRef) => {\n const { __scopeTabs, loop = true, ...listProps } = props;\n const context = useTabsContext(TAB_LIST_NAME, __scopeTabs);\n const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeTabs);\n return /* @__PURE__ */ jsx(\n RovingFocusGroup.Root,\n {\n asChild: true,\n ...rovingFocusGroupScope,\n orientation: context.orientation,\n dir: context.dir,\n loop,\n children: /* @__PURE__ */ jsx(\n Primitive.div,\n {\n role: \"tablist\",\n \"aria-orientation\": context.orientation,\n ...listProps,\n ref: forwardedRef\n }\n )\n }\n );\n }\n);\nTabsList.displayName = TAB_LIST_NAME;\nvar TRIGGER_NAME = \"TabsTrigger\";\nvar TabsTrigger = React.forwardRef(\n (props, forwardedRef) => {\n const { __scopeTabs, value, disabled = false, ...triggerProps } = props;\n const context = useTabsContext(TRIGGER_NAME, __scopeTabs);\n const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeTabs);\n const triggerId = makeTriggerId(context.baseId, value);\n const contentId = makeContentId(context.baseId, value);\n const isSelected = value === context.value;\n return /* @__PURE__ */ jsx(\n RovingFocusGroup.Item,\n {\n asChild: true,\n ...rovingFocusGroupScope,\n focusable: !disabled,\n active: isSelected,\n children: /* @__PURE__ */ jsx(\n Primitive.button,\n {\n type: \"button\",\n role: \"tab\",\n \"aria-selected\": isSelected,\n \"aria-controls\": contentId,\n \"data-state\": isSelected ? \"active\" : \"inactive\",\n \"data-disabled\": disabled ? \"\" : void 0,\n disabled,\n id: triggerId,\n ...triggerProps,\n ref: forwardedRef,\n onMouseDown: composeEventHandlers(props.onMouseDown, (event) => {\n if (!disabled && event.button === 0 && event.ctrlKey === false) {\n context.onValueChange(value);\n } else {\n event.preventDefault();\n }\n }),\n onKeyDown: composeEventHandlers(props.onKeyDown, (event) => {\n if ([\" \", \"Enter\"].includes(event.key)) context.onValueChange(value);\n }),\n onFocus: composeEventHandlers(props.onFocus, () => {\n const isAutomaticActivation = context.activationMode !== \"manual\";\n if (!isSelected && !disabled && isAutomaticActivation) {\n context.onValueChange(value);\n }\n })\n }\n )\n }\n );\n }\n);\nTabsTrigger.displayName = TRIGGER_NAME;\nvar CONTENT_NAME = \"TabsContent\";\nvar TabsContent = React.forwardRef(\n (props, forwardedRef) => {\n const { __scopeTabs, value, forceMount, children, ...contentProps } = props;\n const context = useTabsContext(CONTENT_NAME, __scopeTabs);\n const triggerId = makeTriggerId(context.baseId, value);\n const contentId = makeContentId(context.baseId, value);\n const isSelected = value === context.value;\n const isMountAnimationPreventedRef = React.useRef(isSelected);\n React.useEffect(() => {\n const rAF = requestAnimationFrame(() => isMountAnimationPreventedRef.current = false);\n return () => cancelAnimationFrame(rAF);\n }, []);\n return /* @__PURE__ */ jsx(Presence, { present: forceMount || isSelected, children: ({ present }) => /* @__PURE__ */ jsx(\n Primitive.div,\n {\n \"data-state\": isSelected ? \"active\" : \"inactive\",\n \"data-orientation\": context.orientation,\n role: \"tabpanel\",\n \"aria-labelledby\": triggerId,\n hidden: !present,\n id: contentId,\n tabIndex: 0,\n ...contentProps,\n ref: forwardedRef,\n style: {\n ...props.style,\n animationDuration: isMountAnimationPreventedRef.current ? \"0s\" : void 0\n },\n children: present && children\n }\n ) });\n }\n);\nTabsContent.displayName = CONTENT_NAME;\nfunction makeTriggerId(baseId, value) {\n return `${baseId}-trigger-${value}`;\n}\nfunction makeContentId(baseId, value) {\n return `${baseId}-content-${value}`;\n}\nvar Root2 = Tabs;\nvar List = TabsList;\nvar Trigger = TabsTrigger;\nvar Content = TabsContent;\nexport {\n Content,\n List,\n Root2 as Root,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Trigger,\n createTabsScope\n};\n//# sourceMappingURL=index.mjs.map\n","import * as React from 'react'\nimport { Await, Link } from 'react-router'\nimport { Icon } from '#app/components/icons.tsx'\nimport { Loading } from '#app/components/loading.tsx'\nimport { DiscordCTA, useDiscordCTALink } from '#app/routes/_app+/discord.tsx'\nimport { useAltDown } from '#app/utils/misc.tsx'\nimport { useIsOnline } from '#app/utils/online.ts'\n\ntype EmojiData = {\n\temojiName?: string\n\temojiUrl?: string\n}\n\ntype DiscordTag = { name: string } & EmojiData\ntype DiscordReaction = { count: number } & EmojiData\n\nexport type DiscordThread = {\n\tid: string\n\ttags: DiscordTag[]\n\tname: string\n\tlink: string\n\tauthorDisplayName: string\n\tauthorHexAccentColor?: string | null\n\tauthorAvatarUrl: string | null\n\tmessagePreview: string\n\tmessageCount: number\n\tlastUpdated: string\n\tlastUpdatedDisplay: string\n\tpreviewImageUrl: string | null\n\treactions: DiscordReaction[]\n}\n\nexport function DiscordChat({\n\tdiscordPostsPromise,\n}: {\n\tdiscordPostsPromise: Promise<DiscordThread[]>\n}) {\n\treturn (\n\t\t<div className=\"flex h-full w-full flex-col gap-4 pt-4\">\n\t\t\t<div className=\"text-center\">\n\t\t\t\t<DiscordCTA />\n\t\t\t</div>\n\t\t\t<div className=\"bg-accent scrollbar-thin scrollbar-thumb-scrollbar flex-1 overflow-y-scroll pb-4\">\n\t\t\t\t<DiscordPosts discordPostsPromise={discordPostsPromise} />\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nfunction DiscordPosts({\n\tdiscordPostsPromise,\n}: {\n\tdiscordPostsPromise: Promise<DiscordThread[]>\n}) {\n\tconst ctaLink = useDiscordCTALink()\n\tconst altDown = useAltDown()\n\tconst isOnline = useIsOnline()\n\tif (!isOnline) {\n\t\treturn (\n\t\t\t<div className=\"flex h-full flex-col items-center justify-between\">\n\t\t\t\t<div className=\"text-foreground-destructive flex h-full w-full flex-col items-center justify-center\">\n\t\t\t\t\t<Icon name=\"WifiNoConnection\" size=\"xl\">\n\t\t\t\t\t\tUnable to load discord messages when offline\n\t\t\t\t\t</Icon>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t)\n\t}\n\treturn (\n\t\t<div className=\"flex h-full flex-col items-center justify-between\">\n\t\t\t<React.Suspense\n\t\t\t\tfallback={\n\t\t\t\t\t<div className=\"flex h-full w-full flex-col items-center justify-center\">\n\t\t\t\t\t\t<Loading>Loading Discord Posts</Loading>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t>\n\t\t\t\t<Await\n\t\t\t\t\tresolve={discordPostsPromise}\n\t\t\t\t\terrorElement={\n\t\t\t\t\t\t<div className=\"text-foreground-destructive\">\n\t\t\t\t\t\t\tThere was a problem loading the discord posts\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t>\n\t\t\t\t\t{(posts) => (\n\t\t\t\t\t\t<ul className=\"flex w-full flex-col gap-4 p-3 xl:p-12\">\n\t\t\t\t\t\t\t{posts.map((post) => (\n\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\tkey={post.id}\n\t\t\t\t\t\t\t\t\tclassName=\"bg-background rounded-xl border transition-all duration-200 focus-within:-translate-y-1 focus-within:shadow-lg hover:-translate-y-1 hover:shadow-lg\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<DiscordPost thread={post} />\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t)}\n\t\t\t\t</Await>\n\t\t\t</React.Suspense>\n\t\t\t<div>\n\t\t\t\t<Link\n\t\t\t\t\tto={\n\t\t\t\t\t\taltDown && !ctaLink.includes('oauth')\n\t\t\t\t\t\t\t? ctaLink.replace(/^https/, 'discord')\n\t\t\t\t\t\t\t: ctaLink\n\t\t\t\t\t}\n\t\t\t\t\ttarget={ctaLink.includes('oauth') ? undefined : '_blank'}\n\t\t\t\t\trel=\"noreferrer noopener\"\n\t\t\t\t\tonClick={\n\t\t\t\t\t\taltDown\n\t\t\t\t\t\t\t? (event) => {\n\t\t\t\t\t\t\t\t\tevent.preventDefault()\n\t\t\t\t\t\t\t\t\twindow.open(\n\t\t\t\t\t\t\t\t\t\tevent.currentTarget.href,\n\t\t\t\t\t\t\t\t\t\t'_blank',\n\t\t\t\t\t\t\t\t\t\t'noreferrer noopener',\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t}\n\t\t\t\t\tclassName=\"flex items-center gap-2 p-2 text-xl hover:underline\"\n\t\t\t\t>\n\t\t\t\t\tCreate Post <Icon name=\"ExternalLink\" />\n\t\t\t\t</Link>\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nfunction DiscordPost({ thread }: { thread: DiscordThread }) {\n\tconst reactionsWithCounts = thread.reactions.filter(\n\t\t(reaction) => reaction.count,\n\t)\n\n\treturn (\n\t\t<div>\n\t\t\t<div className=\"flex flex-col gap-2 p-4\">\n\t\t\t\t<div className=\"flex min-w-0 gap-4\">\n\t\t\t\t\t<div className=\"flex min-w-0 flex-col gap-1\">\n\t\t\t\t\t\t{thread.tags.length ? (\n\t\t\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t\t\t{thread.tags.map((tag) => (\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tkey={`${tag.name}-${tag.emojiName ?? tag.emojiUrl ?? 'tag'}`}\n\t\t\t\t\t\t\t\t\t\tclassName=\"bg-accent flex items-center justify-center gap-1 rounded-full px-2 py-1 text-sm\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<span className=\"h-3 w-3 leading-3\">\n\t\t\t\t\t\t\t\t\t\t\t<Emoji name={tag.emojiName} url={tag.emojiUrl} />\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t<span>{tag.name}</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t<strong className=\"text-xl font-bold\">{thread.name}</strong>\n\t\t\t\t\t\t<div className=\"flex min-w-0 flex-col gap-1\">\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-1\">\n\t\t\t\t\t\t\t\t{thread.authorAvatarUrl ? (\n\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\tsrc={thread.authorAvatarUrl}\n\t\t\t\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\t\t\t\tclassName=\"h-6 w-6 rounded-full\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\tclassName=\"font-bold\"\n\t\t\t\t\t\t\t\t\t\tstyle={\n\t\t\t\t\t\t\t\t\t\t\tthread.authorHexAccentColor\n\t\t\t\t\t\t\t\t\t\t\t\t? { color: thread.authorHexAccentColor }\n\t\t\t\t\t\t\t\t\t\t\t\t: {}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{thread.authorDisplayName}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t:{' '}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<span className=\"text-muted-foreground line-clamp-4 min-w-0\">\n\t\t\t\t\t\t\t\t{thread.messagePreview}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t{thread.previewImageUrl ? (\n\t\t\t\t\t\t<img\n\t\t\t\t\t\t\tsrc={thread.previewImageUrl}\n\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\tclassName=\"h-28 w-28 rounded-lg object-cover\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\n\t\t\t\t<div className=\"flex justify-between\">\n\t\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t{reactionsWithCounts.length ? (\n\t\t\t\t\t\t\t\t<ul className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t{reactionsWithCounts.map((reaction, index) => (\n\t\t\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\t\t\tkey={`${reaction.emojiName ?? reaction.emojiUrl ?? 'reaction'}-${index}`}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"border-info/60 bg-info/10 text-info flex items-center gap-1 rounded-md border px-[5px] py-[0.5px] text-sm\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"h-3 w-3 leading-3\">\n\t\t\t\t\t\t\t\t\t\t\t\t<Emoji\n\t\t\t\t\t\t\t\t\t\t\t\t\tname={reaction.emojiName}\n\t\t\t\t\t\t\t\t\t\t\t\t\turl={reaction.emojiUrl}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t<span>{reaction.count}</span>\n\t\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"flex items-center gap-1\">\n\t\t\t\t\t\t\t<span className=\"inline-flex items-center gap-1\">\n\t\t\t\t\t\t\t\t<Icon name=\"Chat\" /> {thread.messageCount}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t{` · ${thread.lastUpdatedDisplay}`}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<span className=\"flex items-center gap-4\">\n\t\t\t\t\t\t<a href={thread.link.replace(/^https/, 'discord')}>\n\t\t\t\t\t\t\t<Icon name=\"Discord\" />\n\t\t\t\t\t\t</a>\n\t\t\t\t\t\t<a href={thread.link} target=\"_blank\" rel=\"noreferrer noopener\">\n\t\t\t\t\t\t\t<Icon name=\"ExternalLink\" />\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nfunction Emoji({ name, url }: { name?: string; url?: string }) {\n\treturn url ? (\n\t\t<img src={url} alt={name} className=\"h-full w-full\" />\n\t) : name ? (\n\t\tname\n\t) : null\n}\n","import * as Tabs from '@radix-ui/react-tabs'\nimport * as React from 'react'\nimport { Link } from 'react-router'\nimport { StatusIndicator } from '#app/components/status-indicator.tsx'\nimport { cn } from '#app/utils/misc.tsx'\n\nexport type PreviewTab = {\n\tid: string\n\tlabel: string\n\tto: string\n\thidden?: boolean\n\tstatus?: 'running' | 'passed' | 'failed' | null\n\tonClick?: React.MouseEventHandler<HTMLAnchorElement>\n}\n\nexport function getPreviewSearchParams(\n\tsearchParams: URLSearchParams,\n\tpreviewValue: string,\n\tdefaultValue: string,\n) {\n\tconst next = new URLSearchParams(searchParams)\n\t// Keep URLs clean by omitting the preview param for the default tab.\n\tif (previewValue === defaultValue) {\n\t\tnext.delete('preview')\n\t} else {\n\t\tnext.set('preview', previewValue)\n\t}\n\treturn next\n}\n\nexport function PreviewTabsList({ tabs }: { tabs: PreviewTab[] }) {\n\treturn (\n\t\t<Tabs.List className=\"scrollbar-thin scrollbar-thumb-scrollbar h-14 min-h-14 overflow-x-auto border-b whitespace-nowrap\">\n\t\t\t{tabs.map((tab) => (\n\t\t\t\t<Tabs.Trigger key={tab.id} value={tab.id} hidden={tab.hidden} asChild>\n\t\t\t\t\t<Link\n\t\t\t\t\t\tid={`${tab.id}-tab`}\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t'clip-path-button radix-state-active:z-10 radix-state-active:bg-foreground radix-state-active:text-background radix-state-active:hover:bg-foreground/80 radix-state-active:hover:text-background/80 radix-state-inactive:hover:bg-foreground/20 radix-state-inactive:hover:text-foreground/80 focus:bg-foreground/80 focus:text-background/80 relative h-full px-6 py-4 font-mono text-sm uppercase outline-none',\n\t\t\t\t\t\t\ttab.hidden ? 'hidden' : 'inline-block',\n\t\t\t\t\t\t)}\n\t\t\t\t\t\tpreventScrollReset\n\t\t\t\t\t\tprefetch=\"intent\"\n\t\t\t\t\t\tonClick={tab.onClick}\n\t\t\t\t\t\tto={tab.to}\n\t\t\t\t\t>\n\t\t\t\t\t\t<span className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t{tab.status ? <StatusIndicator status={tab.status} /> : null}\n\t\t\t\t\t\t\t<span>{tab.label}</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</Link>\n\t\t\t\t</Tabs.Trigger>\n\t\t\t))}\n\t\t</Tabs.List>\n\t)\n}\n","import { toast as showToast } from 'sonner'\nimport { Icon } from '#app/components/icons.tsx'\nimport { type InBrowserBrowserRef } from '#app/components/in-browser-browser'\nimport { SimpleTooltip } from '#app/components/ui/tooltip'\nimport { SetAppToPlayground } from '#app/routes/set-playground'\nimport { PlaygroundWindow } from './playground-window'\nimport { Preview } from './preview'\n\nexport function Playground({\n\tappInfo: playgroundAppInfo,\n\tinBrowserBrowserRef,\n\tproblemAppName,\n\tallApps,\n\tisUpToDate,\n}: {\n\tappInfo: Parameters<typeof Preview>['0']['appInfo'] | null\n\tinBrowserBrowserRef: React.RefObject<InBrowserBrowserRef | null>\n\tproblemAppName?: string\n\tallApps: Array<{ name: string; displayName: string }>\n\tisUpToDate: boolean\n}) {\n\treturn (\n\t\t<PlaygroundWindow\n\t\t\tplaygroundAppName={playgroundAppInfo?.appName}\n\t\t\tproblemAppName={problemAppName}\n\t\t\tallApps={allApps}\n\t\t\tisUpToDate={isUpToDate}\n\t\t>\n\t\t\t{playgroundAppInfo?.dev?.type === 'none' ? (\n\t\t\t\t<div className=\"flex h-full flex-col items-center justify-center gap-4\">\n\t\t\t\t\t<div className=\"text-secondary-foreground text-2xl\">\n\t\t\t\t\t\tNon-UI exercise\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"text-secondary-foreground max-w-md text-center text-balance\">\n\t\t\t\t\t\tThis exercise has no application or other UI associated with it.{' '}\n\t\t\t\t\t\t<br />\n\t\t\t\t\t\tNavigate to{' '}\n\t\t\t\t\t\t<SimpleTooltip content={playgroundAppInfo.fullPath}>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclassName=\"inline-flex cursor-pointer items-center gap-1.5 underline\"\n\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\tvoid navigator.clipboard.writeText(playgroundAppInfo.fullPath)\n\t\t\t\t\t\t\t\t\tshowToast.success('Copied playground path to clipboard')\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tthe playground directory\n\t\t\t\t\t\t\t\t<Icon name=\"Copy\" size=\"sm\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</SimpleTooltip>{' '}\n\t\t\t\t\t\tin your editor and follow the exercise instructions to complete it.\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t) : playgroundAppInfo ? (\n\t\t\t\t<Preview\n\t\t\t\t\tid={playgroundAppInfo.appName}\n\t\t\t\t\tappInfo={playgroundAppInfo}\n\t\t\t\t\tinBrowserBrowserRef={inBrowserBrowserRef}\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<div className=\"flex flex-col justify-center gap-2\">\n\t\t\t\t\t<p>Please set the playground first</p>\n\t\t\t\t\t{problemAppName ? (\n\t\t\t\t\t\t<SetAppToPlayground appName={problemAppName} />\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</PlaygroundWindow>\n\t)\n}\n"],"names":["ENTRY_FOCUS","EVENT_OPTIONS","GROUP_NAME","Collection","useCollection","createCollectionScope","createCollection","createRovingFocusGroupContext","createRovingFocusGroupScope","createContextScope","RovingFocusProvider","useRovingFocusContext","RovingFocusGroup","React.forwardRef","props","forwardedRef","jsx","RovingFocusGroupImpl","__scopeRovingFocusGroup","orientation","loop","dir","currentTabStopIdProp","defaultCurrentTabStopId","onCurrentTabStopIdChange","onEntryFocus","preventScrollOnEntryFocus","groupProps","ref","React.useRef","composedRefs","useComposedRefs","direction","useDirection","currentTabStopId","setCurrentTabStopId","useControllableState","isTabbingBackOut","setIsTabbingBackOut","React.useState","handleEntryFocus","useCallbackRef","getItems","isClickFocusRef","focusableItemsCount","setFocusableItemsCount","React.useEffect","node","React.useCallback","tabStopId","prevCount","Primitive","composeEventHandlers","event","isKeyboardFocus","entryFocusEvent","items","item","activeItem","currentItem","candidateNodes","focusFirst","ITEM_NAME","RovingFocusGroupItem","focusable","active","children","itemProps","autoId","useId","id","context","isCurrentTabStop","onFocusableItemAdd","onFocusableItemRemove","focusIntent","getFocusIntent","currentIndex","wrapArray","MAP_KEY_TO_FOCUS_INTENT","getDirectionAwareKey","key","candidates","preventScroll","PREVIOUSLY_FOCUSED_ELEMENT","candidate","array","startIndex","_","index","Root","Item","TABS_NAME","createTabsContext","useRovingFocusGroupScope","TabsProvider","useTabsContext","Tabs","__scopeTabs","valueProp","onValueChange","defaultValue","activationMode","tabsProps","value","setValue","TAB_LIST_NAME","TabsList","listProps","rovingFocusGroupScope","RovingFocusGroup.Root","TRIGGER_NAME","TabsTrigger","disabled","triggerProps","triggerId","makeTriggerId","contentId","makeContentId","isSelected","RovingFocusGroup.Item","isAutomaticActivation","CONTENT_NAME","TabsContent","forceMount","contentProps","isMountAnimationPreventedRef","rAF","Presence","present","baseId","Root2","List","Trigger","Content","DiscordChat","discordPostsPromise","jsxs","DiscordCTA","DiscordPosts","ctaLink","useDiscordCTALink","altDown","useAltDown","useIsOnline","React.Suspense","Loading","Await","posts","post","DiscordPost","Link","Icon","thread","reactionsWithCounts","reaction","tag","Emoji","name","url","getPreviewSearchParams","searchParams","previewValue","next","PreviewTabsList","tabs","Tabs.List","tab","Tabs.Trigger","cn","StatusIndicator","Playground","playgroundAppInfo","inBrowserBrowserRef","problemAppName","allApps","isUpToDate","PlaygroundWindow","SimpleTooltip","showToast","Preview","SetAppToPlayground"],"mappings":"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","x_google_ignoreList":[0,1]}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{j as e}from"./jsx-runtime-C5WNSv3b.js";import{I as t}from"./misc-W4055b-0.js";import{S as x}from"./tooltip-Tlsyx2YO.js";import{a as n,P as f}from"./set-playground-
|
|
2
|
-
//# sourceMappingURL=playground-window-
|
|
1
|
+
import{j as e}from"./jsx-runtime-C5WNSv3b.js";import{I as t}from"./misc-W4055b-0.js";import{S as x}from"./tooltip-Tlsyx2YO.js";import{a as n,P as f}from"./set-playground-DO5tJu4h.js";function p({playgroundAppName:s,problemAppName:i,isUpToDate:r,allApps:a,children:d}){const o=s===i,c=o&&r?e.jsx(t,{size:"xl",name:"Linked"}):e.jsx(t,{size:"xl",name:"Unlinked",className:"text-foreground-destructive animate-pulse"});let l="Click to reset Playground.";return r||(l="Playground is out of date. Click to reset Playground."),o||(l="Playground is not set to the right app. Click to set Playground."),e.jsxs("div",{className:"flex h-full w-full flex-col justify-between",children:[e.jsxs("div",{className:"flex h-14 shrink-0 items-center justify-start gap-2 border-b px-3",children:[e.jsx("div",{className:"display-alt-up",children:i?e.jsx(n,{appName:i,tooltipText:l,children:c}):e.jsx(x,{content:"No problem app available for this step",children:e.jsx("div",{children:e.jsx(t,{name:"Question"})})})}),e.jsx("div",{className:"display-alt-down",children:s?e.jsx(n,{appName:s,reset:!0,tooltipText:"Reset Playground",children:e.jsx("div",{className:"flex h-7 w-7 items-center justify-center",children:e.jsx(t,{name:"Refresh"})})}):e.jsx("div",{className:"h-7 w-7"})}),e.jsx(f,{allApps:a,playgroundAppName:s})]}),e.jsx("div",{className:"flex min-h-0 flex-1 grow items-stretch justify-center overflow-hidden",children:d})]})}export{p as P};
|
|
2
|
+
//# sourceMappingURL=playground-window-CF8lTXXI.js.map
|
package/build/client/assets/{playground-window-x2mQ5o1O.js.map → playground-window-CF8lTXXI.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playground-window-
|
|
1
|
+
{"version":3,"file":"playground-window-CF8lTXXI.js","sources":["../../../app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/__shared/playground-window.tsx"],"sourcesContent":["import { Icon } from '#app/components/icons'\nimport { SimpleTooltip } from '#app/components/ui/tooltip.tsx'\nimport { PlaygroundChooser, SetPlayground } from '#app/routes/set-playground'\n\nexport function PlaygroundWindow({\n\tplaygroundAppName,\n\tproblemAppName,\n\tisUpToDate,\n\tallApps,\n\tchildren,\n}: {\n\tplaygroundAppName?: string\n\tproblemAppName?: string\n\tisUpToDate: boolean\n\tallApps: Array<{ name: string; displayName: string }>\n\tchildren: React.ReactNode\n}) {\n\tconst isCorrectApp = playgroundAppName === problemAppName\n\tconst playgroundLinkedUI =\n\t\tisCorrectApp && isUpToDate ? (\n\t\t\t<Icon size=\"xl\" name=\"Linked\" />\n\t\t) : (\n\t\t\t<Icon\n\t\t\t\tsize=\"xl\"\n\t\t\t\tname=\"Unlinked\"\n\t\t\t\tclassName=\"text-foreground-destructive animate-pulse\"\n\t\t\t/>\n\t\t)\n\tlet setPlaygroundTooltipText = 'Click to reset Playground.'\n\tif (!isUpToDate) {\n\t\tsetPlaygroundTooltipText =\n\t\t\t'Playground is out of date. Click to reset Playground.'\n\t}\n\tif (!isCorrectApp) {\n\t\tsetPlaygroundTooltipText =\n\t\t\t'Playground is not set to the right app. Click to set Playground.'\n\t}\n\treturn (\n\t\t<div className=\"flex h-full w-full flex-col justify-between\">\n\t\t\t<div className=\"flex h-14 shrink-0 items-center justify-start gap-2 border-b px-3\">\n\t\t\t\t<div className=\"display-alt-up\">\n\t\t\t\t\t{problemAppName ? (\n\t\t\t\t\t\t<SetPlayground\n\t\t\t\t\t\t\tappName={problemAppName}\n\t\t\t\t\t\t\ttooltipText={setPlaygroundTooltipText}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{playgroundLinkedUI}\n\t\t\t\t\t\t</SetPlayground>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<SimpleTooltip content=\"No problem app available for this step\">\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<Icon name=\"Question\" />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</SimpleTooltip>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t<div className=\"display-alt-down\">\n\t\t\t\t\t{playgroundAppName ? (\n\t\t\t\t\t\t<SetPlayground\n\t\t\t\t\t\t\tappName={playgroundAppName}\n\t\t\t\t\t\t\treset\n\t\t\t\t\t\t\ttooltipText=\"Reset Playground\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<div className=\"flex h-7 w-7 items-center justify-center\">\n\t\t\t\t\t\t\t\t<Icon name=\"Refresh\" />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</SetPlayground>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<div className=\"h-7 w-7\" />\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t<PlaygroundChooser\n\t\t\t\t\tallApps={allApps}\n\t\t\t\t\tplaygroundAppName={playgroundAppName}\n\t\t\t\t/>\n\t\t\t</div>\n\t\t\t<div className=\"flex min-h-0 flex-1 grow items-stretch justify-center overflow-hidden\">\n\t\t\t\t{children}\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n"],"names":["PlaygroundWindow","playgroundAppName","problemAppName","isUpToDate","allApps","children","isCorrectApp","playgroundLinkedUI","jsx","Icon","setPlaygroundTooltipText","jsxs","SetPlayground","SimpleTooltip","PlaygroundChooser"],"mappings":"uLAIO,SAASA,EAAiB,CAChC,kBAAAC,EACA,eAAAC,EACA,WAAAC,EACA,QAAAC,EACA,SAAAC,CACD,EAMG,CACF,MAAMC,EAAeL,IAAsBC,EACrCK,EACLD,GAAgBH,EACfK,EAAAA,IAACC,GAAK,KAAK,KAAK,KAAK,QAAA,CAAS,EAE9BD,EAAAA,IAACC,EAAA,CACA,KAAK,KACL,KAAK,WACL,UAAU,2CAAA,CAAA,EAGb,IAAIC,EAA2B,6BAC/B,OAAKP,IACJO,EACC,yDAEGJ,IACJI,EACC,oEAGDC,EAAAA,KAAC,MAAA,CAAI,UAAU,8CACd,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,oEACd,SAAA,CAAAH,EAAAA,IAAC,MAAA,CAAI,UAAU,iBACb,SAAAN,EACAM,EAAAA,IAACI,EAAA,CACA,QAASV,EACT,YAAaQ,EAEZ,SAAAH,CAAA,CAAA,EAGFC,EAAAA,IAACK,EAAA,CAAc,QAAQ,yCACtB,SAAAL,EAAAA,IAAC,MAAA,CACA,SAAAA,EAAAA,IAACC,EAAA,CAAK,KAAK,UAAA,CAAW,CAAA,CACvB,EACD,EAEF,EACAD,EAAAA,IAAC,MAAA,CAAI,UAAU,mBACb,SAAAP,EACAO,EAAAA,IAACI,EAAA,CACA,QAASX,EACT,MAAK,GACL,YAAY,mBAEZ,SAAAO,EAAAA,IAAC,OAAI,UAAU,2CACd,eAACC,EAAA,CAAK,KAAK,UAAU,CAAA,CACtB,CAAA,CAAA,EAGDD,EAAAA,IAAC,MAAA,CAAI,UAAU,UAAU,CAAA,CAE3B,EACAA,EAAAA,IAACM,EAAA,CACA,QAAAV,EACA,kBAAAH,CAAA,CAAA,CACD,EACD,EACAO,EAAAA,IAAC,MAAA,CAAI,UAAU,wEACb,SAAAH,CAAA,CACF,CAAA,EACD,CAEF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{w as y,b as N,k as V,f as D,F as r,L as P}from"./chunk-EPOLDU6W-BCLmut3y.js";import{j as e}from"./jsx-runtime-C5WNSv3b.js";import{B as m}from"./button-Cd-ekki5.js";import{b as R,I as o}from"./misc-W4055b-0.js";import{S as l}from"./tooltip-Tlsyx2YO.js";import{f as u}from"./format-CZ5n8p10.js";import{u as S}from"./root-loader-
|
|
2
|
-
//# sourceMappingURL=preferences-
|
|
1
|
+
import{w as y,b as N,k as V,f as D,F as r,L as P}from"./chunk-EPOLDU6W-BCLmut3y.js";import{j as e}from"./jsx-runtime-C5WNSv3b.js";import{B as m}from"./button-Cd-ekki5.js";import{b as R,I as o}from"./misc-W4055b-0.js";import{S as l}from"./tooltip-Tlsyx2YO.js";import{f as u}from"./format-CZ5n8p10.js";import{u as S}from"./root-loader-BmUqzUDN.js";import"./index-CqIc3cxq.js";import"./index-vDCSPjrM.js";const k=[{value:"best",label:"Best available"},{value:"high",label:"High"},{value:"medium",label:"Medium"},{value:"low",label:"Low"}],A=y(function(){const h=N(),n=S(),c=n.preferences?.player,p=n.preferences?.offlineVideo,f=n.preferences?.fontSize,b=n.preferences?.presence,j=n.preferences?.playground,g=n.preferences?.exerciseWarning,v=V(),i=D(),s=i.data?.offlineVideos??h.offlineVideos,t=s.downloadState.status==="running",x=[s.unavailableVideos>0?`${s.unavailableVideos} unavailable`:null,s.notDownloadableVideos>0?`${s.notDownloadableVideos} not downloadable`:null].filter(Boolean).join(", "),w=s.totalVideos>0&&s.downloadedVideos<s.totalVideos&&s.remainingDownloadBytes>0,a=v.state==="submitting";return R(()=>{i.state==="idle"&&i.load("/resources/offline-videos")},t?2e3:null),e.jsx("div",{className:"h-full w-full overflow-y-auto",children:e.jsxs("main",{className:"container mt-12 flex w-full max-w-3xl grow flex-col gap-4 pb-24",children:[e.jsx("h1",{className:"text-h1 mb-4",children:"Preferences"}),e.jsxs(r,{method:"post",className:"flex w-full max-w-sm flex-col gap-4",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"text-body-xl mb-2",children:"Video Player Preferences"}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("label",{htmlFor:"minResolution",children:"Minimum Resolution:"}),e.jsxs("select",{id:"minResolution",name:"minResolution",defaultValue:c?.minResolution,className:"border-border bg-background text-foreground rounded-md border px-2 py-1",children:[e.jsx("option",{value:"",children:"Auto"}),e.jsx("option",{value:"480",children:"480p"}),e.jsx("option",{value:"720",children:"720p"}),e.jsx("option",{value:"1080",children:"1080p"}),e.jsx("option",{value:"1440",children:"1440p"}),e.jsx("option",{value:"2160",children:"2160p (4K)"})]})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("label",{htmlFor:"maxResolution",children:"Maximum Resolution:"}),e.jsxs("select",{id:"maxResolution",name:"maxResolution",defaultValue:c?.maxResolution,className:"border-border bg-background text-foreground rounded-md border px-2 py-1",children:[e.jsx("option",{value:"",children:"Auto"}),e.jsx("option",{value:"720",children:"720p"}),e.jsx("option",{value:"1080",children:"1080p"}),e.jsx("option",{value:"1440",children:"1440p"}),e.jsx("option",{value:"2160",children:"2160p (4K)"})]})]})]}),e.jsxs("div",{children:[e.jsx("h2",{className:"text-body-xl mb-2",children:"Offline Video Downloads"}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("label",{htmlFor:"downloadResolution",children:"Download Resolution:"}),e.jsx("select",{id:"downloadResolution",name:"downloadResolution",defaultValue:p?.downloadResolution??"best",className:"border-border bg-background text-foreground rounded-md border px-2 py-1",children:k.map(d=>e.jsx("option",{value:d.value,children:d.label},d.value))})]}),e.jsx("p",{className:"text-muted-foreground text-sm",children:"Defaults to the best available resolution."})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"mb-2 flex items-center gap-2",children:[e.jsx("h2",{className:"text-body-xl",children:"Font Size Preference"}),e.jsx(l,{content:"Defaults to 16px",children:e.jsx(o,{name:"Question",tabIndex:0})})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("label",{htmlFor:"fontSize",children:"Font Size"}),e.jsx("input",{type:"number",id:"fontSize",name:"fontSize",defaultValue:f??16,step:"1",min:"12",max:"26",className:"border-border bg-background text-foreground rounded-md border px-2 py-1"})]})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"mb-2 flex items-center gap-2",children:[e.jsx("h2",{className:"text-body-xl",children:"Presence Preference"}),e.jsx(l,{content:"This controls whether your name and avatar are displayed in the pile of faces in navigation",children:e.jsx(o,{name:"Question",tabIndex:0})})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("input",{type:"checkbox",id:"optOutPresence",name:"optOutPresence",defaultChecked:b?.optOut}),e.jsx("label",{htmlFor:"optOutPresence",children:"Opt out of presence features"})]})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"mb-2 flex items-center gap-2",children:[e.jsx("h2",{className:"text-body-xl",children:"Persist Playground"}),e.jsx(l,{content:'When enabled, clicking "Set to Playground" will save the current playground in the "saved-playgrounds" directory.',children:e.jsx(o,{name:"Question",tabIndex:0})})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("input",{type:"checkbox",id:"persistPlayground",name:"persistPlayground",defaultChecked:j?.persist}),e.jsx("label",{htmlFor:"persistPlayground",children:"Enable saving playground"})]})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"mb-2 flex items-center gap-2",children:[e.jsx("h2",{className:"text-body-xl",children:"Exercise Directory Warning"}),e.jsx(l,{content:"When enabled, you'll see a warning banner when you have changes in the exercises directory. This helps remind you to work in the playground directory instead.",children:e.jsx(o,{name:"Question",tabIndex:0})})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("input",{type:"checkbox",id:"dismissExerciseWarning",name:"dismissExerciseWarning",defaultChecked:g?.dismissed}),e.jsx("label",{htmlFor:"dismissExerciseWarning",children:"Dismiss exercise directory warnings"})]})]}),e.jsx("div",{className:"h-4"}),e.jsx(m,{varient:"primary",type:"submit",name:"intent",value:"update-preferences",disabled:a,children:a?"Updating...":"Update Preferences"})]}),e.jsxs("section",{className:"border-border mt-6 flex w-full max-w-xl flex-col gap-3 border-t pt-6",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("h2",{className:"text-body-xl",children:"Offline videos"}),e.jsx(l,{content:"Keep videos ready to watch when you're offline.",children:e.jsx(o,{name:"Question",tabIndex:0})})]}),e.jsx("p",{className:"text-muted-foreground text-sm",children:"Download all workshop videos so you can watch them when offline."}),e.jsxs("p",{className:"text-muted-foreground text-sm",children:["Admins can manage all downloaded videos on the"," ",e.jsx(P,{className:"text-foreground underline",to:"/admin/offline-videos",children:"Offline videos admin page"}),"."]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[e.jsx(r,{method:"post",children:e.jsx(m,{varient:"primary",type:"submit",name:"intent",value:"download-offline-videos",disabled:t||a||s.totalVideos===0,children:t?"Downloading...":"Download all videos"})}),e.jsx(r,{method:"post",children:e.jsx("button",{type:"submit",name:"intent",value:"delete-offline-videos",disabled:a||s.downloadedVideos===0,className:"border-border text-foreground hover:bg-muted inline-flex items-center gap-2 rounded border px-3 py-2 text-sm disabled:cursor-not-allowed disabled:opacity-50",children:"Delete offline videos"})}),e.jsxs("span",{className:"text-muted-foreground text-sm",children:[s.downloadedVideos," of ",s.totalVideos," ","downloaded",x?` (${x})`:null]})]}),e.jsxs("div",{className:"text-muted-foreground text-sm",children:[s.downloadedVideos>0?e.jsxs("p",{children:["Downloaded size: ",u(s.totalBytes)]}):null,w?e.jsxs("p",{children:["Download all size:"," ",u(s.remainingDownloadBytes)]}):null]}),t?e.jsxs("div",{className:"text-muted-foreground text-sm",children:[e.jsxs("p",{children:["Downloading ",s.downloadState.completed," of"," ",s.downloadState.total," videos"]}),s.downloadState.current?e.jsxs("p",{className:"truncate",children:["Current: ",s.downloadState.current.title]}):null]}):null]})]})})});export{A as default};
|
|
2
|
+
//# sourceMappingURL=preferences-Czy5oqWs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preferences-B7ND1VS9.js","sources":["../../../app/routes/_app+/preferences.tsx"],"sourcesContent":["import {\n\tgetPreferences,\n\tsetPreferences,\n} from '@epic-web/workshop-utils/db.server'\nimport {\n\tgetOfflineVideoSummary,\n\tstartOfflineVideoDownload,\n\tdeleteOfflineVideosForWorkshop,\n} from '@epic-web/workshop-utils/offline-videos.server'\nimport {\n\tForm,\n\tLink,\n\tuseFetcher,\n\tuseLoaderData,\n\tuseNavigation,\n} from 'react-router'\nimport { Button } from '#app/components/button.tsx'\nimport { Icon } from '#app/components/icons.tsx'\nimport { SimpleTooltip } from '#app/components/ui/tooltip.tsx'\nimport { formatBytes } from '#app/utils/format.ts'\nimport { ensureUndeployed, useInterval } from '#app/utils/misc.tsx'\nimport { useRootLoaderData } from '#app/utils/root-loader.ts'\nimport { redirectWithToast } from '#app/utils/toast.server.ts'\nimport { type Route } from './+types/preferences.tsx'\n\nconst downloadResolutionOptions = [\n\t{ value: 'best', label: 'Best available' },\n\t{ value: 'high', label: 'High' },\n\t{ value: 'medium', label: 'Medium' },\n\t{ value: 'low', label: 'Low' },\n] as const\ntype DownloadResolutionOption =\n\t(typeof downloadResolutionOptions)[number]['value']\n\nfunction isDownloadResolutionOption(\n\tvalue: FormDataEntryValue | null,\n): value is DownloadResolutionOption {\n\treturn (\n\t\ttypeof value === 'string' &&\n\t\tdownloadResolutionOptions.some((option) => option.value === value)\n\t)\n}\n\nexport async function loader({ request }: Route.LoaderArgs) {\n\tensureUndeployed()\n\tconst [preferences, offlineVideos] = await Promise.all([\n\t\tgetPreferences(),\n\t\tgetOfflineVideoSummary({ request }),\n\t])\n\treturn { preferences, offlineVideos }\n}\n\nexport async function action({ request }: Route.ActionArgs) {\n\tensureUndeployed()\n\tconst formData = await request.formData()\n\tconst intent = formData.get('intent')\n\n\tif (intent === 'download-offline-videos') {\n\t\tconst result = await startOfflineVideoDownload({ request })\n\t\tif (result.queued === 0) {\n\t\t\tconst description =\n\t\t\t\tresult.available === 0\n\t\t\t\t\t? 'No downloadable videos were found for this workshop.'\n\t\t\t\t\t: result.unavailable > 0\n\t\t\t\t\t\t? 'All available videos are already downloaded. Some videos require access to download.'\n\t\t\t\t\t\t: 'All available videos are already downloaded.'\n\t\t\treturn redirectWithToast('/preferences', {\n\t\t\t\ttitle: 'Offline videos are ready',\n\t\t\t\tdescription,\n\t\t\t\ttype: 'success',\n\t\t\t})\n\t\t}\n\n\t\treturn redirectWithToast('/preferences', {\n\t\t\ttitle: 'Offline downloads started',\n\t\t\tdescription: `Queued ${result.queued} video${result.queued === 1 ? '' : 's'} for download.`,\n\t\t\ttype: 'success',\n\t\t})\n\t}\n\n\tif (intent === 'delete-offline-videos') {\n\t\tconst result = await deleteOfflineVideosForWorkshop()\n\t\tconst description =\n\t\t\tresult.deletedFiles === 0\n\t\t\t\t? 'No offline videos were removed.'\n\t\t\t\t: `Removed ${result.deletedFiles} offline video${\n\t\t\t\t\t\tresult.deletedFiles === 1 ? '' : 's'\n\t\t\t\t\t}.`\n\t\treturn redirectWithToast('/preferences', {\n\t\t\ttitle: 'Offline videos cleared',\n\t\t\tdescription,\n\t\t\ttype: 'success',\n\t\t})\n\t}\n\n\tconst minResolution = formData.get('minResolution')\n\tconst maxResolution = formData.get('maxResolution')\n\tconst downloadResolution = formData.get('downloadResolution')\n\tconst fontSize = formData.get('fontSize')\n\tconst optOutPresence = formData.get('optOutPresence') === 'on'\n\tconst persistPlayground = formData.get('persistPlayground') === 'on'\n\tconst dismissExerciseWarning = formData.get('dismissExerciseWarning') === 'on'\n\tconst downloadResolutionValue = isDownloadResolutionOption(downloadResolution)\n\t\t? downloadResolution\n\t\t: undefined\n\n\tawait setPreferences({\n\t\tplayer: {\n\t\t\tminResolution: minResolution ? Number(minResolution) : undefined,\n\t\t\tmaxResolution: maxResolution ? Number(maxResolution) : undefined,\n\t\t},\n\t\tofflineVideo: {\n\t\t\tdownloadResolution: downloadResolutionValue,\n\t\t},\n\t\tfontSize: fontSize ? Number(fontSize) : undefined,\n\t\tpresence: { optOut: optOutPresence },\n\t\tplayground: { persist: persistPlayground },\n\t\texerciseWarning: { dismissed: dismissExerciseWarning },\n\t})\n\n\treturn redirectWithToast('/preferences', {\n\t\ttitle: 'Preferences updated',\n\t\tdescription: 'Your preferences have been updated.',\n\t\ttype: 'success',\n\t})\n}\n\nexport default function AccountSettings() {\n\tconst loaderData = useLoaderData<typeof loader>()\n\tconst rootData = useRootLoaderData()\n\tconst playerPreferences = rootData.preferences?.player\n\tconst offlineVideoPreferences = rootData.preferences?.offlineVideo\n\tconst fontSizePreference = rootData.preferences?.fontSize\n\tconst presencePreferences = rootData.preferences?.presence\n\tconst playgroundPreferences = rootData.preferences?.playground\n\tconst exerciseWarningPreferences = rootData.preferences?.exerciseWarning\n\tconst navigation = useNavigation()\n\tconst offlineVideosFetcher = useFetcher<typeof loader>()\n\tconst offlineVideos =\n\t\tofflineVideosFetcher.data?.offlineVideos ?? loaderData.offlineVideos\n\tconst isDownloading = offlineVideos.downloadState.status === 'running'\n\tconst offlineVideoNotes = [\n\t\tofflineVideos.unavailableVideos > 0\n\t\t\t? `${offlineVideos.unavailableVideos} unavailable`\n\t\t\t: null,\n\t\tofflineVideos.notDownloadableVideos > 0\n\t\t\t? `${offlineVideos.notDownloadableVideos} not downloadable`\n\t\t\t: null,\n\t]\n\t\t.filter(Boolean)\n\t\t.join(', ')\n\tconst showRemainingDownloadSize =\n\t\tofflineVideos.totalVideos > 0 &&\n\t\tofflineVideos.downloadedVideos < offlineVideos.totalVideos &&\n\t\tofflineVideos.remainingDownloadBytes > 0\n\n\tconst isSubmitting = navigation.state === 'submitting'\n\n\tuseInterval(\n\t\t() => {\n\t\t\tif (offlineVideosFetcher.state === 'idle') {\n\t\t\t\tvoid offlineVideosFetcher.load('/resources/offline-videos')\n\t\t\t}\n\t\t},\n\t\tisDownloading ? 2000 : null,\n\t)\n\n\treturn (\n\t\t<div className=\"h-full w-full overflow-y-auto\">\n\t\t\t<main className=\"container mt-12 flex w-full max-w-3xl grow flex-col gap-4 pb-24\">\n\t\t\t\t<h1 className=\"text-h1 mb-4\">Preferences</h1>\n\t\t\t\t<Form method=\"post\" className=\"flex w-full max-w-sm flex-col gap-4\">\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<h2 className=\"text-body-xl mb-2\">Video Player Preferences</h2>\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<label htmlFor=\"minResolution\">Minimum Resolution:</label>\n\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\tid=\"minResolution\"\n\t\t\t\t\t\t\t\tname=\"minResolution\"\n\t\t\t\t\t\t\t\tdefaultValue={playerPreferences?.minResolution}\n\t\t\t\t\t\t\t\tclassName=\"border-border bg-background text-foreground rounded-md border px-2 py-1\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<option value=\"\">Auto</option>\n\t\t\t\t\t\t\t\t<option value=\"480\">480p</option>\n\t\t\t\t\t\t\t\t<option value=\"720\">720p</option>\n\t\t\t\t\t\t\t\t<option value=\"1080\">1080p</option>\n\t\t\t\t\t\t\t\t<option value=\"1440\">1440p</option>\n\t\t\t\t\t\t\t\t<option value=\"2160\">2160p (4K)</option>\n\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<label htmlFor=\"maxResolution\">Maximum Resolution:</label>\n\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\tid=\"maxResolution\"\n\t\t\t\t\t\t\t\tname=\"maxResolution\"\n\t\t\t\t\t\t\t\tdefaultValue={playerPreferences?.maxResolution}\n\t\t\t\t\t\t\t\tclassName=\"border-border bg-background text-foreground rounded-md border px-2 py-1\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<option value=\"\">Auto</option>\n\t\t\t\t\t\t\t\t<option value=\"720\">720p</option>\n\t\t\t\t\t\t\t\t<option value=\"1080\">1080p</option>\n\t\t\t\t\t\t\t\t<option value=\"1440\">1440p</option>\n\t\t\t\t\t\t\t\t<option value=\"2160\">2160p (4K)</option>\n\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<h2 className=\"text-body-xl mb-2\">Offline Video Downloads</h2>\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<label htmlFor=\"downloadResolution\">Download Resolution:</label>\n\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\tid=\"downloadResolution\"\n\t\t\t\t\t\t\t\tname=\"downloadResolution\"\n\t\t\t\t\t\t\t\tdefaultValue={\n\t\t\t\t\t\t\t\t\tofflineVideoPreferences?.downloadResolution ?? 'best'\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tclassName=\"border-border bg-background text-foreground rounded-md border px-2 py-1\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{downloadResolutionOptions.map((option) => (\n\t\t\t\t\t\t\t\t\t<option key={option.value} value={option.value}>\n\t\t\t\t\t\t\t\t\t\t{option.label}\n\t\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<p className=\"text-muted-foreground text-sm\">\n\t\t\t\t\t\t\tDefaults to the best available resolution.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<div className=\"mb-2 flex items-center gap-2\">\n\t\t\t\t\t\t\t<h2 className=\"text-body-xl\">Font Size Preference</h2>\n\t\t\t\t\t\t\t<SimpleTooltip content=\"Defaults to 16px\">\n\t\t\t\t\t\t\t\t<Icon name=\"Question\" tabIndex={0} />\n\t\t\t\t\t\t\t</SimpleTooltip>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<label htmlFor=\"fontSize\">Font Size</label>\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\tid=\"fontSize\"\n\t\t\t\t\t\t\t\tname=\"fontSize\"\n\t\t\t\t\t\t\t\tdefaultValue={fontSizePreference ?? 16}\n\t\t\t\t\t\t\t\tstep=\"1\"\n\t\t\t\t\t\t\t\tmin=\"12\"\n\t\t\t\t\t\t\t\tmax=\"26\"\n\t\t\t\t\t\t\t\tclassName=\"border-border bg-background text-foreground rounded-md border px-2 py-1\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<div className=\"mb-2 flex items-center gap-2\">\n\t\t\t\t\t\t\t<h2 className=\"text-body-xl\">Presence Preference</h2>\n\n\t\t\t\t\t\t\t<SimpleTooltip content=\"This controls whether your name and avatar are displayed in the pile of faces in navigation\">\n\t\t\t\t\t\t\t\t<Icon name=\"Question\" tabIndex={0} />\n\t\t\t\t\t\t\t</SimpleTooltip>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tid=\"optOutPresence\"\n\t\t\t\t\t\t\t\tname=\"optOutPresence\"\n\t\t\t\t\t\t\t\tdefaultChecked={presencePreferences?.optOut}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<label htmlFor=\"optOutPresence\">\n\t\t\t\t\t\t\t\tOpt out of presence features\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<div className=\"mb-2 flex items-center gap-2\">\n\t\t\t\t\t\t\t<h2 className=\"text-body-xl\">Persist Playground</h2>\n\n\t\t\t\t\t\t\t<SimpleTooltip\n\t\t\t\t\t\t\t\tcontent={`When enabled, clicking \"Set to Playground\" will save the current playground in the \"saved-playgrounds\" directory.`}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Icon name=\"Question\" tabIndex={0} />\n\t\t\t\t\t\t\t</SimpleTooltip>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tid=\"persistPlayground\"\n\t\t\t\t\t\t\t\tname=\"persistPlayground\"\n\t\t\t\t\t\t\t\tdefaultChecked={playgroundPreferences?.persist}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<label htmlFor=\"persistPlayground\">\n\t\t\t\t\t\t\t\tEnable saving playground\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<div className=\"mb-2 flex items-center gap-2\">\n\t\t\t\t\t\t\t<h2 className=\"text-body-xl\">Exercise Directory Warning</h2>\n\n\t\t\t\t\t\t\t<SimpleTooltip\n\t\t\t\t\t\t\t\tcontent={`When enabled, you'll see a warning banner when you have changes in the exercises directory. This helps remind you to work in the playground directory instead.`}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Icon name=\"Question\" tabIndex={0} />\n\t\t\t\t\t\t\t</SimpleTooltip>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tid=\"dismissExerciseWarning\"\n\t\t\t\t\t\t\t\tname=\"dismissExerciseWarning\"\n\t\t\t\t\t\t\t\tdefaultChecked={exerciseWarningPreferences?.dismissed}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<label htmlFor=\"dismissExerciseWarning\">\n\t\t\t\t\t\t\t\tDismiss exercise directory warnings\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div className=\"h-4\" />\n\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvarient=\"primary\"\n\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\tname=\"intent\"\n\t\t\t\t\t\tvalue=\"update-preferences\"\n\t\t\t\t\t\tdisabled={isSubmitting}\n\t\t\t\t\t>\n\t\t\t\t\t\t{isSubmitting ? 'Updating...' : 'Update Preferences'}\n\t\t\t\t\t</Button>\n\t\t\t\t</Form>\n\n\t\t\t\t<section className=\"border-border mt-6 flex w-full max-w-xl flex-col gap-3 border-t pt-6\">\n\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t<h2 className=\"text-body-xl\">Offline videos</h2>\n\t\t\t\t\t\t<SimpleTooltip content=\"Keep videos ready to watch when you're offline.\">\n\t\t\t\t\t\t\t<Icon name=\"Question\" tabIndex={0} />\n\t\t\t\t\t\t</SimpleTooltip>\n\t\t\t\t\t</div>\n\t\t\t\t\t<p className=\"text-muted-foreground text-sm\">\n\t\t\t\t\t\tDownload all workshop videos so you can watch them when offline.\n\t\t\t\t\t</p>\n\t\t\t\t\t<p className=\"text-muted-foreground text-sm\">\n\t\t\t\t\t\tAdmins can manage all downloaded videos on the{' '}\n\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\tclassName=\"text-foreground underline\"\n\t\t\t\t\t\t\tto=\"/admin/offline-videos\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tOffline videos admin page\n\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t.\n\t\t\t\t\t</p>\n\t\t\t\t\t<div className=\"flex flex-wrap items-center gap-3\">\n\t\t\t\t\t\t<Form method=\"post\">\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvarient=\"primary\"\n\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\tname=\"intent\"\n\t\t\t\t\t\t\t\tvalue=\"download-offline-videos\"\n\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\tisDownloading ||\n\t\t\t\t\t\t\t\t\tisSubmitting ||\n\t\t\t\t\t\t\t\t\tofflineVideos.totalVideos === 0\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isDownloading ? 'Downloading...' : 'Download all videos'}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</Form>\n\t\t\t\t\t\t<Form method=\"post\">\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\tname=\"intent\"\n\t\t\t\t\t\t\t\tvalue=\"delete-offline-videos\"\n\t\t\t\t\t\t\t\tdisabled={isSubmitting || offlineVideos.downloadedVideos === 0}\n\t\t\t\t\t\t\t\tclassName=\"border-border text-foreground hover:bg-muted inline-flex items-center gap-2 rounded border px-3 py-2 text-sm disabled:cursor-not-allowed disabled:opacity-50\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tDelete offline videos\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</Form>\n\t\t\t\t\t\t<span className=\"text-muted-foreground text-sm\">\n\t\t\t\t\t\t\t{offlineVideos.downloadedVideos} of {offlineVideos.totalVideos}{' '}\n\t\t\t\t\t\t\tdownloaded\n\t\t\t\t\t\t\t{offlineVideoNotes ? ` (${offlineVideoNotes})` : null}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"text-muted-foreground text-sm\">\n\t\t\t\t\t\t{offlineVideos.downloadedVideos > 0 ? (\n\t\t\t\t\t\t\t<p>Downloaded size: {formatBytes(offlineVideos.totalBytes)}</p>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{showRemainingDownloadSize ? (\n\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\tDownload all size:{' '}\n\t\t\t\t\t\t\t\t{formatBytes(offlineVideos.remainingDownloadBytes)}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t</div>\n\t\t\t\t\t{isDownloading ? (\n\t\t\t\t\t\t<div className=\"text-muted-foreground text-sm\">\n\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\tDownloading {offlineVideos.downloadState.completed} of{' '}\n\t\t\t\t\t\t\t\t{offlineVideos.downloadState.total} videos\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t{offlineVideos.downloadState.current ? (\n\t\t\t\t\t\t\t\t<p className=\"truncate\">\n\t\t\t\t\t\t\t\t\tCurrent: {offlineVideos.downloadState.current.title}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : null}\n\t\t\t\t</section>\n\t\t\t</main>\n\t\t</div>\n\t)\n}\n"],"names":["downloadResolutionOptions","value","label","preferences","_UNSAFE_withComponentProps","loaderData","useLoaderData","rootData","useRootLoaderData","playerPreferences","player","offlineVideoPreferences","offlineVideo","fontSizePreference","fontSize","presencePreferences","presence","playgroundPreferences","playground","exerciseWarningPreferences","exerciseWarning","navigation","useNavigation","offlineVideosFetcher","useFetcher","offlineVideos","data","isDownloading","downloadState","status","offlineVideoNotes","unavailableVideos","notDownloadableVideos","filter","Boolean","join","showRemainingDownloadSize","totalVideos","downloadedVideos","remainingDownloadBytes","isSubmitting","state","useInterval","load","className","children","jsxs","jsx","Form","method","htmlFor","id","name","defaultValue","minResolution","maxResolution","downloadResolution","map","option","SimpleTooltip","content","Icon","tabIndex","type","step","min","max","defaultChecked","optOut","persist","dismissed","Button","varient","disabled","Link","to","formatBytes","totalBytes","completed","total","current","title"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"preferences-Czy5oqWs.js","sources":["../../../app/routes/_app+/preferences.tsx"],"sourcesContent":["import {\n\tgetPreferences,\n\tsetPreferences,\n} from '@epic-web/workshop-utils/db.server'\nimport {\n\tgetOfflineVideoSummary,\n\tstartOfflineVideoDownload,\n\tdeleteOfflineVideosForWorkshop,\n} from '@epic-web/workshop-utils/offline-videos.server'\nimport {\n\tForm,\n\tLink,\n\tuseFetcher,\n\tuseLoaderData,\n\tuseNavigation,\n} from 'react-router'\nimport { Button } from '#app/components/button.tsx'\nimport { Icon } from '#app/components/icons.tsx'\nimport { SimpleTooltip } from '#app/components/ui/tooltip.tsx'\nimport { formatBytes } from '#app/utils/format.ts'\nimport { ensureUndeployed, useInterval } from '#app/utils/misc.tsx'\nimport { useRootLoaderData } from '#app/utils/root-loader.ts'\nimport { redirectWithToast } from '#app/utils/toast.server.ts'\nimport { type Route } from './+types/preferences.tsx'\n\nconst downloadResolutionOptions = [\n\t{ value: 'best', label: 'Best available' },\n\t{ value: 'high', label: 'High' },\n\t{ value: 'medium', label: 'Medium' },\n\t{ value: 'low', label: 'Low' },\n] as const\ntype DownloadResolutionOption =\n\t(typeof downloadResolutionOptions)[number]['value']\n\nfunction isDownloadResolutionOption(\n\tvalue: FormDataEntryValue | null,\n): value is DownloadResolutionOption {\n\treturn (\n\t\ttypeof value === 'string' &&\n\t\tdownloadResolutionOptions.some((option) => option.value === value)\n\t)\n}\n\nexport async function loader({ request }: Route.LoaderArgs) {\n\tensureUndeployed()\n\tconst [preferences, offlineVideos] = await Promise.all([\n\t\tgetPreferences(),\n\t\tgetOfflineVideoSummary({ request }),\n\t])\n\treturn { preferences, offlineVideos }\n}\n\nexport async function action({ request }: Route.ActionArgs) {\n\tensureUndeployed()\n\tconst formData = await request.formData()\n\tconst intent = formData.get('intent')\n\n\tif (intent === 'download-offline-videos') {\n\t\tconst result = await startOfflineVideoDownload({ request })\n\t\tif (result.queued === 0) {\n\t\t\tconst description =\n\t\t\t\tresult.available === 0\n\t\t\t\t\t? 'No downloadable videos were found for this workshop.'\n\t\t\t\t\t: result.unavailable > 0\n\t\t\t\t\t\t? 'All available videos are already downloaded. Some videos require access to download.'\n\t\t\t\t\t\t: 'All available videos are already downloaded.'\n\t\t\treturn redirectWithToast('/preferences', {\n\t\t\t\ttitle: 'Offline videos are ready',\n\t\t\t\tdescription,\n\t\t\t\ttype: 'success',\n\t\t\t})\n\t\t}\n\n\t\treturn redirectWithToast('/preferences', {\n\t\t\ttitle: 'Offline downloads started',\n\t\t\tdescription: `Queued ${result.queued} video${result.queued === 1 ? '' : 's'} for download.`,\n\t\t\ttype: 'success',\n\t\t})\n\t}\n\n\tif (intent === 'delete-offline-videos') {\n\t\tconst result = await deleteOfflineVideosForWorkshop()\n\t\tconst description =\n\t\t\tresult.deletedFiles === 0\n\t\t\t\t? 'No offline videos were removed.'\n\t\t\t\t: `Removed ${result.deletedFiles} offline video${\n\t\t\t\t\t\tresult.deletedFiles === 1 ? '' : 's'\n\t\t\t\t\t}.`\n\t\treturn redirectWithToast('/preferences', {\n\t\t\ttitle: 'Offline videos cleared',\n\t\t\tdescription,\n\t\t\ttype: 'success',\n\t\t})\n\t}\n\n\tconst minResolution = formData.get('minResolution')\n\tconst maxResolution = formData.get('maxResolution')\n\tconst downloadResolution = formData.get('downloadResolution')\n\tconst fontSize = formData.get('fontSize')\n\tconst optOutPresence = formData.get('optOutPresence') === 'on'\n\tconst persistPlayground = formData.get('persistPlayground') === 'on'\n\tconst dismissExerciseWarning = formData.get('dismissExerciseWarning') === 'on'\n\tconst downloadResolutionValue = isDownloadResolutionOption(downloadResolution)\n\t\t? downloadResolution\n\t\t: undefined\n\n\tawait setPreferences({\n\t\tplayer: {\n\t\t\tminResolution: minResolution ? Number(minResolution) : undefined,\n\t\t\tmaxResolution: maxResolution ? Number(maxResolution) : undefined,\n\t\t},\n\t\tofflineVideo: {\n\t\t\tdownloadResolution: downloadResolutionValue,\n\t\t},\n\t\tfontSize: fontSize ? Number(fontSize) : undefined,\n\t\tpresence: { optOut: optOutPresence },\n\t\tplayground: { persist: persistPlayground },\n\t\texerciseWarning: { dismissed: dismissExerciseWarning },\n\t})\n\n\treturn redirectWithToast('/preferences', {\n\t\ttitle: 'Preferences updated',\n\t\tdescription: 'Your preferences have been updated.',\n\t\ttype: 'success',\n\t})\n}\n\nexport default function AccountSettings() {\n\tconst loaderData = useLoaderData<typeof loader>()\n\tconst rootData = useRootLoaderData()\n\tconst playerPreferences = rootData.preferences?.player\n\tconst offlineVideoPreferences = rootData.preferences?.offlineVideo\n\tconst fontSizePreference = rootData.preferences?.fontSize\n\tconst presencePreferences = rootData.preferences?.presence\n\tconst playgroundPreferences = rootData.preferences?.playground\n\tconst exerciseWarningPreferences = rootData.preferences?.exerciseWarning\n\tconst navigation = useNavigation()\n\tconst offlineVideosFetcher = useFetcher<typeof loader>()\n\tconst offlineVideos =\n\t\tofflineVideosFetcher.data?.offlineVideos ?? loaderData.offlineVideos\n\tconst isDownloading = offlineVideos.downloadState.status === 'running'\n\tconst offlineVideoNotes = [\n\t\tofflineVideos.unavailableVideos > 0\n\t\t\t? `${offlineVideos.unavailableVideos} unavailable`\n\t\t\t: null,\n\t\tofflineVideos.notDownloadableVideos > 0\n\t\t\t? `${offlineVideos.notDownloadableVideos} not downloadable`\n\t\t\t: null,\n\t]\n\t\t.filter(Boolean)\n\t\t.join(', ')\n\tconst showRemainingDownloadSize =\n\t\tofflineVideos.totalVideos > 0 &&\n\t\tofflineVideos.downloadedVideos < offlineVideos.totalVideos &&\n\t\tofflineVideos.remainingDownloadBytes > 0\n\n\tconst isSubmitting = navigation.state === 'submitting'\n\n\tuseInterval(\n\t\t() => {\n\t\t\tif (offlineVideosFetcher.state === 'idle') {\n\t\t\t\tvoid offlineVideosFetcher.load('/resources/offline-videos')\n\t\t\t}\n\t\t},\n\t\tisDownloading ? 2000 : null,\n\t)\n\n\treturn (\n\t\t<div className=\"h-full w-full overflow-y-auto\">\n\t\t\t<main className=\"container mt-12 flex w-full max-w-3xl grow flex-col gap-4 pb-24\">\n\t\t\t\t<h1 className=\"text-h1 mb-4\">Preferences</h1>\n\t\t\t\t<Form method=\"post\" className=\"flex w-full max-w-sm flex-col gap-4\">\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<h2 className=\"text-body-xl mb-2\">Video Player Preferences</h2>\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<label htmlFor=\"minResolution\">Minimum Resolution:</label>\n\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\tid=\"minResolution\"\n\t\t\t\t\t\t\t\tname=\"minResolution\"\n\t\t\t\t\t\t\t\tdefaultValue={playerPreferences?.minResolution}\n\t\t\t\t\t\t\t\tclassName=\"border-border bg-background text-foreground rounded-md border px-2 py-1\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<option value=\"\">Auto</option>\n\t\t\t\t\t\t\t\t<option value=\"480\">480p</option>\n\t\t\t\t\t\t\t\t<option value=\"720\">720p</option>\n\t\t\t\t\t\t\t\t<option value=\"1080\">1080p</option>\n\t\t\t\t\t\t\t\t<option value=\"1440\">1440p</option>\n\t\t\t\t\t\t\t\t<option value=\"2160\">2160p (4K)</option>\n\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<label htmlFor=\"maxResolution\">Maximum Resolution:</label>\n\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\tid=\"maxResolution\"\n\t\t\t\t\t\t\t\tname=\"maxResolution\"\n\t\t\t\t\t\t\t\tdefaultValue={playerPreferences?.maxResolution}\n\t\t\t\t\t\t\t\tclassName=\"border-border bg-background text-foreground rounded-md border px-2 py-1\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<option value=\"\">Auto</option>\n\t\t\t\t\t\t\t\t<option value=\"720\">720p</option>\n\t\t\t\t\t\t\t\t<option value=\"1080\">1080p</option>\n\t\t\t\t\t\t\t\t<option value=\"1440\">1440p</option>\n\t\t\t\t\t\t\t\t<option value=\"2160\">2160p (4K)</option>\n\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<h2 className=\"text-body-xl mb-2\">Offline Video Downloads</h2>\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<label htmlFor=\"downloadResolution\">Download Resolution:</label>\n\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\tid=\"downloadResolution\"\n\t\t\t\t\t\t\t\tname=\"downloadResolution\"\n\t\t\t\t\t\t\t\tdefaultValue={\n\t\t\t\t\t\t\t\t\tofflineVideoPreferences?.downloadResolution ?? 'best'\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tclassName=\"border-border bg-background text-foreground rounded-md border px-2 py-1\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{downloadResolutionOptions.map((option) => (\n\t\t\t\t\t\t\t\t\t<option key={option.value} value={option.value}>\n\t\t\t\t\t\t\t\t\t\t{option.label}\n\t\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<p className=\"text-muted-foreground text-sm\">\n\t\t\t\t\t\t\tDefaults to the best available resolution.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<div className=\"mb-2 flex items-center gap-2\">\n\t\t\t\t\t\t\t<h2 className=\"text-body-xl\">Font Size Preference</h2>\n\t\t\t\t\t\t\t<SimpleTooltip content=\"Defaults to 16px\">\n\t\t\t\t\t\t\t\t<Icon name=\"Question\" tabIndex={0} />\n\t\t\t\t\t\t\t</SimpleTooltip>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<label htmlFor=\"fontSize\">Font Size</label>\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\tid=\"fontSize\"\n\t\t\t\t\t\t\t\tname=\"fontSize\"\n\t\t\t\t\t\t\t\tdefaultValue={fontSizePreference ?? 16}\n\t\t\t\t\t\t\t\tstep=\"1\"\n\t\t\t\t\t\t\t\tmin=\"12\"\n\t\t\t\t\t\t\t\tmax=\"26\"\n\t\t\t\t\t\t\t\tclassName=\"border-border bg-background text-foreground rounded-md border px-2 py-1\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<div className=\"mb-2 flex items-center gap-2\">\n\t\t\t\t\t\t\t<h2 className=\"text-body-xl\">Presence Preference</h2>\n\n\t\t\t\t\t\t\t<SimpleTooltip content=\"This controls whether your name and avatar are displayed in the pile of faces in navigation\">\n\t\t\t\t\t\t\t\t<Icon name=\"Question\" tabIndex={0} />\n\t\t\t\t\t\t\t</SimpleTooltip>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tid=\"optOutPresence\"\n\t\t\t\t\t\t\t\tname=\"optOutPresence\"\n\t\t\t\t\t\t\t\tdefaultChecked={presencePreferences?.optOut}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<label htmlFor=\"optOutPresence\">\n\t\t\t\t\t\t\t\tOpt out of presence features\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<div className=\"mb-2 flex items-center gap-2\">\n\t\t\t\t\t\t\t<h2 className=\"text-body-xl\">Persist Playground</h2>\n\n\t\t\t\t\t\t\t<SimpleTooltip\n\t\t\t\t\t\t\t\tcontent={`When enabled, clicking \"Set to Playground\" will save the current playground in the \"saved-playgrounds\" directory.`}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Icon name=\"Question\" tabIndex={0} />\n\t\t\t\t\t\t\t</SimpleTooltip>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tid=\"persistPlayground\"\n\t\t\t\t\t\t\t\tname=\"persistPlayground\"\n\t\t\t\t\t\t\t\tdefaultChecked={playgroundPreferences?.persist}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<label htmlFor=\"persistPlayground\">\n\t\t\t\t\t\t\t\tEnable saving playground\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<div className=\"mb-2 flex items-center gap-2\">\n\t\t\t\t\t\t\t<h2 className=\"text-body-xl\">Exercise Directory Warning</h2>\n\n\t\t\t\t\t\t\t<SimpleTooltip\n\t\t\t\t\t\t\t\tcontent={`When enabled, you'll see a warning banner when you have changes in the exercises directory. This helps remind you to work in the playground directory instead.`}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Icon name=\"Question\" tabIndex={0} />\n\t\t\t\t\t\t\t</SimpleTooltip>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tid=\"dismissExerciseWarning\"\n\t\t\t\t\t\t\t\tname=\"dismissExerciseWarning\"\n\t\t\t\t\t\t\t\tdefaultChecked={exerciseWarningPreferences?.dismissed}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<label htmlFor=\"dismissExerciseWarning\">\n\t\t\t\t\t\t\t\tDismiss exercise directory warnings\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div className=\"h-4\" />\n\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvarient=\"primary\"\n\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\tname=\"intent\"\n\t\t\t\t\t\tvalue=\"update-preferences\"\n\t\t\t\t\t\tdisabled={isSubmitting}\n\t\t\t\t\t>\n\t\t\t\t\t\t{isSubmitting ? 'Updating...' : 'Update Preferences'}\n\t\t\t\t\t</Button>\n\t\t\t\t</Form>\n\n\t\t\t\t<section className=\"border-border mt-6 flex w-full max-w-xl flex-col gap-3 border-t pt-6\">\n\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t<h2 className=\"text-body-xl\">Offline videos</h2>\n\t\t\t\t\t\t<SimpleTooltip content=\"Keep videos ready to watch when you're offline.\">\n\t\t\t\t\t\t\t<Icon name=\"Question\" tabIndex={0} />\n\t\t\t\t\t\t</SimpleTooltip>\n\t\t\t\t\t</div>\n\t\t\t\t\t<p className=\"text-muted-foreground text-sm\">\n\t\t\t\t\t\tDownload all workshop videos so you can watch them when offline.\n\t\t\t\t\t</p>\n\t\t\t\t\t<p className=\"text-muted-foreground text-sm\">\n\t\t\t\t\t\tAdmins can manage all downloaded videos on the{' '}\n\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\tclassName=\"text-foreground underline\"\n\t\t\t\t\t\t\tto=\"/admin/offline-videos\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tOffline videos admin page\n\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t.\n\t\t\t\t\t</p>\n\t\t\t\t\t<div className=\"flex flex-wrap items-center gap-3\">\n\t\t\t\t\t\t<Form method=\"post\">\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvarient=\"primary\"\n\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\tname=\"intent\"\n\t\t\t\t\t\t\t\tvalue=\"download-offline-videos\"\n\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\tisDownloading ||\n\t\t\t\t\t\t\t\t\tisSubmitting ||\n\t\t\t\t\t\t\t\t\tofflineVideos.totalVideos === 0\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isDownloading ? 'Downloading...' : 'Download all videos'}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</Form>\n\t\t\t\t\t\t<Form method=\"post\">\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\tname=\"intent\"\n\t\t\t\t\t\t\t\tvalue=\"delete-offline-videos\"\n\t\t\t\t\t\t\t\tdisabled={isSubmitting || offlineVideos.downloadedVideos === 0}\n\t\t\t\t\t\t\t\tclassName=\"border-border text-foreground hover:bg-muted inline-flex items-center gap-2 rounded border px-3 py-2 text-sm disabled:cursor-not-allowed disabled:opacity-50\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tDelete offline videos\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</Form>\n\t\t\t\t\t\t<span className=\"text-muted-foreground text-sm\">\n\t\t\t\t\t\t\t{offlineVideos.downloadedVideos} of {offlineVideos.totalVideos}{' '}\n\t\t\t\t\t\t\tdownloaded\n\t\t\t\t\t\t\t{offlineVideoNotes ? ` (${offlineVideoNotes})` : null}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"text-muted-foreground text-sm\">\n\t\t\t\t\t\t{offlineVideos.downloadedVideos > 0 ? (\n\t\t\t\t\t\t\t<p>Downloaded size: {formatBytes(offlineVideos.totalBytes)}</p>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{showRemainingDownloadSize ? (\n\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\tDownload all size:{' '}\n\t\t\t\t\t\t\t\t{formatBytes(offlineVideos.remainingDownloadBytes)}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t</div>\n\t\t\t\t\t{isDownloading ? (\n\t\t\t\t\t\t<div className=\"text-muted-foreground text-sm\">\n\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\tDownloading {offlineVideos.downloadState.completed} of{' '}\n\t\t\t\t\t\t\t\t{offlineVideos.downloadState.total} videos\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t{offlineVideos.downloadState.current ? (\n\t\t\t\t\t\t\t\t<p className=\"truncate\">\n\t\t\t\t\t\t\t\t\tCurrent: {offlineVideos.downloadState.current.title}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : null}\n\t\t\t\t</section>\n\t\t\t</main>\n\t\t</div>\n\t)\n}\n"],"names":["downloadResolutionOptions","value","label","preferences","_UNSAFE_withComponentProps","loaderData","useLoaderData","rootData","useRootLoaderData","playerPreferences","player","offlineVideoPreferences","offlineVideo","fontSizePreference","fontSize","presencePreferences","presence","playgroundPreferences","playground","exerciseWarningPreferences","exerciseWarning","navigation","useNavigation","offlineVideosFetcher","useFetcher","offlineVideos","data","isDownloading","downloadState","status","offlineVideoNotes","unavailableVideos","notDownloadableVideos","filter","Boolean","join","showRemainingDownloadSize","totalVideos","downloadedVideos","remainingDownloadBytes","isSubmitting","state","useInterval","load","className","children","jsxs","jsx","Form","method","htmlFor","id","name","defaultValue","minResolution","maxResolution","downloadResolution","map","option","SimpleTooltip","content","Icon","tabIndex","type","step","min","max","defaultChecked","optOut","persist","dismissed","Button","varient","disabled","Link","to","formatBytes","totalBytes","completed","total","current","title"],"mappings":"kZAyBA,MAAMA,EAA4B,CACjC,CAAEC,MAAO,OAAQC,MAAO,gBAAiB,EACzC,CAAED,MAAO,OAAQC,MAAO,MAAO,EAC/B,CAAED,MAAO,SAAUC,MAAO,QAAS,EACnC,CAAED,MAAO,MAAOC,MAAO,KAAM,CAAA,EAkG9BC,EAAAC,EAAA,UAA0C,CACzC,MAAMC,EAAaC,EAAA,EACbC,EAAWC,EAAA,EACXC,EAAoBF,EAASJ,aAAaO,OAC1CC,EAA0BJ,EAASJ,aAAaS,aAChDC,EAAqBN,EAASJ,aAAaW,SAC3CC,EAAsBR,EAASJ,aAAaa,SAC5CC,EAAwBV,EAASJ,aAAae,WAC9CC,EAA6BZ,EAASJ,aAAaiB,gBACnDC,EAAaC,EAAA,EACbC,EAAuBC,EAAA,EACvBC,EACLF,EAAqBG,MAAMD,eAAiBpB,EAAWoB,cAClDE,EAAgBF,EAAcG,cAAcC,SAAW,UACvDC,EAAoB,CACzBL,EAAcM,kBAAoB,EAC/B,GAAGN,EAAcM,iBAAiB,eAClC,KACHN,EAAcO,sBAAwB,EACnC,GAAGP,EAAcO,qBAAqB,oBACtC,IAAA,EAEFC,OAAOC,OAAO,EACdC,KAAK,IAAI,EACLC,EACLX,EAAcY,YAAc,GAC5BZ,EAAca,iBAAmBb,EAAcY,aAC/CZ,EAAcc,uBAAyB,EAElCC,EAAenB,EAAWoB,QAAU,aAE1CC,OAAAA,EACC,IAAM,CACDnB,EAAqBkB,QAAU,QAC7BlB,EAAqBoB,KAAK,2BAA2B,CAE5D,EACAhB,EAAgB,IAAO,IACxB,QAGE,MAAA,CAAIiB,UAAU,gCACdC,SAAAC,EAAAA,KAAC,OAAA,CAAKF,UAAU,kEACfC,SAAA,CAAAE,EAAAA,IAAC,KAAA,CAAGH,UAAU,eAAeC,SAAA,aAAA,CAAW,EACxCC,EAAAA,KAACE,EAAA,CAAKC,OAAO,OAAOL,UAAU,sCAC7BC,SAAA,CAAAC,EAAAA,KAAC,MAAA,CACAD,SAAA,CAAAE,EAAAA,IAAC,KAAA,CAAGH,UAAU,oBAAoBC,SAAA,0BAAA,CAAwB,EAC1DC,EAAAA,KAAC,MAAA,CAAIF,UAAU,0BACdC,SAAA,CAAAE,EAAAA,IAAC,QAAA,CAAMG,QAAQ,gBAAgBL,SAAA,qBAAA,CAAmB,EAClDC,EAAAA,KAAC,SAAA,CACAK,GAAG,gBACHC,KAAK,gBACLC,aAAc5C,GAAmB6C,cACjCV,UAAU,0EAEVC,SAAA,CAAAE,EAAAA,IAAC,SAAA,CAAO9C,MAAM,GAAG4C,SAAA,MAAA,CAAI,EACrBE,EAAAA,IAAC,SAAA,CAAO9C,MAAM,MAAM4C,SAAA,MAAA,CAAI,EACxBE,EAAAA,IAAC,SAAA,CAAO9C,MAAM,MAAM4C,SAAA,MAAA,CAAI,EACxBE,EAAAA,IAAC,SAAA,CAAO9C,MAAM,OAAO4C,SAAA,OAAA,CAAK,EAC1BE,EAAAA,IAAC,SAAA,CAAO9C,MAAM,OAAO4C,SAAA,OAAA,CAAK,EAC1BE,EAAAA,IAAC,SAAA,CAAO9C,MAAM,OAAO4C,SAAA,YAAA,CAAU,CAAA,CAAA,CAChC,CAAA,CAAA,CACD,EACAC,EAAAA,KAAC,MAAA,CAAIF,UAAU,0BACdC,SAAA,CAAAE,EAAAA,IAAC,QAAA,CAAMG,QAAQ,gBAAgBL,SAAA,qBAAA,CAAmB,EAClDC,EAAAA,KAAC,SAAA,CACAK,GAAG,gBACHC,KAAK,gBACLC,aAAc5C,GAAmB8C,cACjCX,UAAU,0EAEVC,SAAA,CAAAE,EAAAA,IAAC,SAAA,CAAO9C,MAAM,GAAG4C,SAAA,MAAA,CAAI,EACrBE,EAAAA,IAAC,SAAA,CAAO9C,MAAM,MAAM4C,SAAA,MAAA,CAAI,EACxBE,EAAAA,IAAC,SAAA,CAAO9C,MAAM,OAAO4C,SAAA,OAAA,CAAK,EAC1BE,EAAAA,IAAC,SAAA,CAAO9C,MAAM,OAAO4C,SAAA,OAAA,CAAK,EAC1BE,EAAAA,IAAC,SAAA,CAAO9C,MAAM,OAAO4C,SAAA,YAAA,CAAU,CAAA,CAAA,CAChC,CAAA,CAAA,CACD,CAAA,CAAA,CACD,SACC,MAAA,CACAA,SAAA,CAAAE,EAAAA,IAAC,KAAA,CAAGH,UAAU,oBAAoBC,SAAA,yBAAA,CAAuB,EACzDC,EAAAA,KAAC,MAAA,CAAIF,UAAU,0BACdC,SAAA,CAAAE,EAAAA,IAAC,QAAA,CAAMG,QAAQ,qBAAqBL,SAAA,sBAAA,CAAoB,EACxDE,EAAAA,IAAC,SAAA,CACAI,GAAG,qBACHC,KAAK,qBACLC,aACC1C,GAAyB6C,oBAAsB,OAEhDZ,UAAU,0EAETC,SAAA7C,EAA0ByD,IAAKC,GAC/BX,EAAAA,IAAC,SAAA,CAA0B9C,MAAOyD,EAAOzD,MACvC4C,SAAAa,EAAOxD,KAAA,EADIwD,EAAOzD,KAEpB,CACA,CAAA,CACF,CAAA,CAAA,CACD,EACA8C,EAAAA,IAAC,IAAA,CAAEH,UAAU,gCAAgCC,SAAA,4CAAA,CAE7C,CAAA,CAAA,CACD,SACC,MAAA,CACAA,SAAA,CAAAC,EAAAA,KAAC,MAAA,CAAIF,UAAU,+BACdC,SAAA,CAAAE,EAAAA,IAAC,KAAA,CAAGH,UAAU,eAAeC,SAAA,sBAAA,CAAoB,EACjDE,EAAAA,IAACY,EAAA,CAAcC,QAAQ,mBACtBf,SAAAE,EAAAA,IAACc,GAAKT,KAAK,WAAWU,SAAU,EAAG,CAAA,CACpC,CAAA,CAAA,CACD,EACAhB,EAAAA,KAAC,MAAA,CAAIF,UAAU,0BACdC,SAAA,CAAAE,EAAAA,IAAC,QAAA,CAAMG,QAAQ,WAAWL,SAAA,WAAA,CAAS,EACnCE,EAAAA,IAAC,QAAA,CACAgB,KAAK,SACLZ,GAAG,WACHC,KAAK,WACLC,aAAcxC,GAAsB,GACpCmD,KAAK,IACLC,IAAI,KACJC,IAAI,KACJtB,UAAU,yEAAA,CACX,CAAA,CAAA,CACD,CAAA,CAAA,CACD,SAEC,MAAA,CACAC,SAAA,CAAAC,EAAAA,KAAC,MAAA,CAAIF,UAAU,+BACdC,SAAA,CAAAE,EAAAA,IAAC,KAAA,CAAGH,UAAU,eAAeC,SAAA,qBAAA,CAAmB,EAEhDE,EAAAA,IAACY,EAAA,CAAcC,QAAQ,8FACtBf,SAAAE,EAAAA,IAACc,GAAKT,KAAK,WAAWU,SAAU,EAAG,CAAA,CACpC,CAAA,CAAA,CACD,EACAhB,EAAAA,KAAC,MAAA,CAAIF,UAAU,0BACdC,SAAA,CAAAE,EAAAA,IAAC,QAAA,CACAgB,KAAK,WACLZ,GAAG,iBACHC,KAAK,iBACLe,eAAgBpD,GAAqBqD,MAAA,CACtC,EACArB,EAAAA,IAAC,QAAA,CAAMG,QAAQ,iBAAiBL,SAAA,8BAAA,CAEhC,CAAA,CAAA,CACD,CAAA,CAAA,CACD,SAEC,MAAA,CACAA,SAAA,CAAAC,EAAAA,KAAC,MAAA,CAAIF,UAAU,+BACdC,SAAA,CAAAE,EAAAA,IAAC,KAAA,CAAGH,UAAU,eAAeC,SAAA,oBAAA,CAAkB,EAE/CE,EAAAA,IAACY,EAAA,CACAC,QAAS,oHAETf,SAAAE,EAAAA,IAACc,EAAA,CAAKT,KAAK,WAAWU,SAAU,EAAG,CAAA,CACpC,CAAA,CAAA,CACD,EACAhB,EAAAA,KAAC,MAAA,CAAIF,UAAU,0BACdC,SAAA,CAAAE,EAAAA,IAAC,QAAA,CACAgB,KAAK,WACLZ,GAAG,oBACHC,KAAK,oBACLe,eAAgBlD,GAAuBoD,OAAA,CACxC,EACAtB,EAAAA,IAAC,QAAA,CAAMG,QAAQ,oBAAoBL,SAAA,0BAAA,CAEnC,CAAA,CAAA,CACD,CAAA,CAAA,CACD,SAEC,MAAA,CACAA,SAAA,CAAAC,EAAAA,KAAC,MAAA,CAAIF,UAAU,+BACdC,SAAA,CAAAE,EAAAA,IAAC,KAAA,CAAGH,UAAU,eAAeC,SAAA,4BAAA,CAA0B,EAEvDE,EAAAA,IAACY,EAAA,CACAC,QAAS,iKAETf,SAAAE,EAAAA,IAACc,EAAA,CAAKT,KAAK,WAAWU,SAAU,EAAG,CAAA,CACpC,CAAA,CAAA,CACD,EACAhB,EAAAA,KAAC,MAAA,CAAIF,UAAU,0BACdC,SAAA,CAAAE,EAAAA,IAAC,QAAA,CACAgB,KAAK,WACLZ,GAAG,yBACHC,KAAK,yBACLe,eAAgBhD,GAA4BmD,SAAA,CAC7C,EACAvB,EAAAA,IAAC,QAAA,CAAMG,QAAQ,yBAAyBL,SAAA,qCAAA,CAExC,CAAA,CAAA,CACD,CAAA,CAAA,CACD,EAEAE,EAAAA,IAAC,MAAA,CAAIH,UAAU,KAAA,CAAM,EAErBG,EAAAA,IAACwB,EAAA,CACAC,QAAQ,UACRT,KAAK,SACLX,KAAK,SACLnD,MAAM,qBACNwE,SAAUjC,EAETK,WAAe,cAAgB,oBAAA,CACjC,CAAA,CAAA,CACD,EAEAC,EAAAA,KAAC,UAAA,CAAQF,UAAU,uEAClBC,SAAA,CAAAC,EAAAA,KAAC,MAAA,CAAIF,UAAU,0BACdC,SAAA,CAAAE,EAAAA,IAAC,KAAA,CAAGH,UAAU,eAAeC,SAAA,gBAAA,CAAc,EAC3CE,EAAAA,IAACY,EAAA,CAAcC,QAAQ,kDACtBf,SAAAE,EAAAA,IAACc,GAAKT,KAAK,WAAWU,SAAU,EAAG,CAAA,CACpC,CAAA,CAAA,CACD,EACAf,EAAAA,IAAC,IAAA,CAAEH,UAAU,gCAAgCC,SAAA,kEAAA,CAE7C,EACAC,EAAAA,KAAC,IAAA,CAAEF,UAAU,gCAAgCC,SAAA,CAAA,iDACG,IAC/CE,EAAAA,IAAC2B,EAAA,CACA9B,UAAU,4BACV+B,GAAG,wBACH9B,SAAA,4BAED,EAAO,GAAA,CAAA,CAER,EACAC,EAAAA,KAAC,MAAA,CAAIF,UAAU,oCACdC,SAAA,CAAAE,EAAAA,IAACC,EAAA,CAAKC,OAAO,OACZJ,SAAAE,EAAAA,IAACwB,EAAA,CACAC,QAAQ,UACRT,KAAK,SACLX,KAAK,SACLnD,MAAM,0BACNwE,SACC9C,GACAa,GACAf,EAAcY,cAAgB,EAG9BQ,WAAgB,iBAAmB,sBACrC,CAAA,CACD,EACAE,EAAAA,IAACC,EAAA,CAAKC,OAAO,OACZJ,SAAAE,EAAAA,IAAC,SAAA,CACAgB,KAAK,SACLX,KAAK,SACLnD,MAAM,wBACNwE,SAAUjC,GAAgBf,EAAca,mBAAqB,EAC7DM,UAAU,+JACVC,SAAA,wBAED,CAAA,CACD,EACAC,EAAAA,KAAC,OAAA,CAAKF,UAAU,gCACdC,SAAA,CAAApB,EAAca,iBAAiB,OAAKb,EAAcY,YAAa,IAAI,aAEnEP,EAAoB,KAAKA,CAAiB,IAAM,IAAA,CAAA,CAClD,CAAA,CAAA,CACD,EACAgB,EAAAA,KAAC,MAAA,CAAIF,UAAU,gCACbC,SAAA,CAAApB,EAAca,iBAAmB,EACjCQ,EAAAA,KAAC,IAAA,CAAED,SAAA,CAAA,oBAAkB+B,EAAYnD,EAAcoD,UAAU,CAAA,EAAE,EACxD,KACHzC,SACC,IAAA,CAAES,SAAA,CAAA,qBACiB,IAClB+B,EAAYnD,EAAcc,sBAAsB,CAAA,EAClD,EACG,IAAA,EACL,EACCZ,EACAmB,EAAAA,KAAC,MAAA,CAAIF,UAAU,gCACdC,SAAA,CAAAC,EAAAA,KAAC,IAAA,CAAED,SAAA,CAAA,eACWpB,EAAcG,cAAckD,UAAU,MAAI,IACtDrD,EAAcG,cAAcmD,MAAM,SAAA,CAAA,CACpC,EACCtD,EAAcG,cAAcoD,QAC5BlC,EAAAA,KAAC,IAAA,CAAEF,UAAU,WAAWC,SAAA,CAAA,YACbpB,EAAcG,cAAcoD,QAAQC,KAAA,EAC/C,EACG,IAAA,EACL,EACG,IAAA,CAAA,CACL,CAAA,EACD,CAAA,CACD,CAEF,CAAA"}
|