@epic-web/workshop-app 5.12.2 → 5.13.1
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-B5x3u5o0.js → _exerciseNumber-Bd4c2v-C.js} +2 -2
- package/build/client/assets/{_exerciseNumber-B5x3u5o0.js.map → _exerciseNumber-Bd4c2v-C.js.map} +1 -1
- package/build/client/assets/{_exerciseNumber_.finished-CWHBWFYP.js → _exerciseNumber_.finished-LvfViAKb.js} +2 -2
- package/build/client/assets/{_exerciseNumber_.finished-CWHBWFYP.js.map → _exerciseNumber_.finished-LvfViAKb.js.map} +1 -1
- package/build/client/assets/{_layout-D7PSWvDV.js → _layout-DhhhwkhJ.js} +2 -2
- package/build/client/assets/{_layout-D7PSWvDV.js.map → _layout-DhhhwkhJ.js.map} +1 -1
- package/build/client/assets/{_layout-B5GBAg5P.js → _layout-cO4Dvl1j.js} +2 -2
- package/build/client/assets/_layout-cO4Dvl1j.js.map +1 -0
- package/build/client/assets/{app-C2OvdXoM.js → app-DK_pEo6e.js} +2 -2
- package/build/client/assets/{app-C2OvdXoM.js.map → app-DK_pEo6e.js.map} +1 -1
- package/build/client/assets/{diff-BIJG7lM0.js → diff-BrUTVi14.js} +2 -2
- package/build/client/assets/diff-BrUTVi14.js.map +1 -0
- package/build/client/assets/{diff-BLwCINNG.js → diff-DI-wBaTF.js} +2 -2
- package/build/client/assets/{diff-BLwCINNG.js.map → diff-DI-wBaTF.js.map} +1 -1
- package/build/client/assets/{epic-video-BN9tH2d3.js → epic-video-DoUlMEIW.js} +2 -2
- package/build/client/assets/{epic-video-BN9tH2d3.js.map → epic-video-DoUlMEIW.js.map} +1 -1
- package/build/client/assets/{finished-DCXZj8Z6.js → finished-y90Pn48k.js} +2 -2
- package/build/client/assets/{finished-DCXZj8Z6.js.map → finished-y90Pn48k.js.map} +1 -1
- package/build/client/assets/index-BOO5UotZ.js +36 -0
- package/build/client/assets/index-BOO5UotZ.js.map +1 -0
- package/build/client/assets/{index-URcvFBYJ.js → index-CxEnb-w2.js} +2 -2
- package/build/client/assets/index-CxEnb-w2.js.map +1 -0
- package/build/client/assets/{index-I2Jm_PoG.js → index-De6oiBJI.js} +2 -2
- package/build/client/assets/{index-I2Jm_PoG.js.map → index-De6oiBJI.js.map} +1 -1
- package/build/client/assets/{loading-ZSC9wgHC.js → loading-D4V_nJZr.js} +2 -2
- package/build/client/assets/{loading-ZSC9wgHC.js.map → loading-D4V_nJZr.js.map} +1 -1
- package/build/client/assets/{login-RlImW-EE.js → login-DiEHnGfv.js} +2 -2
- package/build/client/assets/{login-RlImW-EE.js.map → login-DiEHnGfv.js.map} +1 -1
- package/build/client/assets/{manifest-022c9919.js → manifest-44a29362.js} +1 -1
- package/build/client/assets/{mdx-wFsONSmT.js → mdx-Ce3knRHx.js} +2 -2
- package/build/client/assets/{mdx-wFsONSmT.js.map → mdx-Ce3knRHx.js.map} +1 -1
- package/build/client/assets/{onboarding-y1OmFtYT.js → onboarding-Dnb9KzTs.js} +2 -2
- package/build/client/assets/{onboarding-y1OmFtYT.js.map → onboarding-Dnb9KzTs.js.map} +1 -1
- package/build/client/assets/{online-DZEJ9mQ4.js → online-BrcRwzQC.js} +2 -2
- package/build/client/assets/{online-DZEJ9mQ4.js.map → online-BrcRwzQC.js.map} +1 -1
- package/build/client/assets/{presence-BPdQqqxh.js → presence-Dmt5LCeW.js} +2 -2
- package/build/client/assets/{presence-BPdQqqxh.js.map → presence-Dmt5LCeW.js.map} +1 -1
- package/build/client/assets/{preview-DwJJV8IU.js → preview-BUkOZv9x.js} +2 -2
- package/build/client/assets/{preview-DwJJV8IU.js.map → preview-BUkOZv9x.js.map} +1 -1
- package/build/client/assets/{product-DD8nUALO.js → product-CleOH5Nn.js} +2 -2
- package/build/client/assets/{product-DD8nUALO.js.map → product-CleOH5Nn.js.map} +1 -1
- package/build/client/assets/{revalidation-ws-BIizeOeQ.js → revalidation-ws-CibzUlFP.js} +2 -2
- package/build/client/assets/{revalidation-ws-BIizeOeQ.js.map → revalidation-ws-CibzUlFP.js.map} +1 -1
- package/build/client/assets/{root-D2jqGwJl.js → root-BOerY_70.js} +3 -3
- package/build/client/assets/{root-D2jqGwJl.js.map → root-BOerY_70.js.map} +1 -1
- package/build/client/assets/{set-playground-BoPKA2de.js → set-playground-DBPp1Pek.js} +2 -2
- package/build/client/assets/set-playground-DBPp1Pek.js.map +1 -0
- package/build/client/assets/tailwind-Bcxzac75.css +1 -0
- package/build/client/assets/{test-BqhhxMae.js → test-DGBB06nI.js} +2 -2
- package/build/client/assets/{test-BqhhxMae.js.map → test-DGBB06nI.js.map} +1 -1
- package/build/client/assets/{tests-9K325XZ-.js → tests-B6AwzXv3.js} +2 -2
- package/build/client/assets/{tests-9K325XZ-.js.map → tests-B6AwzXv3.js.map} +1 -1
- package/build/server/index.js +8 -409
- package/build/server/index.js.map +1 -1
- package/package.json +3 -6
- package/build/client/assets/_layout-B5GBAg5P.js.map +0 -1
- package/build/client/assets/diff-BIJG7lM0.js.map +0 -1
- package/build/client/assets/index-Ba7zHVvt.js +0 -36
- package/build/client/assets/index-Ba7zHVvt.js.map +0 -1
- package/build/client/assets/index-URcvFBYJ.js.map +0 -1
- package/build/client/assets/set-playground-BoPKA2de.js.map +0 -1
- package/build/client/assets/tailwind-4klXLdJc.css +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as l,j as t,e as xe}from"./index-CGzylDPY.js";import{h as pe,u as G,n as ve,P,f as N,e as K,d as he,g as ge,S as je}from"./tooltip-CzrLrLJU.js";import{f as be,u as B}from"./index-D7-ne3iG.js";import{u as $,I as E,c as we}from"./misc-D9k1wGip.js";import{D as Ne}from"./diff-BLwCINNG.js";import{G as ye}from"./error-boundary-DBVB3BBH.js";import{L as Ce}from"./loading-ZSC9wgHC.js";import{D as Te,u as Pe}from"./discord-DsGCI_e6.js";import{u as Ee}from"./online-DZEJ9mQ4.js";import{u as V,A as Ie,L as H,e as Se}from"./components-DrvY4pal.js";import{J as De}from"./index-CB8bjE90.js";import{S as Re}from"./set-playground-BoPKA2de.js";import{P as Fe,a as _e}from"./tests-9K325XZ-.js";import{P as F}from"./preview-DwJJV8IU.js";import"./index-CWadM2q_.js";import"./accordion-BL_cX9y6.js";import"./mdx-wFsONSmT.js";import"./epic-video-BN9tH2d3.js";import"./index-Ba7zHVvt.js";import"./pe-DXT2FOp1.js";import"./user-C0j04V55.js";import"./workshop-config-oL_FWDKq.js";import"./progress-bar-IswLOt8e.js";import"./revalidation-ws-BIizeOeQ.js";import"./use-event-source-AZJtQsFX.js";import"./button-DhtjxLl5.js";function Ae(e,s=[]){let r=[];function n(c,o){const u=l.createContext(o),x=r.length;r=[...r,o];function f(d){const{scope:p,children:w,...m}=d,g=(p==null?void 0:p[e][x])||u,i=l.useMemo(()=>m,Object.values(m));return t.jsx(g.Provider,{value:i,children:w})}function v(d,p){const w=(p==null?void 0:p[e][x])||u,m=l.useContext(w);if(m)return m;if(o!==void 0)return o;throw new Error(`\`${d}\` must be used within \`${c}\``)}return f.displayName=c+"Provider",[f,v]}const a=()=>{const c=r.map(o=>l.createContext(o));return function(u){const x=(u==null?void 0:u[e])||c;return l.useMemo(()=>({[`__scope${e}`]:{...u,[e]:x}}),[u,x])}};return a.scopeName=e,[n,ke(a,...s)]}function ke(...e){const s=e[0];if(e.length===1)return s;const r=()=>{const n=e.map(a=>({useScope:a(),scopeName:a.scopeName}));return function(c){const o=n.reduce((u,{useScope:x,scopeName:f})=>{const d=x(c)[`__scope${f}`];return{...u,...d}},{});return l.useMemo(()=>({[`__scope${s.scopeName}`]:o}),[o])}};return r.scopeName=s.scopeName,r}var R="rovingFocusGroup.onEntryFocus",Le={bubbles:!1,cancelable:!0},I="RovingFocusGroup",[_,z,Ue]=be(I),[Oe,Y]=Ae(I,[Ue]),[Me,Ge]=Oe(I),W=l.forwardRef((e,s)=>t.jsx(_.Provider,{scope:e.__scopeRovingFocusGroup,children:t.jsx(_.Slot,{scope:e.__scopeRovingFocusGroup,children:t.jsx(Ke,{...e,ref:s})})}));W.displayName=I;var Ke=l.forwardRef((e,s)=>{const{__scopeRovingFocusGroup:r,orientation:n,loop:a=!1,dir:c,currentTabStopId:o,defaultCurrentTabStopId:u,onCurrentTabStopIdChange:x,onEntryFocus:f,preventScrollOnEntryFocus:v=!1,...d}=e,p=l.useRef(null),w=pe(s,p),m=B(c),[g=null,i]=G({prop:o,defaultProp:u,onChange:x}),[h,b]=l.useState(!1),C=ve(f),le=z(r),S=l.useRef(!1),[ce,U]=l.useState(0);return l.useEffect(()=>{const j=p.current;if(j)return j.addEventListener(R,C),()=>j.removeEventListener(R,C)},[C]),t.jsx(Me,{scope:r,orientation:n,dir:m,loop:a,currentTabStopId:g,onItemFocus:l.useCallback(j=>i(j),[i]),onItemShiftTab:l.useCallback(()=>b(!0),[]),onFocusableItemAdd:l.useCallback(()=>U(j=>j+1),[]),onFocusableItemRemove:l.useCallback(()=>U(j=>j-1),[]),children:t.jsx(P.div,{tabIndex:h||ce===0?-1:0,"data-orientation":n,...d,ref:w,style:{outline:"none",...e.style},onMouseDown:N(e.onMouseDown,()=>{S.current=!0}),onFocus:N(e.onFocus,j=>{const ue=!S.current;if(j.target===j.currentTarget&&ue&&!h){const O=new CustomEvent(R,Le);if(j.currentTarget.dispatchEvent(O),!O.defaultPrevented){const D=le().filter(y=>y.focusable),de=D.find(y=>y.active),fe=D.find(y=>y.id===g),me=[de,fe,...D].filter(Boolean).map(y=>y.ref.current);Q(me,v)}}S.current=!1}),onBlur:N(e.onBlur,()=>b(!1))})})}),J="RovingFocusGroupItem",q=l.forwardRef((e,s)=>{const{__scopeRovingFocusGroup:r,focusable:n=!0,active:a=!1,tabStopId:c,...o}=e,u=K(),x=c||u,f=Ge(J,r),v=f.currentTabStopId===x,d=z(r),{onFocusableItemAdd:p,onFocusableItemRemove:w}=f;return l.useEffect(()=>{if(n)return p(),()=>w()},[n,p,w]),t.jsx(_.ItemSlot,{scope:r,id:x,focusable:n,active:a,children:t.jsx(P.span,{tabIndex:v?0:-1,"data-orientation":f.orientation,...o,ref:s,onMouseDown:N(e.onMouseDown,m=>{n?f.onItemFocus(x):m.preventDefault()}),onFocus:N(e.onFocus,()=>f.onItemFocus(x)),onKeyDown:N(e.onKeyDown,m=>{if(m.key==="Tab"&&m.shiftKey){f.onItemShiftTab();return}if(m.target!==m.currentTarget)return;const g=Ve(m,f.orientation,f.dir);if(g!==void 0){if(m.metaKey||m.ctrlKey||m.altKey||m.shiftKey)return;m.preventDefault();let h=d().filter(b=>b.focusable).map(b=>b.ref.current);if(g==="last")h.reverse();else if(g==="prev"||g==="next"){g==="prev"&&h.reverse();const b=h.indexOf(m.currentTarget);h=f.loop?He(h,b+1):h.slice(b+1)}setTimeout(()=>Q(h))}})})})});q.displayName=J;var Be={ArrowLeft:"prev",ArrowUp:"prev",ArrowRight:"next",ArrowDown:"next",PageUp:"first",Home:"first",PageDown:"last",End:"last"};function $e(e,s){return s!=="rtl"?e:e==="ArrowLeft"?"ArrowRight":e==="ArrowRight"?"ArrowLeft":e}function Ve(e,s,r){const n=$e(e.key,r);if(!(s==="vertical"&&["ArrowLeft","ArrowRight"].includes(n))&&!(s==="horizontal"&&["ArrowUp","ArrowDown"].includes(n)))return Be[n]}function Q(e,s=!1){const r=document.activeElement;for(const n of e)if(n===r||(n.focus({preventScroll:s}),document.activeElement!==r))return}function He(e,s){return e.map((r,n)=>e[(s+n)%e.length])}var ze=W,Ye=q,k="Tabs",[We,Ft]=he(k,[Y]),X=Y(),[Je,L]=We(k),Z=l.forwardRef((e,s)=>{const{__scopeTabs:r,value:n,onValueChange:a,defaultValue:c,orientation:o="horizontal",dir:u,activationMode:x="automatic",...f}=e,v=B(u),[d,p]=G({prop:n,onChange:a,defaultProp:c});return t.jsx(Je,{scope:r,baseId:K(),value:d,onValueChange:p,orientation:o,dir:v,activationMode:x,children:t.jsx(P.div,{dir:v,"data-orientation":o,...f,ref:s})})});Z.displayName=k;var ee="TabsList",te=l.forwardRef((e,s)=>{const{__scopeTabs:r,loop:n=!0,...a}=e,c=L(ee,r),o=X(r);return t.jsx(ze,{asChild:!0,...o,orientation:c.orientation,dir:c.dir,loop:n,children:t.jsx(P.div,{role:"tablist","aria-orientation":c.orientation,...a,ref:s})})});te.displayName=ee;var re="TabsTrigger",se=l.forwardRef((e,s)=>{const{__scopeTabs:r,value:n,disabled:a=!1,...c}=e,o=L(re,r),u=X(r),x=ae(o.baseId,n),f=ie(o.baseId,n),v=n===o.value;return t.jsx(Ye,{asChild:!0,...u,focusable:!a,active:v,children:t.jsx(P.button,{type:"button",role:"tab","aria-selected":v,"aria-controls":f,"data-state":v?"active":"inactive","data-disabled":a?"":void 0,disabled:a,id:x,...c,ref:s,onMouseDown:N(e.onMouseDown,d=>{!a&&d.button===0&&d.ctrlKey===!1?o.onValueChange(n):d.preventDefault()}),onKeyDown:N(e.onKeyDown,d=>{[" ","Enter"].includes(d.key)&&o.onValueChange(n)}),onFocus:N(e.onFocus,()=>{const d=o.activationMode!=="manual";!v&&!a&&d&&o.onValueChange(n)})})})});se.displayName=re;var ne="TabsContent",oe=l.forwardRef((e,s)=>{const{__scopeTabs:r,value:n,forceMount:a,children:c,...o}=e,u=L(ne,r),x=ae(u.baseId,n),f=ie(u.baseId,n),v=n===u.value,d=l.useRef(v);return l.useEffect(()=>{const p=requestAnimationFrame(()=>d.current=!1);return()=>cancelAnimationFrame(p)},[]),t.jsx(ge,{present:a||v,children:({present:p})=>t.jsx(P.div,{"data-state":v?"active":"inactive","data-orientation":u.orientation,role:"tabpanel","aria-labelledby":x,hidden:!p,id:f,tabIndex:0,...o,ref:s,style:{...e.style,animationDuration:d.current?"0s":void 0},children:p&&c})})});oe.displayName=ne;function ae(e,s){return`${e}-trigger-${s}`}function ie(e,s){return`${e}-content-${s}`}var qe=Z,Qe=te,Xe=se,T=oe;function Ze(){return t.jsxs("div",{className:"flex h-full w-full flex-col gap-4 pt-4",children:[t.jsx("div",{className:"text-center",children:t.jsx(Te,{})}),t.jsx("div",{className:"flex-1 overflow-y-scroll bg-accent pb-4 scrollbar-thin scrollbar-thumb-scrollbar",children:t.jsx(et,{})})]})}function et(){const e=V(),s=Pe(),r=$();return Ee()?t.jsxs("div",{className:"flex h-full flex-col items-center justify-between",children:[t.jsx(l.Suspense,{fallback:t.jsx("div",{className:"flex h-full w-full flex-col items-center justify-center",children:t.jsx(Ce,{children:"Loading Discord Posts"})}),children:t.jsx(Ie,{resolve:e.discordPostsPromise,errorElement:t.jsx("div",{className:"text-red-500",children:"There was a problem loading the discord posts"}),children:a=>t.jsx("ul",{className:"flex w-full flex-col gap-4 p-3 xl:p-12",children:a.map(c=>t.jsx("li",{className:"rounded-xl border bg-background transition-all duration-200 focus-within:-translate-y-1 focus-within:shadow-lg hover:-translate-y-1 hover:shadow-lg",children:t.jsx(tt,{thread:c})},c.id))})})}),t.jsx("div",{children:t.jsxs(H,{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 ",t.jsx(E,{name:"ExternalLink"})]})})]}):t.jsx("div",{className:"flex h-full flex-col items-center justify-between",children:t.jsx("div",{className:"text-foreground-destructive flex h-full w-full flex-col items-center justify-center",children:t.jsx(E,{name:"WifiNoConnection",size:"xl",children:"Unable to load discord messages when offline"})})})}function tt({thread:e}){const s=e.reactions.filter(r=>r.count);return t.jsx("div",{children:t.jsxs("div",{className:"flex flex-col gap-2 p-4",children:[t.jsxs("div",{className:"flex gap-4",children:[t.jsxs("div",{className:"flex flex-col gap-1",children:[e.tags.length?t.jsx("div",{className:"flex gap-2",children:e.tags.map(r=>t.jsxs("div",{className:"flex items-center justify-center gap-1 rounded-full bg-accent px-2 py-1 text-sm",children:[t.jsx("span",{className:"h-3 w-3 leading-3",children:t.jsx(M,{name:r.emojiName,url:r.emojiUrl})}),t.jsx("span",{children:r.name})]},r.name))}):null,t.jsx("strong",{className:"text-xl font-bold",children:e.name}),t.jsxs("div",{className:"flex items-start gap-1",children:[t.jsxs("div",{className:"flex items-center gap-1",children:[e.authorAvatarUrl?t.jsx("img",{src:e.authorAvatarUrl,alt:"",className:"h-6 w-6 rounded-full"}):null,t.jsxs("span",{children:[t.jsx("span",{className:"font-bold",style:e.authorHexAccentColor?{color:e.authorHexAccentColor}:{},children:e.authorDisplayName}),":"," "]})]}),t.jsx("span",{className:"flex-1 overflow-ellipsis text-muted-foreground",children:e.messagePreview})]})]}),e.previewImageUrl?t.jsx("img",{src:e.previewImageUrl,alt:"",className:"h-28 w-28 rounded-lg object-cover"}):null]}),t.jsxs("div",{className:"flex justify-between",children:[t.jsxs("div",{className:"flex items-center gap-3",children:[t.jsx("span",{children:s.length?t.jsx("ul",{className:"flex items-center gap-2",children:s.map((r,n)=>t.jsxs("li",{className:"flex items-center gap-1 rounded-md border border-blue-600 bg-blue-500/20 px-[5px] py-[0.5px] text-sm",children:[t.jsx("span",{className:"h-3 w-3 leading-3",children:t.jsx(M,{name:r.emojiName,url:r.emojiUrl})}),t.jsx("span",{children:r.count})]},n))}):null}),t.jsxs("span",{className:"flex items-center gap-1",children:[t.jsxs("span",{className:"inline-flex items-center gap-1",children:[t.jsx(E,{name:"Chat"})," ",e.messageCount]}),` · ${e.lastUpdatedDisplay}`]})]}),t.jsxs("span",{className:"flex items-center gap-4",children:[t.jsx("a",{href:e.link.replace(/^https/,"discord"),children:t.jsx(E,{name:"Discord"})}),t.jsx("a",{href:e.link,target:"_blank",rel:"noreferrer noopener",children:t.jsx(E,{name:"ExternalLink"})})]})]})]})})}function M({name:e,url:s}){return s?t.jsx("img",{src:s,alt:e,className:"h-full w-full"}):e||null}function rt({appInfo:e,inBrowserBrowserRef:s,problemAppName:r,allApps:n,isUpToDate:a}){return t.jsx(Fe,{playgroundAppName:e==null?void 0:e.appName,problemAppName:r,allApps:n,isUpToDate:a,children:(e==null?void 0:e.dev.type)==="none"?t.jsxs("div",{children:[t.jsx("div",{className:"text-foreground-secondary flex h-full items-center justify-center text-2xl",children:"Non-UI playground"}),t.jsx("div",{children:t.jsxs("div",{className:"text-foreground-secondary flex flex-wrap gap-1 text-center",children:["Navigate to"," ",t.jsx(je,{content:e.fullPath,children:t.jsx("span",{className:"underline",onClick:()=>{navigator.clipboard.writeText(e.fullPath),De.success("Copied playground path to clipboard")},children:"the playground directory"})})," ","in your editor and terminal to work on this exercise!"]})})]}):e?t.jsx(F,{id:e.appName,appInfo:e,inBrowserBrowserRef:s}):t.jsxs("div",{className:"flex flex-col justify-center gap-2",children:[t.jsx("p",{children:"Please set the playground first"}),r?t.jsx(Re,{appName:r}):null]})})}const A=["playground","problem","solution","tests","diff","chat"],st=e=>!!(e&&A.includes(e));function nt(e,s,r){const n=new URLSearchParams(e);return r===null?n.delete(s):n.set(s,r),n}function _t(){var v,d,p,w,m,g;const e=V(),[s]=Se(),r=s.get("preview"),n=l.useRef(null),a=$(),c=xe();function o(i){var h,b,C;if(i==="tests")return ENV.EPICSHOP_DEPLOYED||!e.playground||e.playground.test.type==="none";if(i==="problem"||i==="solution"){if(((h=e[i])==null?void 0:h.dev.type)==="none")return!0;if(ENV.EPICSHOP_DEPLOYED)return((b=e[i])==null?void 0:b.dev.type)!=="browser"&&!((C=e[i])!=null&&C.stackBlitzUrl)}return!!(i==="playground"&&ENV.EPICSHOP_DEPLOYED)}const u=st(r)?r:A.find(i=>!o(i)),x=`/diff?${new URLSearchParams({app1:((v=e.problem)==null?void 0:v.name)??"",app2:((d=e.solution)==null?void 0:d.name)??""})}`;function f(i){i.altKey&&!i.ctrlKey&&!i.shiftKey&&!i.metaKey&&(i.preventDefault(),c(x))}return t.jsxs(qe,{className:"relative flex flex-col overflow-y-auto sm:col-span-1 sm:row-span-1",value:u,children:[t.jsx(Qe,{className:"h-14 min-h-14 overflow-x-hidden border-b scrollbar-thin scrollbar-thumb-scrollbar",children:A.map(i=>{const h=o(i);return t.jsx(Xe,{value:i,hidden:h,asChild:!0,children:t.jsx(H,{id:`${i}-tab`,className:we("clip-path-button relative h-full px-6 py-4 font-mono text-sm uppercase outline-none 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",h?"hidden":"inline-block"),preventScrollReset:!0,prefetch:"intent",onClick:f,to:i==="diff"&&a?x:`?${nt(s,"preview",i==="playground"?null:i)}`,children:i})},i)})}),t.jsxs("div",{className:"relative z-10 flex min-h-96 flex-grow flex-col overflow-y-auto",children:[t.jsx(T,{value:"playground",className:"flex w-full flex-grow items-center justify-center self-start radix-state-inactive:hidden",children:t.jsx(rt,{appInfo:e.playground,problemAppName:(p=e.problem)==null?void 0:p.name,inBrowserBrowserRef:n,allApps:e.allApps,isUpToDate:((w=e.playground)==null?void 0:w.isUpToDate)??!1})}),t.jsx(T,{value:"problem",className:"flex w-full flex-grow items-center justify-center self-start radix-state-inactive:hidden",children:t.jsx(F,{appInfo:e.problem,inBrowserBrowserRef:n})}),t.jsx(T,{value:"solution",className:"flex w-full flex-grow items-center justify-center self-start radix-state-inactive:hidden",children:t.jsx(F,{appInfo:e.solution,inBrowserBrowserRef:n})}),t.jsx(T,{value:"tests",className:"flex w-full flex-grow items-start justify-center self-start overflow-hidden radix-state-inactive:hidden",children:t.jsx(_e,{appInfo:e.playground,problemAppName:(m=e.problem)==null?void 0:m.name,allApps:e.allApps,isUpToDate:((g=e.playground)==null?void 0:g.isUpToDate)??!1})}),t.jsx(T,{value:"diff",className:"flex h-full w-full flex-grow items-start justify-center self-start radix-state-inactive:hidden",children:t.jsx(Ne,{diff:e.diff,allApps:e.allApps})}),t.jsx(T,{value:"chat",className:"flex h-full w-full flex-grow items-start justify-center self-start radix-state-inactive:hidden",children:t.jsx(Ze,{})})]})]})}function At(){return t.jsx(ye,{statusHandlers:{404:()=>t.jsx("p",{children:"Sorry, we couldn't find an app here."})}})}export{At as ErrorBoundary,_t as default};
|
|
2
|
-
//# sourceMappingURL=index-
|
|
1
|
+
import{r as l,j as t,e as xe}from"./index-CGzylDPY.js";import{h as pe,u as G,n as ve,P,f as N,e as K,d as he,g as ge,S as je}from"./tooltip-CzrLrLJU.js";import{f as be,u as B}from"./index-D7-ne3iG.js";import{u as $,I as E,c as we}from"./misc-D9k1wGip.js";import{D as Ne}from"./diff-DI-wBaTF.js";import{G as ye}from"./error-boundary-DBVB3BBH.js";import{L as Ce}from"./loading-D4V_nJZr.js";import{D as Te,u as Pe}from"./discord-DsGCI_e6.js";import{u as Ee}from"./online-BrcRwzQC.js";import{u as V,A as Ie,L as H,e as Se}from"./components-DrvY4pal.js";import{J as De}from"./index-CB8bjE90.js";import{S as Re}from"./set-playground-DBPp1Pek.js";import{P as Fe,a as _e}from"./tests-B6AwzXv3.js";import{P as F}from"./preview-BUkOZv9x.js";import"./index-CWadM2q_.js";import"./accordion-BL_cX9y6.js";import"./mdx-Ce3knRHx.js";import"./epic-video-DoUlMEIW.js";import"./index-BOO5UotZ.js";import"./pe-DXT2FOp1.js";import"./user-C0j04V55.js";import"./workshop-config-oL_FWDKq.js";import"./progress-bar-IswLOt8e.js";import"./revalidation-ws-CibzUlFP.js";import"./use-event-source-AZJtQsFX.js";import"./button-DhtjxLl5.js";function Ae(e,s=[]){let r=[];function n(c,o){const u=l.createContext(o),x=r.length;r=[...r,o];function f(d){const{scope:p,children:w,...m}=d,g=(p==null?void 0:p[e][x])||u,i=l.useMemo(()=>m,Object.values(m));return t.jsx(g.Provider,{value:i,children:w})}function v(d,p){const w=(p==null?void 0:p[e][x])||u,m=l.useContext(w);if(m)return m;if(o!==void 0)return o;throw new Error(`\`${d}\` must be used within \`${c}\``)}return f.displayName=c+"Provider",[f,v]}const a=()=>{const c=r.map(o=>l.createContext(o));return function(u){const x=(u==null?void 0:u[e])||c;return l.useMemo(()=>({[`__scope${e}`]:{...u,[e]:x}}),[u,x])}};return a.scopeName=e,[n,ke(a,...s)]}function ke(...e){const s=e[0];if(e.length===1)return s;const r=()=>{const n=e.map(a=>({useScope:a(),scopeName:a.scopeName}));return function(c){const o=n.reduce((u,{useScope:x,scopeName:f})=>{const d=x(c)[`__scope${f}`];return{...u,...d}},{});return l.useMemo(()=>({[`__scope${s.scopeName}`]:o}),[o])}};return r.scopeName=s.scopeName,r}var R="rovingFocusGroup.onEntryFocus",Le={bubbles:!1,cancelable:!0},I="RovingFocusGroup",[_,z,Ue]=be(I),[Oe,Y]=Ae(I,[Ue]),[Me,Ge]=Oe(I),W=l.forwardRef((e,s)=>t.jsx(_.Provider,{scope:e.__scopeRovingFocusGroup,children:t.jsx(_.Slot,{scope:e.__scopeRovingFocusGroup,children:t.jsx(Ke,{...e,ref:s})})}));W.displayName=I;var Ke=l.forwardRef((e,s)=>{const{__scopeRovingFocusGroup:r,orientation:n,loop:a=!1,dir:c,currentTabStopId:o,defaultCurrentTabStopId:u,onCurrentTabStopIdChange:x,onEntryFocus:f,preventScrollOnEntryFocus:v=!1,...d}=e,p=l.useRef(null),w=pe(s,p),m=B(c),[g=null,i]=G({prop:o,defaultProp:u,onChange:x}),[h,b]=l.useState(!1),C=ve(f),le=z(r),S=l.useRef(!1),[ce,U]=l.useState(0);return l.useEffect(()=>{const j=p.current;if(j)return j.addEventListener(R,C),()=>j.removeEventListener(R,C)},[C]),t.jsx(Me,{scope:r,orientation:n,dir:m,loop:a,currentTabStopId:g,onItemFocus:l.useCallback(j=>i(j),[i]),onItemShiftTab:l.useCallback(()=>b(!0),[]),onFocusableItemAdd:l.useCallback(()=>U(j=>j+1),[]),onFocusableItemRemove:l.useCallback(()=>U(j=>j-1),[]),children:t.jsx(P.div,{tabIndex:h||ce===0?-1:0,"data-orientation":n,...d,ref:w,style:{outline:"none",...e.style},onMouseDown:N(e.onMouseDown,()=>{S.current=!0}),onFocus:N(e.onFocus,j=>{const ue=!S.current;if(j.target===j.currentTarget&&ue&&!h){const O=new CustomEvent(R,Le);if(j.currentTarget.dispatchEvent(O),!O.defaultPrevented){const D=le().filter(y=>y.focusable),de=D.find(y=>y.active),fe=D.find(y=>y.id===g),me=[de,fe,...D].filter(Boolean).map(y=>y.ref.current);Q(me,v)}}S.current=!1}),onBlur:N(e.onBlur,()=>b(!1))})})}),J="RovingFocusGroupItem",q=l.forwardRef((e,s)=>{const{__scopeRovingFocusGroup:r,focusable:n=!0,active:a=!1,tabStopId:c,...o}=e,u=K(),x=c||u,f=Ge(J,r),v=f.currentTabStopId===x,d=z(r),{onFocusableItemAdd:p,onFocusableItemRemove:w}=f;return l.useEffect(()=>{if(n)return p(),()=>w()},[n,p,w]),t.jsx(_.ItemSlot,{scope:r,id:x,focusable:n,active:a,children:t.jsx(P.span,{tabIndex:v?0:-1,"data-orientation":f.orientation,...o,ref:s,onMouseDown:N(e.onMouseDown,m=>{n?f.onItemFocus(x):m.preventDefault()}),onFocus:N(e.onFocus,()=>f.onItemFocus(x)),onKeyDown:N(e.onKeyDown,m=>{if(m.key==="Tab"&&m.shiftKey){f.onItemShiftTab();return}if(m.target!==m.currentTarget)return;const g=Ve(m,f.orientation,f.dir);if(g!==void 0){if(m.metaKey||m.ctrlKey||m.altKey||m.shiftKey)return;m.preventDefault();let h=d().filter(b=>b.focusable).map(b=>b.ref.current);if(g==="last")h.reverse();else if(g==="prev"||g==="next"){g==="prev"&&h.reverse();const b=h.indexOf(m.currentTarget);h=f.loop?He(h,b+1):h.slice(b+1)}setTimeout(()=>Q(h))}})})})});q.displayName=J;var Be={ArrowLeft:"prev",ArrowUp:"prev",ArrowRight:"next",ArrowDown:"next",PageUp:"first",Home:"first",PageDown:"last",End:"last"};function $e(e,s){return s!=="rtl"?e:e==="ArrowLeft"?"ArrowRight":e==="ArrowRight"?"ArrowLeft":e}function Ve(e,s,r){const n=$e(e.key,r);if(!(s==="vertical"&&["ArrowLeft","ArrowRight"].includes(n))&&!(s==="horizontal"&&["ArrowUp","ArrowDown"].includes(n)))return Be[n]}function Q(e,s=!1){const r=document.activeElement;for(const n of e)if(n===r||(n.focus({preventScroll:s}),document.activeElement!==r))return}function He(e,s){return e.map((r,n)=>e[(s+n)%e.length])}var ze=W,Ye=q,k="Tabs",[We,Ft]=he(k,[Y]),X=Y(),[Je,L]=We(k),Z=l.forwardRef((e,s)=>{const{__scopeTabs:r,value:n,onValueChange:a,defaultValue:c,orientation:o="horizontal",dir:u,activationMode:x="automatic",...f}=e,v=B(u),[d,p]=G({prop:n,onChange:a,defaultProp:c});return t.jsx(Je,{scope:r,baseId:K(),value:d,onValueChange:p,orientation:o,dir:v,activationMode:x,children:t.jsx(P.div,{dir:v,"data-orientation":o,...f,ref:s})})});Z.displayName=k;var ee="TabsList",te=l.forwardRef((e,s)=>{const{__scopeTabs:r,loop:n=!0,...a}=e,c=L(ee,r),o=X(r);return t.jsx(ze,{asChild:!0,...o,orientation:c.orientation,dir:c.dir,loop:n,children:t.jsx(P.div,{role:"tablist","aria-orientation":c.orientation,...a,ref:s})})});te.displayName=ee;var re="TabsTrigger",se=l.forwardRef((e,s)=>{const{__scopeTabs:r,value:n,disabled:a=!1,...c}=e,o=L(re,r),u=X(r),x=ae(o.baseId,n),f=ie(o.baseId,n),v=n===o.value;return t.jsx(Ye,{asChild:!0,...u,focusable:!a,active:v,children:t.jsx(P.button,{type:"button",role:"tab","aria-selected":v,"aria-controls":f,"data-state":v?"active":"inactive","data-disabled":a?"":void 0,disabled:a,id:x,...c,ref:s,onMouseDown:N(e.onMouseDown,d=>{!a&&d.button===0&&d.ctrlKey===!1?o.onValueChange(n):d.preventDefault()}),onKeyDown:N(e.onKeyDown,d=>{[" ","Enter"].includes(d.key)&&o.onValueChange(n)}),onFocus:N(e.onFocus,()=>{const d=o.activationMode!=="manual";!v&&!a&&d&&o.onValueChange(n)})})})});se.displayName=re;var ne="TabsContent",oe=l.forwardRef((e,s)=>{const{__scopeTabs:r,value:n,forceMount:a,children:c,...o}=e,u=L(ne,r),x=ae(u.baseId,n),f=ie(u.baseId,n),v=n===u.value,d=l.useRef(v);return l.useEffect(()=>{const p=requestAnimationFrame(()=>d.current=!1);return()=>cancelAnimationFrame(p)},[]),t.jsx(ge,{present:a||v,children:({present:p})=>t.jsx(P.div,{"data-state":v?"active":"inactive","data-orientation":u.orientation,role:"tabpanel","aria-labelledby":x,hidden:!p,id:f,tabIndex:0,...o,ref:s,style:{...e.style,animationDuration:d.current?"0s":void 0},children:p&&c})})});oe.displayName=ne;function ae(e,s){return`${e}-trigger-${s}`}function ie(e,s){return`${e}-content-${s}`}var qe=Z,Qe=te,Xe=se,T=oe;function Ze(){return t.jsxs("div",{className:"flex h-full w-full flex-col gap-4 pt-4",children:[t.jsx("div",{className:"text-center",children:t.jsx(Te,{})}),t.jsx("div",{className:"flex-1 overflow-y-scroll bg-accent pb-4 scrollbar-thin scrollbar-thumb-scrollbar",children:t.jsx(et,{})})]})}function et(){const e=V(),s=Pe(),r=$();return Ee()?t.jsxs("div",{className:"flex h-full flex-col items-center justify-between",children:[t.jsx(l.Suspense,{fallback:t.jsx("div",{className:"flex h-full w-full flex-col items-center justify-center",children:t.jsx(Ce,{children:"Loading Discord Posts"})}),children:t.jsx(Ie,{resolve:e.discordPostsPromise,errorElement:t.jsx("div",{className:"text-red-500",children:"There was a problem loading the discord posts"}),children:a=>t.jsx("ul",{className:"flex w-full flex-col gap-4 p-3 xl:p-12",children:a.map(c=>t.jsx("li",{className:"rounded-xl border bg-background transition-all duration-200 focus-within:-translate-y-1 focus-within:shadow-lg hover:-translate-y-1 hover:shadow-lg",children:t.jsx(tt,{thread:c})},c.id))})})}),t.jsx("div",{children:t.jsxs(H,{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 ",t.jsx(E,{name:"ExternalLink"})]})})]}):t.jsx("div",{className:"flex h-full flex-col items-center justify-between",children:t.jsx("div",{className:"text-foreground-destructive flex h-full w-full flex-col items-center justify-center",children:t.jsx(E,{name:"WifiNoConnection",size:"xl",children:"Unable to load discord messages when offline"})})})}function tt({thread:e}){const s=e.reactions.filter(r=>r.count);return t.jsx("div",{children:t.jsxs("div",{className:"flex flex-col gap-2 p-4",children:[t.jsxs("div",{className:"flex gap-4",children:[t.jsxs("div",{className:"flex flex-col gap-1",children:[e.tags.length?t.jsx("div",{className:"flex gap-2",children:e.tags.map(r=>t.jsxs("div",{className:"flex items-center justify-center gap-1 rounded-full bg-accent px-2 py-1 text-sm",children:[t.jsx("span",{className:"h-3 w-3 leading-3",children:t.jsx(M,{name:r.emojiName,url:r.emojiUrl})}),t.jsx("span",{children:r.name})]},r.name))}):null,t.jsx("strong",{className:"text-xl font-bold",children:e.name}),t.jsxs("div",{className:"flex items-start gap-1",children:[t.jsxs("div",{className:"flex items-center gap-1",children:[e.authorAvatarUrl?t.jsx("img",{src:e.authorAvatarUrl,alt:"",className:"h-6 w-6 rounded-full"}):null,t.jsxs("span",{children:[t.jsx("span",{className:"font-bold",style:e.authorHexAccentColor?{color:e.authorHexAccentColor}:{},children:e.authorDisplayName}),":"," "]})]}),t.jsx("span",{className:"flex-1 overflow-ellipsis text-muted-foreground",children:e.messagePreview})]})]}),e.previewImageUrl?t.jsx("img",{src:e.previewImageUrl,alt:"",className:"h-28 w-28 rounded-lg object-cover"}):null]}),t.jsxs("div",{className:"flex justify-between",children:[t.jsxs("div",{className:"flex items-center gap-3",children:[t.jsx("span",{children:s.length?t.jsx("ul",{className:"flex items-center gap-2",children:s.map((r,n)=>t.jsxs("li",{className:"flex items-center gap-1 rounded-md border border-blue-600 bg-blue-500/20 px-[5px] py-[0.5px] text-sm",children:[t.jsx("span",{className:"h-3 w-3 leading-3",children:t.jsx(M,{name:r.emojiName,url:r.emojiUrl})}),t.jsx("span",{children:r.count})]},n))}):null}),t.jsxs("span",{className:"flex items-center gap-1",children:[t.jsxs("span",{className:"inline-flex items-center gap-1",children:[t.jsx(E,{name:"Chat"})," ",e.messageCount]}),` · ${e.lastUpdatedDisplay}`]})]}),t.jsxs("span",{className:"flex items-center gap-4",children:[t.jsx("a",{href:e.link.replace(/^https/,"discord"),children:t.jsx(E,{name:"Discord"})}),t.jsx("a",{href:e.link,target:"_blank",rel:"noreferrer noopener",children:t.jsx(E,{name:"ExternalLink"})})]})]})]})})}function M({name:e,url:s}){return s?t.jsx("img",{src:s,alt:e,className:"h-full w-full"}):e||null}function rt({appInfo:e,inBrowserBrowserRef:s,problemAppName:r,allApps:n,isUpToDate:a}){return t.jsx(Fe,{playgroundAppName:e==null?void 0:e.appName,problemAppName:r,allApps:n,isUpToDate:a,children:(e==null?void 0:e.dev.type)==="none"?t.jsxs("div",{children:[t.jsx("div",{className:"text-foreground-secondary flex h-full items-center justify-center text-2xl",children:"Non-UI playground"}),t.jsx("div",{children:t.jsxs("div",{className:"text-foreground-secondary flex flex-wrap gap-1 text-center",children:["Navigate to"," ",t.jsx(je,{content:e.fullPath,children:t.jsx("span",{className:"underline",onClick:()=>{navigator.clipboard.writeText(e.fullPath),De.success("Copied playground path to clipboard")},children:"the playground directory"})})," ","in your editor and terminal to work on this exercise!"]})})]}):e?t.jsx(F,{id:e.appName,appInfo:e,inBrowserBrowserRef:s}):t.jsxs("div",{className:"flex flex-col justify-center gap-2",children:[t.jsx("p",{children:"Please set the playground first"}),r?t.jsx(Re,{appName:r}):null]})})}const A=["playground","problem","solution","tests","diff","chat"],st=e=>!!(e&&A.includes(e));function nt(e,s,r){const n=new URLSearchParams(e);return r===null?n.delete(s):n.set(s,r),n}function _t(){var v,d,p,w,m,g;const e=V(),[s]=Se(),r=s.get("preview"),n=l.useRef(null),a=$(),c=xe();function o(i){var h,b,C;if(i==="tests")return ENV.EPICSHOP_DEPLOYED||!e.playground||e.playground.test.type==="none";if(i==="problem"||i==="solution"){if(((h=e[i])==null?void 0:h.dev.type)==="none")return!0;if(ENV.EPICSHOP_DEPLOYED)return((b=e[i])==null?void 0:b.dev.type)!=="browser"&&!((C=e[i])!=null&&C.stackBlitzUrl)}return!!(i==="playground"&&ENV.EPICSHOP_DEPLOYED)}const u=st(r)?r:A.find(i=>!o(i)),x=`/diff?${new URLSearchParams({app1:((v=e.problem)==null?void 0:v.name)??"",app2:((d=e.solution)==null?void 0:d.name)??""})}`;function f(i){i.altKey&&!i.ctrlKey&&!i.shiftKey&&!i.metaKey&&(i.preventDefault(),c(x))}return t.jsxs(qe,{className:"relative flex flex-col overflow-y-auto sm:col-span-1 sm:row-span-1",value:u,children:[t.jsx(Qe,{className:"h-14 min-h-14 overflow-x-hidden border-b scrollbar-thin scrollbar-thumb-scrollbar",children:A.map(i=>{const h=o(i);return t.jsx(Xe,{value:i,hidden:h,asChild:!0,children:t.jsx(H,{id:`${i}-tab`,className:we("clip-path-button relative h-full px-6 py-4 font-mono text-sm uppercase outline-none 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",h?"hidden":"inline-block"),preventScrollReset:!0,prefetch:"intent",onClick:f,to:i==="diff"&&a?x:`?${nt(s,"preview",i==="playground"?null:i)}`,children:i})},i)})}),t.jsxs("div",{className:"relative z-10 flex min-h-96 flex-grow flex-col overflow-y-auto",children:[t.jsx(T,{value:"playground",className:"flex w-full flex-grow items-center justify-center self-start radix-state-inactive:hidden",children:t.jsx(rt,{appInfo:e.playground,problemAppName:(p=e.problem)==null?void 0:p.name,inBrowserBrowserRef:n,allApps:e.allApps,isUpToDate:((w=e.playground)==null?void 0:w.isUpToDate)??!1})}),t.jsx(T,{value:"problem",className:"flex w-full flex-grow items-center justify-center self-start radix-state-inactive:hidden",children:t.jsx(F,{appInfo:e.problem,inBrowserBrowserRef:n})}),t.jsx(T,{value:"solution",className:"flex w-full flex-grow items-center justify-center self-start radix-state-inactive:hidden",children:t.jsx(F,{appInfo:e.solution,inBrowserBrowserRef:n})}),t.jsx(T,{value:"tests",className:"flex w-full flex-grow items-start justify-center self-start overflow-hidden radix-state-inactive:hidden",children:t.jsx(_e,{appInfo:e.playground,problemAppName:(m=e.problem)==null?void 0:m.name,allApps:e.allApps,isUpToDate:((g=e.playground)==null?void 0:g.isUpToDate)??!1})}),t.jsx(T,{value:"diff",className:"flex h-full w-full flex-grow items-start justify-center self-start radix-state-inactive:hidden",children:t.jsx(Ne,{diff:e.diff,allApps:e.allApps})}),t.jsx(T,{value:"chat",className:"flex h-full w-full flex-grow items-start justify-center self-start radix-state-inactive:hidden",children:t.jsx(Ze,{})})]})]})}function At(){return t.jsx(ye,{statusHandlers:{404:()=>t.jsx("p",{children:"Sorry, we couldn't find an app here."})}})}export{At as ErrorBoundary,_t as default};
|
|
2
|
+
//# sourceMappingURL=index-CxEnb-w2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-CxEnb-w2.js","sources":["../../../../../node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-context/dist/index.mjs","../../../../../node_modules/@radix-ui/react-roving-focus/dist/index.mjs","../../../../../node_modules/@radix-ui/react-tabs/dist/index.mjs","../../../app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/__shared/discord.tsx","../../../app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/__shared/playground.tsx","../../../app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/index.tsx"],"sourcesContent":["// packages/react/context/src/createContext.tsx\nimport * as React from \"react\";\nimport { jsx } from \"react/jsx-runtime\";\nfunction createContext2(rootComponentName, defaultContext) {\n const Context = React.createContext(defaultContext);\n function Provider(props) {\n const { children, ...context } = props;\n const value = React.useMemo(() => context, Object.values(context));\n return /* @__PURE__ */ jsx(Context.Provider, { value, children });\n }\n function useContext2(consumerName) {\n const context = React.useContext(Context);\n if (context) return context;\n if (defaultContext !== void 0) return defaultContext;\n throw new Error(`\\`${consumerName}\\` must be used within \\`${rootComponentName}\\``);\n }\n Provider.displayName = rootComponentName + \"Provider\";\n return [Provider, useContext2];\n}\nfunction createContextScope(scopeName, createContextScopeDeps = []) {\n let defaultContexts = [];\n function createContext3(rootComponentName, defaultContext) {\n const BaseContext = React.createContext(defaultContext);\n const index = defaultContexts.length;\n defaultContexts = [...defaultContexts, defaultContext];\n function Provider(props) {\n const { scope, children, ...context } = props;\n const Context = scope?.[scopeName][index] || BaseContext;\n const value = React.useMemo(() => context, Object.values(context));\n return /* @__PURE__ */ jsx(Context.Provider, { value, children });\n }\n function useContext2(consumerName, scope) {\n const Context = scope?.[scopeName][index] || BaseContext;\n const context = React.useContext(Context);\n if (context) return context;\n if (defaultContext !== void 0) return defaultContext;\n throw new Error(`\\`${consumerName}\\` must be used within \\`${rootComponentName}\\``);\n }\n Provider.displayName = rootComponentName + \"Provider\";\n return [Provider, useContext2];\n }\n const createScope = () => {\n const scopeContexts = defaultContexts.map((defaultContext) => {\n return React.createContext(defaultContext);\n });\n return function useScope(scope) {\n const contexts = scope?.[scopeName] || scopeContexts;\n return React.useMemo(\n () => ({ [`__scope${scopeName}`]: { ...scope, [scopeName]: contexts } }),\n [scope, contexts]\n );\n };\n };\n createScope.scopeName = scopeName;\n return [createContext3, composeContextScopes(createScope, ...createContextScopeDeps)];\n}\nfunction composeContextScopes(...scopes) {\n const baseScope = scopes[0];\n if (scopes.length === 1) return baseScope;\n const createScope = () => {\n const scopeHooks = scopes.map((createScope2) => ({\n useScope: createScope2(),\n scopeName: createScope2.scopeName\n }));\n return function useComposedScopes(overrideScopes) {\n const nextScopes = scopeHooks.reduce((nextScopes2, { useScope, scopeName }) => {\n const scopeProps = useScope(overrideScopes);\n const currentScope = scopeProps[`__scope${scopeName}`];\n return { ...nextScopes2, ...currentScope };\n }, {});\n return React.useMemo(() => ({ [`__scope${baseScope.scopeName}`]: nextScopes }), [nextScopes]);\n };\n };\n createScope.scopeName = baseScope.scopeName;\n return createScope;\n}\nexport {\n createContext2 as createContext,\n createContextScope\n};\n//# sourceMappingURL=index.mjs.map\n","\"use client\";\n\n// packages/react/roving-focus/src/RovingFocusGroup.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 = null, setCurrentTabStopId] = useControllableState({\n prop: currentTabStopIdProp,\n defaultProp: defaultCurrentTabStopId,\n onChange: onCurrentTabStopIdChange\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 ...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 } = 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 }\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// packages/react/tabs/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 });\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 { type SerializeFrom } from '@remix-run/node'\nimport { Await, Link, useLoaderData } from '@remix-run/react'\nimport * as React from 'react'\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'\nimport { type loader } from '../index.tsx'\n\nexport function DiscordChat() {\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=\"flex-1 overflow-y-scroll bg-accent pb-4 scrollbar-thin scrollbar-thumb-scrollbar\">\n\t\t\t\t<DiscordPosts />\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nfunction DiscordPosts() {\n\tconst data = useLoaderData<typeof loader>()\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={data.discordPostsPromise}\n\t\t\t\t\terrorElement={\n\t\t\t\t\t\t<div className=\"text-red-500\">\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=\"rounded-xl border bg-background 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? (e) => {\n\t\t\t\t\t\t\t\t\te.preventDefault()\n\t\t\t\t\t\t\t\t\twindow.open(\n\t\t\t\t\t\t\t\t\t\te.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({\n\tthread,\n}: {\n\tthread: Awaited<SerializeFrom<typeof loader>['discordPostsPromise']>[number]\n}) {\n\tconst reactionsWithCounts = thread.reactions.filter((r) => r.count)\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 gap-4\">\n\t\t\t\t\t<div className=\"flex 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((t) => (\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tkey={t.name}\n\t\t\t\t\t\t\t\t\t\tclassName=\"flex items-center justify-center gap-1 rounded-full bg-accent 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={t.emojiName} url={t.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>{t.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 items-start 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=\"flex-1 overflow-ellipsis text-muted-foreground\">\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((r, i) => (\n\t\t\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\t\t\tkey={i}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"flex items-center gap-1 rounded-md border border-blue-600 bg-blue-500/20 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 name={r.emojiName} url={r.emojiUrl} />\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>{r.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 { toast as showToast } from 'sonner'\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>\n\t\t\t\t\t<div className=\"text-foreground-secondary flex h-full items-center justify-center text-2xl\">\n\t\t\t\t\t\tNon-UI playground\n\t\t\t\t\t</div>\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<div className=\"text-foreground-secondary flex flex-wrap gap-1 text-center\">\n\t\t\t\t\t\t\tNavigate to{' '}\n\t\t\t\t\t\t\t<SimpleTooltip content={playgroundAppInfo.fullPath}>\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tclassName=\"underline\"\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\tvoid navigator.clipboard.writeText(\n\t\t\t\t\t\t\t\t\t\t\tplaygroundAppInfo.fullPath,\n\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\tshowToast.success('Copied playground path to clipboard')\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\t\tthe playground directory\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</SimpleTooltip>{' '}\n\t\t\t\t\t\t\tin your editor and terminal to work on this exercise!\n\t\t\t\t\t\t</div>\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","import {\n\tgetAppByName,\n\tgetAppDisplayName,\n\tgetApps,\n\tgetExerciseApp,\n\tisExerciseStepApp,\n\tisPlaygroundApp,\n\trequireExerciseApp,\n\ttype App,\n\ttype ExerciseStepApp,\n} from '@epic-web/workshop-utils/apps.server'\nimport { compileMarkdownString } from '@epic-web/workshop-utils/compile-mdx.server'\nimport { userHasAccessToWorkshop } from '@epic-web/workshop-utils/epic-api.server'\nimport {\n\tcombineServerTimings,\n\tgetServerTimeHeader,\n\tmakeTimings,\n} from '@epic-web/workshop-utils/timing.server'\nimport * as Tabs from '@radix-ui/react-tabs'\nimport {\n\tunstable_data as data,\n\tredirect,\n\ttype HeadersFunction,\n\ttype LoaderFunctionArgs,\n} from '@remix-run/node'\nimport {\n\tLink,\n\tuseLoaderData,\n\tuseNavigate,\n\tuseSearchParams,\n} from '@remix-run/react'\nimport { clsx } from 'clsx'\nimport * as React from 'react'\nimport { useRef } from 'react'\nimport { Diff } from '#app/components/diff.tsx'\nimport { GeneralErrorBoundary } from '#app/components/error-boundary.tsx'\nimport { type InBrowserBrowserRef } from '#app/components/in-browser-browser.tsx'\nimport { getDiffCode } from '@epic-web/workshop-utils/diff.server'\nimport { useAltDown } from '#app/utils/misc.tsx'\nimport { fetchDiscordPosts } from './__shared/discord.server.ts'\nimport { DiscordChat } from './__shared/discord.tsx'\nimport { Playground } from './__shared/playground.tsx'\nimport { Preview } from './__shared/preview.tsx'\nimport { Tests } from './__shared/tests.tsx'\nimport { getAppRunningState } from './__shared/utils.tsx'\n\nexport async function loader({ request, params }: LoaderFunctionArgs) {\n\tconst timings = makeTimings('exerciseStepTypeIndexLoader')\n\tconst userHasAccess = await userHasAccessToWorkshop({\n\t\trequest,\n\t\ttimings,\n\t})\n\tconst searchParams = new URL(request.url).searchParams\n\tconst cacheOptions = { request, timings }\n\tconst exerciseStepApp = await requireExerciseApp(params, cacheOptions)\n\tconst reqUrl = new URL(request.url)\n\n\tconst pathnameParam = reqUrl.searchParams.get('pathname')\n\tif (pathnameParam === '' || pathnameParam === '/') {\n\t\treqUrl.searchParams.delete('pathname')\n\t\tthrow redirect(reqUrl.toString())\n\t}\n\n\tconst problemApp = await getExerciseApp(\n\t\t{ ...params, type: 'problem' },\n\t\tcacheOptions,\n\t)\n\tconst solutionApp = await getExerciseApp(\n\t\t{ ...params, type: 'solution' },\n\t\tcacheOptions,\n\t)\n\n\tif (!problemApp && !solutionApp) {\n\t\tthrow new Response('Not found', { status: 404 })\n\t}\n\n\tconst allAppsFull = await getApps(cacheOptions)\n\tconst playgroundApp = allAppsFull.find(isPlaygroundApp)\n\n\tconst app1Name = reqUrl.searchParams.get('app1')\n\tconst app2Name = reqUrl.searchParams.get('app2')\n\tconst app1 = app1Name\n\t\t? await getAppByName(app1Name)\n\t\t: playgroundApp || problemApp\n\tconst app2 = app2Name ? await getAppByName(app2Name) : solutionApp\n\n\tfunction getStepId(a: ExerciseStepApp) {\n\t\treturn (\n\t\t\ta.exerciseNumber * 1000 +\n\t\t\ta.stepNumber * 10 +\n\t\t\t(a.type === 'problem' ? 0 : 1)\n\t\t)\n\t}\n\n\tfunction getStepNameAndId(a: App) {\n\t\tif (isExerciseStepApp(a)) {\n\t\t\tconst exerciseNumberStr = String(a.exerciseNumber).padStart(2, '0')\n\t\t\tconst stepNumberStr = String(a.stepNumber).padStart(2, '0')\n\n\t\t\treturn {\n\t\t\t\tstepName: `${exerciseNumberStr}/${stepNumberStr}.${a.type}`,\n\t\t\t\tstepId: getStepId(a),\n\t\t\t}\n\t\t}\n\t\treturn { stepName: '', stepId: -1 }\n\t}\n\n\tconst allApps = allAppsFull\n\t\t.filter((a, i, ar) => ar.findIndex((b) => a.name === b.name) === i)\n\t\t.map((a) => ({\n\t\t\tdisplayName: getAppDisplayName(a, allAppsFull),\n\t\t\tname: a.name,\n\t\t\ttitle: a.title,\n\t\t\ttype: a.type,\n\t\t\t...getStepNameAndId(a),\n\t\t}))\n\n\tallApps.sort((a, b) => {\n\t\t// order them by their stepId\n\t\tif (a.stepId > 0 && b.stepId > 0) return a.stepId - b.stepId\n\n\t\t// non-step apps should come after step apps\n\t\tif (a.stepId > 0) return -1\n\t\tif (b.stepId > 0) return 1\n\n\t\treturn 0\n\t})\n\n\tasync function getDiffProp() {\n\t\tif (!app1 || !app2) {\n\t\t\treturn {\n\t\t\t\tapp1: app1?.name,\n\t\t\t\tapp2: app2?.name,\n\t\t\t\tdiffCode: null,\n\t\t\t}\n\t\t}\n\t\tif (!userHasAccess) {\n\t\t\treturn {\n\t\t\t\tapp1: app1?.name,\n\t\t\t\tapp2: app2?.name,\n\t\t\t\tdiffCode: await compileMarkdownString(\n\t\t\t\t\t`<h1>Access Denied</h1><p>You must login or register for the workshop to view the diff</p>`,\n\t\t\t\t),\n\t\t\t}\n\t\t}\n\t\tconst diffCode = await getDiffCode(app1, app2, {\n\t\t\t...cacheOptions,\n\t\t\tforceFresh: searchParams.get('forceFresh') === 'diff',\n\t\t}).catch((e) => {\n\t\t\tconsole.error(e)\n\t\t\treturn null\n\t\t})\n\t\treturn {\n\t\t\tapp1: app1.name,\n\t\t\tapp2: app2.name,\n\t\t\tdiffCode,\n\t\t}\n\t}\n\n\treturn data(\n\t\t{\n\t\t\ttype: params.type as 'problem' | 'solution',\n\t\t\texerciseStepApp,\n\t\t\tallApps,\n\t\t\t// defer this promise so that we don't block the response from being sent\n\t\t\tdiscordPostsPromise: fetchDiscordPosts({ request }),\n\t\t\tplayground: playgroundApp\n\t\t\t\t? ({\n\t\t\t\t\t\ttype: 'playground',\n\t\t\t\t\t\tfullPath: playgroundApp.fullPath,\n\t\t\t\t\t\tdev: playgroundApp.dev,\n\t\t\t\t\t\ttest: playgroundApp.test,\n\t\t\t\t\t\ttitle: playgroundApp.title,\n\t\t\t\t\t\tname: playgroundApp.name,\n\t\t\t\t\t\tappName: playgroundApp.appName,\n\t\t\t\t\t\tisUpToDate: playgroundApp.isUpToDate,\n\t\t\t\t\t\tstackBlitzUrl: playgroundApp.stackBlitzUrl,\n\t\t\t\t\t\t...(await getAppRunningState(playgroundApp)),\n\t\t\t\t\t} as const)\n\t\t\t\t: null,\n\t\t\tproblem: problemApp\n\t\t\t\t? ({\n\t\t\t\t\t\ttype: 'problem',\n\t\t\t\t\t\tfullPath: problemApp.fullPath,\n\t\t\t\t\t\tdev: problemApp.dev,\n\t\t\t\t\t\ttest: problemApp.test,\n\t\t\t\t\t\ttitle: problemApp.title,\n\t\t\t\t\t\tname: problemApp.name,\n\t\t\t\t\t\tstackBlitzUrl: problemApp.stackBlitzUrl,\n\t\t\t\t\t\t...(await getAppRunningState(problemApp)),\n\t\t\t\t\t} as const)\n\t\t\t\t: null,\n\t\t\tsolution: solutionApp\n\t\t\t\t? ({\n\t\t\t\t\t\ttype: 'solution',\n\t\t\t\t\t\tfullPath: solutionApp.fullPath,\n\t\t\t\t\t\tdev: solutionApp.dev,\n\t\t\t\t\t\ttest: solutionApp.test,\n\t\t\t\t\t\ttitle: solutionApp.title,\n\t\t\t\t\t\tname: solutionApp.name,\n\t\t\t\t\t\tstackBlitzUrl: solutionApp.stackBlitzUrl,\n\t\t\t\t\t\t...(await getAppRunningState(solutionApp)),\n\t\t\t\t\t} as const)\n\t\t\t\t: null,\n\t\t\tdiff: getDiffProp(),\n\t\t} as const,\n\t\t{\n\t\t\theaders: {\n\t\t\t\t'Server-Timing': getServerTimeHeader(timings),\n\t\t\t},\n\t\t},\n\t)\n}\n\nexport const headers: HeadersFunction = ({ loaderHeaders, parentHeaders }) => {\n\tconst headers = {\n\t\t'Server-Timing': combineServerTimings(loaderHeaders, parentHeaders),\n\t}\n\treturn headers\n}\n\nconst tabs = [\n\t'playground',\n\t'problem',\n\t'solution',\n\t'tests',\n\t'diff',\n\t'chat',\n] as const\nconst isValidPreview = (s: string | null): s is (typeof tabs)[number] =>\n\tBoolean(s && tabs.includes(s as (typeof tabs)[number]))\n\nfunction withParam(\n\tsearchParams: URLSearchParams,\n\tkey: string,\n\tvalue: string | null,\n) {\n\tconst newSearchParams = new URLSearchParams(searchParams)\n\tif (value === null) {\n\t\tnewSearchParams.delete(key)\n\t} else {\n\t\tnewSearchParams.set(key, value)\n\t}\n\treturn newSearchParams\n}\n\nexport default function ExercisePartRoute() {\n\tconst data = useLoaderData<typeof loader>()\n\tconst [searchParams] = useSearchParams()\n\n\tconst preview = searchParams.get('preview')\n\tconst inBrowserBrowserRef = useRef<InBrowserBrowserRef>(null)\n\n\tconst altDown = useAltDown()\n\tconst navigate = useNavigate()\n\n\tfunction shouldHideTab(tab: (typeof tabs)[number]) {\n\t\tif (tab === 'tests') {\n\t\t\treturn (\n\t\t\t\tENV.EPICSHOP_DEPLOYED ||\n\t\t\t\t!data.playground ||\n\t\t\t\tdata.playground.test.type === 'none'\n\t\t\t)\n\t\t}\n\t\tif (tab === 'problem' || tab === 'solution') {\n\t\t\tif (data[tab]?.dev.type === 'none') return true\n\t\t\tif (ENV.EPICSHOP_DEPLOYED) {\n\t\t\t\treturn data[tab]?.dev.type !== 'browser' && !data[tab]?.stackBlitzUrl\n\t\t\t}\n\t\t}\n\t\tif (tab === 'playground' && ENV.EPICSHOP_DEPLOYED) return true\n\t\treturn false\n\t}\n\n\tconst activeTab = isValidPreview(preview)\n\t\t? preview\n\t\t: tabs.find((t) => !shouldHideTab(t))\n\n\t// when alt is held down, the diff tab should open to the full-page diff view\n\t// between the problem and solution (this is more for the instructor than the student)\n\tconst altDiffUrl = `/diff?${new URLSearchParams({\n\t\tapp1: data.problem?.name ?? '',\n\t\tapp2: data.solution?.name ?? '',\n\t})}`\n\n\tfunction handleDiffTabClick(event: React.MouseEvent<HTMLAnchorElement>) {\n\t\tif (event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey) {\n\t\t\tevent.preventDefault()\n\t\t\tnavigate(altDiffUrl)\n\t\t}\n\t}\n\n\treturn (\n\t\t<Tabs.Root\n\t\t\tclassName=\"relative flex flex-col overflow-y-auto sm:col-span-1 sm:row-span-1\"\n\t\t\tvalue={activeTab}\n\t\t\t// intentionally no onValueChange here because the Link will trigger the\n\t\t\t// change.\n\t\t>\n\t\t\t<Tabs.List className=\"h-14 min-h-14 overflow-x-hidden border-b scrollbar-thin scrollbar-thumb-scrollbar\">\n\t\t\t\t{tabs.map((tab) => {\n\t\t\t\t\tconst hidden = shouldHideTab(tab)\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<Tabs.Trigger key={tab} value={tab} hidden={hidden} asChild>\n\t\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\t\tid={`${tab}-tab`}\n\t\t\t\t\t\t\t\tclassName={clsx(\n\t\t\t\t\t\t\t\t\t'clip-path-button relative h-full px-6 py-4 font-mono text-sm uppercase outline-none 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',\n\t\t\t\t\t\t\t\t\thidden ? 'hidden' : 'inline-block',\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\tpreventScrollReset\n\t\t\t\t\t\t\t\tprefetch=\"intent\"\n\t\t\t\t\t\t\t\tonClick={handleDiffTabClick}\n\t\t\t\t\t\t\t\tto={\n\t\t\t\t\t\t\t\t\ttab === 'diff' && altDown\n\t\t\t\t\t\t\t\t\t\t? altDiffUrl\n\t\t\t\t\t\t\t\t\t\t: `?${withParam(\n\t\t\t\t\t\t\t\t\t\t\t\tsearchParams,\n\t\t\t\t\t\t\t\t\t\t\t\t'preview',\n\t\t\t\t\t\t\t\t\t\t\t\ttab === 'playground' ? null : tab,\n\t\t\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>\n\t\t\t\t\t\t\t\t{tab}\n\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t</Tabs.Trigger>\n\t\t\t\t\t)\n\t\t\t\t})}\n\t\t\t</Tabs.List>\n\t\t\t<div className=\"relative z-10 flex min-h-96 flex-grow flex-col overflow-y-auto\">\n\t\t\t\t<Tabs.Content\n\t\t\t\t\tvalue=\"playground\"\n\t\t\t\t\tclassName=\"flex w-full flex-grow items-center justify-center self-start radix-state-inactive:hidden\"\n\t\t\t\t>\n\t\t\t\t\t<Playground\n\t\t\t\t\t\tappInfo={data.playground}\n\t\t\t\t\t\tproblemAppName={data.problem?.name}\n\t\t\t\t\t\tinBrowserBrowserRef={inBrowserBrowserRef}\n\t\t\t\t\t\tallApps={data.allApps}\n\t\t\t\t\t\tisUpToDate={data.playground?.isUpToDate ?? false}\n\t\t\t\t\t/>\n\t\t\t\t</Tabs.Content>\n\t\t\t\t<Tabs.Content\n\t\t\t\t\tvalue=\"problem\"\n\t\t\t\t\tclassName=\"flex w-full flex-grow items-center justify-center self-start radix-state-inactive:hidden\"\n\t\t\t\t>\n\t\t\t\t\t<Preview\n\t\t\t\t\t\tappInfo={data.problem}\n\t\t\t\t\t\tinBrowserBrowserRef={inBrowserBrowserRef}\n\t\t\t\t\t/>\n\t\t\t\t</Tabs.Content>\n\t\t\t\t<Tabs.Content\n\t\t\t\t\tvalue=\"solution\"\n\t\t\t\t\tclassName=\"flex w-full flex-grow items-center justify-center self-start radix-state-inactive:hidden\"\n\t\t\t\t>\n\t\t\t\t\t<Preview\n\t\t\t\t\t\tappInfo={data.solution}\n\t\t\t\t\t\tinBrowserBrowserRef={inBrowserBrowserRef}\n\t\t\t\t\t/>\n\t\t\t\t</Tabs.Content>\n\t\t\t\t<Tabs.Content\n\t\t\t\t\tvalue=\"tests\"\n\t\t\t\t\tclassName=\"flex w-full flex-grow items-start justify-center self-start overflow-hidden radix-state-inactive:hidden\"\n\t\t\t\t>\n\t\t\t\t\t<Tests\n\t\t\t\t\t\tappInfo={data.playground}\n\t\t\t\t\t\tproblemAppName={data.problem?.name}\n\t\t\t\t\t\tallApps={data.allApps}\n\t\t\t\t\t\tisUpToDate={data.playground?.isUpToDate ?? false}\n\t\t\t\t\t/>\n\t\t\t\t</Tabs.Content>\n\t\t\t\t<Tabs.Content\n\t\t\t\t\tvalue=\"diff\"\n\t\t\t\t\tclassName=\"flex h-full w-full flex-grow items-start justify-center self-start radix-state-inactive:hidden\"\n\t\t\t\t>\n\t\t\t\t\t<Diff diff={data.diff} allApps={data.allApps} />\n\t\t\t\t</Tabs.Content>\n\t\t\t\t<Tabs.Content\n\t\t\t\t\tvalue=\"chat\"\n\t\t\t\t\tclassName=\"flex h-full w-full flex-grow items-start justify-center self-start radix-state-inactive:hidden\"\n\t\t\t\t>\n\t\t\t\t\t<DiscordChat />\n\t\t\t\t</Tabs.Content>\n\t\t\t</div>\n\t\t</Tabs.Root>\n\t)\n}\n\nexport function ErrorBoundary() {\n\treturn (\n\t\t<GeneralErrorBoundary\n\t\t\tstatusHandlers={{\n\t\t\t\t404: () => <p>Sorry, we couldn't find an app here.</p>,\n\t\t\t}}\n\t\t/>\n\t)\n}\n"],"names":["createContextScope","scopeName","createContextScopeDeps","defaultContexts","createContext3","rootComponentName","defaultContext","BaseContext","React.createContext","index","Provider","props","scope","children","context","Context","value","React.useMemo","jsx","useContext2","consumerName","React.useContext","createScope","scopeContexts","contexts","composeContextScopes","scopes","baseScope","scopeHooks","createScope2","overrideScopes","nextScopes","nextScopes2","useScope","currentScope","ENTRY_FOCUS","EVENT_OPTIONS","GROUP_NAME","Collection","useCollection","createCollectionScope","createCollection","createRovingFocusGroupContext","createRovingFocusGroupScope","RovingFocusProvider","useRovingFocusContext","RovingFocusGroup","React.forwardRef","forwardedRef","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","itemProps","autoId","useId","id","isCurrentTabStop","onFocusableItemAdd","onFocusableItemRemove","focusIntent","getFocusIntent","currentIndex","wrapArray","MAP_KEY_TO_FOCUS_INTENT","getDirectionAwareKey","key","candidates","preventScroll","PREVIOUSLY_FOCUSED_ELEMENT","candidate","array","startIndex","_","Root","Item","TABS_NAME","createTabsContext","createTabsScope","useRovingFocusGroupScope","TabsProvider","useTabsContext","Tabs","__scopeTabs","valueProp","onValueChange","defaultValue","activationMode","tabsProps","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","jsxs","DiscordCTA","DiscordPosts","data","useLoaderData","ctaLink","useDiscordCTALink","altDown","useAltDown","useIsOnline","React.Suspense","Loading","Await","posts","post","DiscordPost","Link","e","Icon","thread","reactionsWithCounts","t","Emoji","i","name","url","Playground","playgroundAppInfo","inBrowserBrowserRef","problemAppName","allApps","isUpToDate","PlaygroundWindow","SimpleTooltip","showToast","Preview","SetAppToPlayground","tabs","isValidPreview","s","Boolean","includes","withParam","searchParams","newSearchParams","URLSearchParams","delete","set","ExercisePartRoute","useSearchParams","preview","get","useRef","navigate","useNavigate","shouldHideTab","tab","ENV","EPICSHOP_DEPLOYED","playground","test","type","dev","stackBlitzUrl","activeTab","find","altDiffUrl","app1","problem","app2","solution","handleDiffTabClick","altKey","ctrlKey","shiftKey","metaKey","preventDefault","className","map","hidden","asChild","clsx","preventScrollReset","prefetch","onClick","to","appInfo","Tests","Diff","diff","ErrorBoundary","GeneralErrorBoundary","statusHandlers"],"mappings":"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","x_google_ignoreList":[0,1,2]}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{j as e}from"./index-CGzylDPY.js";import{E as t}from"./index-egcHQOpF.js";import{E as a}from"./epic-video-
|
|
2
|
-
//# sourceMappingURL=index-
|
|
1
|
+
import{j as e}from"./index-CGzylDPY.js";import{E as t}from"./index-egcHQOpF.js";import{E as a}from"./epic-video-DoUlMEIW.js";import{G as i}from"./error-boundary-DBVB3BBH.js";import{M as l,E as m}from"./mdx-Ce3knRHx.js";import{a as n}from"./misc-D9k1wGip.js";import{P as d,u as c}from"./progress-CpALgZbi.js";import{u as p,L as x}from"./components-DrvY4pal.js";import"./index-BOO5UotZ.js";import"./tooltip-CzrLrLJU.js";import"./pe-DXT2FOp1.js";import"./online-BrcRwzQC.js";import"./loading-D4V_nJZr.js";import"./user-C0j04V55.js";import"./workshop-config-oL_FWDKq.js";import"./progress-bar-IswLOt8e.js";function h({exercise:r}){const s=c(r.exerciseNumber);return e.jsx("li",{children:e.jsxs(x,{className:n("relative flex items-center gap-4 px-4 py-3 text-lg font-semibold transition after:absolute after:right-10 after:-translate-x-2 after:opacity-0 after:transition after:content-['→'] hover:bg-gray-50 hover:after:translate-x-0 hover:after:opacity-100 dark:hover:bg-white/5",s),to:`${r.exerciseNumber.toString().padStart(2,"0")}`,children:[e.jsx("span",{className:"text-xs font-normal tabular-nums opacity-50",children:r.exerciseNumber}),e.jsx("span",{children:r.title})]})},r.exerciseNumber)}const f={h1:()=>null};function S(){const r=p(),s=e.jsxs("ul",{className:"flex flex-col divide-y divide-border dark:divide-border/50",children:[e.jsx("strong",{className:"px-10 pb-3 font-mono text-xs uppercase",children:"Exercises"}),r.exercises.map(o=>e.jsx(h,{exercise:o},o.exerciseNumber))]});return e.jsxs("main",{className:"relative flex h-full w-full max-w-5xl flex-col justify-between border-r md:w-3/4 xl:w-2/3",children:[e.jsxs("article",{id:r.articleId,className:"shadow-on-scrollbox flex w-full flex-1 flex-col gap-12 overflow-y-scroll px-3 py-4 pt-6 scrollbar-thin scrollbar-thumb-scrollbar md:px-10 md:py-12 md:pt-16",children:[e.jsx("div",{children:e.jsx("h1",{className:"px-10 text-[clamp(3rem,6vw,7.5rem)] font-extrabold leading-none",children:r.title})}),e.jsxs("div",{className:"w-full max-w-none scroll-pt-6 border-t px-3 pt-3 md:px-10 md:pt-8",children:[e.jsx("h2",{className:"pb-5 font-mono text-xs font-semibold uppercase",children:"Intro"}),r.workshopReadme.compiled.status==="success"&&r.workshopReadme.compiled.code?e.jsx(a,{epicVideoInfosPromise:r.epicVideoInfosPromise,children:e.jsx("div",{className:"prose dark:prose-invert sm:prose-lg",children:e.jsx(l,{code:r.workshopReadme.compiled.code,components:f})})}):r.workshopReadme.compiled.status==="error"?e.jsxs("div",{className:"text-red-500",children:["There was an error:",e.jsx("pre",{children:r.workshopReadme.compiled.error})]}):"No instructions yet..."]}),e.jsx("div",{className:"pb-5 pt-10",children:r.workshopReadme.compiled.status==="success"&&r.workshopReadme.compiled.code&&r.workshopReadme.compiled.code.length>500?s:null})]}),e.jsx(t,{elementQuery:`#${r.articleId}`}),e.jsx(d,{type:"workshop-instructions",className:"h-14 border-t px-6"}),e.jsx("div",{className:"flex h-16 justify-center border-t",children:e.jsx(m,{file:r.workshopReadme.file,relativePath:r.workshopReadme.relativePath})})]})}function V(){return e.jsx(i,{})}export{V as ErrorBoundary,S as default};
|
|
2
|
+
//# sourceMappingURL=index-De6oiBJI.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-
|
|
1
|
+
{"version":3,"file":"index-De6oiBJI.js","sources":["../../../app/routes/_app+/index.tsx"],"sourcesContent":["import { ElementScrollRestoration } from '@epic-web/restore-scroll'\nimport {\n\tgetExercises,\n\tgetWorkshopInstructions,\n} from '@epic-web/workshop-utils/apps.server'\nimport { getWorkshopConfig } from '@epic-web/workshop-utils/config.server'\nimport { getEpicVideoInfos } from '@epic-web/workshop-utils/epic-api.server'\nimport {\n\tcombineServerTimings,\n\tgetServerTimeHeader,\n\tmakeTimings,\n\ttime,\n} from '@epic-web/workshop-utils/timing.server'\nimport {\n\tunstable_data as data,\n\ttype HeadersFunction,\n\ttype LoaderFunctionArgs,\n\ttype SerializeFrom,\n} from '@remix-run/node'\nimport { Link, useLoaderData } from '@remix-run/react'\nimport slugify from '@sindresorhus/slugify'\nimport { EpicVideoInfoProvider } from '#app/components/epic-video.tsx'\nimport { GeneralErrorBoundary } from '#app/components/error-boundary.tsx'\nimport { EditFileOnGitHub } from '#app/routes/launch-editor.tsx'\nimport { Mdx } from '#app/utils/mdx.tsx'\nimport { cn } from '#app/utils/misc.tsx'\nimport { ProgressToggle, useExerciseProgressClassName } from '../progress.tsx'\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n\tconst timings = makeTimings('indexLoader')\n\tconst { title } = getWorkshopConfig()\n\tconst [exercises, workshopReadme] = await Promise.all([\n\t\ttime(() => getExercises({ request, timings }), {\n\t\t\ttimings,\n\t\t\ttype: 'getExercises',\n\t\t\tdesc: 'getExercises in index',\n\t\t}),\n\t\ttime(() => getWorkshopInstructions({ request }), {\n\t\t\ttimings,\n\t\t\ttype: 'compileMdx',\n\t\t\tdesc: 'compileMdx in index',\n\t\t}),\n\t])\n\n\treturn data(\n\t\t{\n\t\t\tarticleId: `workshop-${slugify(title)}-instructions`,\n\t\t\ttitle:\n\t\t\t\tworkshopReadme.compiled.status === 'success'\n\t\t\t\t\t? workshopReadme.compiled.title\n\t\t\t\t\t: title,\n\t\t\texercises: exercises.map((e) => ({\n\t\t\t\texerciseNumber: e.exerciseNumber,\n\t\t\t\ttitle: e.title,\n\t\t\t})),\n\t\t\tworkshopReadme,\n\t\t\tepicVideoInfosPromise:\n\t\t\t\tworkshopReadme.compiled.status === 'success'\n\t\t\t\t\t? getEpicVideoInfos(workshopReadme.compiled.epicVideoEmbeds, {\n\t\t\t\t\t\t\trequest,\n\t\t\t\t\t\t})\n\t\t\t\t\t: null,\n\t\t},\n\t\t{\n\t\t\theaders: {\n\t\t\t\t'Server-Timing': getServerTimeHeader(timings),\n\t\t\t},\n\t\t},\n\t)\n}\n\nexport const headers: HeadersFunction = ({ loaderHeaders, parentHeaders }) => {\n\tconst headers = {\n\t\t'Cache-Control': loaderHeaders.get('Cache-Control') ?? '',\n\t\t'Server-Timing': combineServerTimings(loaderHeaders, parentHeaders),\n\t}\n\treturn headers\n}\n\nfunction ExerciseListItem({\n\texercise,\n}: {\n\texercise: SerializeFrom<typeof loader>['exercises'][number]\n}) {\n\tconst progressClassName = useExerciseProgressClassName(\n\t\texercise.exerciseNumber,\n\t)\n\treturn (\n\t\t<li key={exercise.exerciseNumber}>\n\t\t\t<Link\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"relative flex items-center gap-4 px-4 py-3 text-lg font-semibold transition after:absolute after:right-10 after:-translate-x-2 after:opacity-0 after:transition after:content-['→'] hover:bg-gray-50 hover:after:translate-x-0 hover:after:opacity-100 dark:hover:bg-white/5\",\n\t\t\t\t\tprogressClassName,\n\t\t\t\t)}\n\t\t\t\tto={`${exercise.exerciseNumber.toString().padStart(2, '0')}`}\n\t\t\t>\n\t\t\t\t<span className=\"text-xs font-normal tabular-nums opacity-50\">\n\t\t\t\t\t{exercise.exerciseNumber}\n\t\t\t\t</span>\n\t\t\t\t<span>{exercise.title}</span>\n\t\t\t</Link>\n\t\t</li>\n\t)\n}\n\nconst mdxComponents = { h1: () => null }\n\nexport default function Index() {\n\tconst data = useLoaderData<typeof loader>()\n\n\tconst exerciseLinks = (\n\t\t<ul className=\"flex flex-col divide-y divide-border dark:divide-border/50\">\n\t\t\t<strong className=\"px-10 pb-3 font-mono text-xs uppercase\">\n\t\t\t\tExercises\n\t\t\t</strong>\n\t\t\t{data.exercises.map((exercise) => (\n\t\t\t\t<ExerciseListItem key={exercise.exerciseNumber} exercise={exercise} />\n\t\t\t))}\n\t\t</ul>\n\t)\n\treturn (\n\t\t<main className=\"relative flex h-full w-full max-w-5xl flex-col justify-between border-r md:w-3/4 xl:w-2/3\">\n\t\t\t<article\n\t\t\t\tid={data.articleId}\n\t\t\t\tclassName=\"shadow-on-scrollbox flex w-full flex-1 flex-col gap-12 overflow-y-scroll px-3 py-4 pt-6 scrollbar-thin scrollbar-thumb-scrollbar md:px-10 md:py-12 md:pt-16\"\n\t\t\t>\n\t\t\t\t<div>\n\t\t\t\t\t<h1 className=\"px-10 text-[clamp(3rem,6vw,7.5rem)] font-extrabold leading-none\">\n\t\t\t\t\t\t{data.title}\n\t\t\t\t\t</h1>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"w-full max-w-none scroll-pt-6 border-t px-3 pt-3 md:px-10 md:pt-8\">\n\t\t\t\t\t<h2 className=\"pb-5 font-mono text-xs font-semibold uppercase\">\n\t\t\t\t\t\tIntro\n\t\t\t\t\t</h2>\n\t\t\t\t\t{data.workshopReadme.compiled.status === 'success' &&\n\t\t\t\t\tdata.workshopReadme.compiled.code ? (\n\t\t\t\t\t\t<EpicVideoInfoProvider\n\t\t\t\t\t\t\tepicVideoInfosPromise={data.epicVideoInfosPromise}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<div className=\"prose dark:prose-invert sm:prose-lg\">\n\t\t\t\t\t\t\t\t<Mdx\n\t\t\t\t\t\t\t\t\tcode={data.workshopReadme.compiled.code}\n\t\t\t\t\t\t\t\t\tcomponents={mdxComponents}\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</EpicVideoInfoProvider>\n\t\t\t\t\t) : data.workshopReadme.compiled.status === 'error' ? (\n\t\t\t\t\t\t<div className=\"text-red-500\">\n\t\t\t\t\t\t\tThere was an error:\n\t\t\t\t\t\t\t<pre>{data.workshopReadme.compiled.error}</pre>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t'No instructions yet...'\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t<div className=\"pb-5 pt-10\">\n\t\t\t\t\t{data.workshopReadme.compiled.status === 'success' &&\n\t\t\t\t\tdata.workshopReadme.compiled.code &&\n\t\t\t\t\tdata.workshopReadme.compiled.code.length > 500\n\t\t\t\t\t\t? exerciseLinks\n\t\t\t\t\t\t: null}\n\t\t\t\t</div>\n\t\t\t</article>\n\t\t\t<ElementScrollRestoration elementQuery={`#${data.articleId}`} />\n\t\t\t<ProgressToggle\n\t\t\t\ttype=\"workshop-instructions\"\n\t\t\t\tclassName=\"h-14 border-t px-6\"\n\t\t\t/>\n\t\t\t<div className=\"flex h-16 justify-center border-t\">\n\t\t\t\t<EditFileOnGitHub\n\t\t\t\t\tfile={data.workshopReadme.file}\n\t\t\t\t\trelativePath={data.workshopReadme.relativePath}\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</main>\n\t)\n}\n\nexport function ErrorBoundary() {\n\treturn <GeneralErrorBoundary />\n}\n"],"names":["ExerciseListItem","exercise","progressClassName","useExerciseProgressClassName","exerciseNumber","children","jsxs","Link","className","cn","to","toString","padStart","jsx","title","mdxComponents","h1","Index","data","useLoaderData","exerciseLinks","exercises","map","id","articleId","workshopReadme","compiled","status","code","EpicVideoInfoProvider","epicVideoInfosPromise","Mdx","components","error","length","ElementScrollRestoration","elementQuery","ProgressToggle","type","EditFileOnGitHub","file","relativePath","ErrorBoundary","GeneralErrorBoundary"],"mappings":"0lBA+EA,SAASA,EAAiB,CACzBC,SAAAA,CACD,EAEG,CACF,MAAMC,EAAoBC,EACzBF,EAASG,cACV,EACA,aACE,KACA,CAAAC,SAAAC,EAAA,KAACC,EAAA,CACAC,UAAWC,EACV,+QACAP,CACD,EACAQ,GAAI,GAAGT,EAASG,eAAeO,SAAW,EAAAC,SAAS,EAAG,GAAG,CAAC,GAE1DP,SAAA,CAAAQ,EAAA,IAAC,OAAK,CAAAL,UAAU,8CACdH,SAAAJ,EAASG,cACX,CAAA,EACAS,EAAA,IAAC,OAAM,CAAAR,SAAAJ,EAASa,KAAM,CAAA,CAAA,EACvB,CAAA,EAZQb,EAASG,cAalB,CAEF,CAEA,MAAMW,EAAgB,CAAEC,GAAIA,IAAM,IAAK,EAEvC,SAAwBC,GAAQ,CAC/B,MAAMC,EAAOC,IAEPC,EACLd,EAAA,KAAC,KAAG,CAAAE,UAAU,6DACbH,SAAA,CAACQ,EAAA,IAAA,SAAA,CAAOL,UAAU,yCAAyCH,SAE3D,WAAA,CAAA,EACCa,EAAKG,UAAUC,IAAKrB,SACnBD,EAA+C,CAAAC,SAAAA,CAAA,EAAzBA,EAASG,cAAoC,CACpE,CAAA,CACF,CAAA,EAGA,OAAAE,EAAAA,KAAC,OAAK,CAAAE,UAAU,4FACfH,SAAA,CAAAC,EAAA,KAAC,UAAA,CACAiB,GAAIL,EAAKM,UACThB,UAAU,8JAEVH,SAAA,CAACQ,EAAA,IAAA,MAAA,CACAR,eAAC,KAAG,CAAAG,UAAU,kEACZH,SAAAa,EAAKJ,MACP,CACD,CAAA,EACAR,EAAA,KAAC,MAAI,CAAAE,UAAU,oEACdH,SAAA,CAACQ,EAAA,IAAA,KAAA,CAAGL,UAAU,iDAAiDH,SAE/D,OAAA,CAAA,EACCa,EAAKO,eAAeC,SAASC,SAAW,WACzCT,EAAKO,eAAeC,SAASE,KAC5Bf,EAAAA,IAACgB,EAAA,CACAC,sBAAuBZ,EAAKY,sBAE5BzB,SAAAQ,EAAA,IAAC,MAAI,CAAAL,UAAU,sCACdH,SAAAQ,EAAA,IAACkB,EAAA,CACAH,KAAMV,EAAKO,eAAeC,SAASE,KACnCI,WAAYjB,EACb,EACD,CAAA,CACD,EACGG,EAAKO,eAAeC,SAASC,SAAW,QAC3CrB,EAAA,KAAC,MAAI,CAAAE,UAAU,eAAeH,SAAA,CAAA,4BAE5B,MAAK,CAAAA,SAAAa,EAAKO,eAAeC,SAASO,KAAM,CAAA,CAAA,CAC1C,CAAA,EAEA,wBAAA,CAEF,CAAA,EACApB,EAAA,IAAC,OAAIL,UAAU,aACbH,SAAAa,EAAKO,eAAeC,SAASC,SAAW,WACzCT,EAAKO,eAAeC,SAASE,MAC7BV,EAAKO,eAAeC,SAASE,KAAKM,OAAS,IACxCd,EACA,IACJ,CAAA,CAAA,CAAA,CACD,QACCe,EAAyB,CAAAC,aAAc,IAAIlB,EAAKM,SAAS,EAAI,CAAA,EAC9DX,EAAA,IAACwB,EAAA,CACAC,KAAK,wBACL9B,UAAU,oBAAA,CACX,EACAK,EAAA,IAAC,MAAI,CAAAL,UAAU,oCACdH,SAAAQ,EAAA,IAAC0B,EAAA,CACAC,KAAMtB,EAAKO,eAAee,KAC1BC,aAAcvB,EAAKO,eAAegB,aACnC,CACD,CAAA,CAAA,CACD,CAAA,CAEF,CAEO,SAASC,GAAgB,CAC/B,aAAQC,EAAqB,CAAA,CAAA,CAC9B"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{j as r,r as n}from"./index-CGzylDPY.js";import{b as f}from"./index-
|
|
2
|
-
//# sourceMappingURL=loading-
|
|
1
|
+
import{j as r,r as n}from"./index-CGzylDPY.js";import{b as f}from"./index-BOO5UotZ.js";import{a as d}from"./misc-D9k1wGip.js";function p({className:s,children:t="Loading"}){const{reducedMotion:e}=f();return e==="reduce"?r.jsx("div",{className:"animate-pulse",children:t}):r.jsxs("div",{className:d("flex items-center gap-2 font-mono text-sm font-medium uppercase",s),role:"status",children:[r.jsx("div",{"aria-hidden":"true",children:r.jsx(u,{})}),t,r.jsx("div",{"aria-hidden":"true",children:r.jsx(u,{})})]})}const o="█<▓█ ▒░/▒░ █░>▒▓/ █▒▒ ▓▒▓/█<░▒ ▓/░>",i=()=>o[Math.floor(Math.random()*o.length)];function u(){const[s,t]=n.useState(o[0]),[e,c]=n.useState(o[1]);return m(()=>{t(i()),c(i())},80),r.jsxs("span",{children:[s,e]})}function m(s,t=1e3){const e=n.useRef(null);n.useEffect(()=>{e.current=s},[s]),n.useEffect(()=>{function c(){var a;(a=e.current)==null||a.call(e)}if(t!==null){const a=setInterval(c,t);return()=>clearInterval(a)}},[t])}export{p as L};
|
|
2
|
+
//# sourceMappingURL=loading-D4V_nJZr.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loading-
|
|
1
|
+
{"version":3,"file":"loading-D4V_nJZr.js","sources":["../../../app/components/loading.tsx"],"sourcesContent":["// originally this was based on baffle-react, but it was unnecessarily bloated\n// and very out-dated. I've replaced it with a simple Characters component that\n// does the same thing we were using baffle-react for\nimport { useEffect, useRef, useState } from 'react'\nimport { useHints } from '#app/utils/client-hints.tsx'\nimport { cn } from '#app/utils/misc.tsx'\n\nexport function Loading({\n\tclassName,\n\tchildren = 'Loading',\n}: {\n\tclassName?: string\n\tchildren?: React.ReactNode\n}) {\n\tconst { reducedMotion } = useHints()\n\tif (reducedMotion === 'reduce') {\n\t\treturn <div className=\"animate-pulse\">{children}</div>\n\t}\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t'flex items-center gap-2 font-mono text-sm font-medium uppercase',\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\trole=\"status\"\n\t\t>\n\t\t\t<div aria-hidden=\"true\">\n\t\t\t\t<Characters />\n\t\t\t</div>\n\t\t\t{children}\n\t\t\t<div aria-hidden=\"true\">\n\t\t\t\t<Characters />\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nconst characters = '█<▓█ ▒░/▒░ █░>▒▓/ █▒▒ ▓▒▓/█<░▒ ▓/░>'\nconst randomCharacter = () =>\n\tcharacters[Math.floor(Math.random() * characters.length)]\nexport function Characters() {\n\tconst [char1, setChar1] = useState(characters[0])\n\tconst [char2, setChar2] = useState(characters[1])\n\n\tuseInterval(() => {\n\t\tsetChar1(randomCharacter())\n\t\tsetChar2(randomCharacter())\n\t}, 80)\n\n\treturn (\n\t\t<span>\n\t\t\t{char1}\n\t\t\t{char2}\n\t\t</span>\n\t)\n}\n\nfunction useInterval(callback: () => void, delay: number | null = 1000) {\n\tconst savedCallback = useRef<() => void>(null)\n\n\t// Remember the latest function.\n\tuseEffect(() => {\n\t\tsavedCallback.current = callback\n\t}, [callback])\n\n\t// Set up the interval.\n\tuseEffect(() => {\n\t\tfunction tick() {\n\t\t\tsavedCallback.current?.()\n\t\t}\n\t\tif (delay !== null) {\n\t\t\tconst id = setInterval(tick, delay)\n\t\t\treturn () => clearInterval(id)\n\t\t}\n\t}, [delay])\n}\n"],"names":["Loading","className","children","reducedMotion","useHints","jsx","jsxs","cn","Characters","characters","randomCharacter","char1","setChar1","useState","char2","setChar2","useInterval","callback","delay","savedCallback","useRef","useEffect","tick","_a","id"],"mappings":"8HAOO,SAASA,EAAQ,CACvB,UAAAC,EACA,SAAAC,EAAW,SACZ,EAGG,CACI,KAAA,CAAE,cAAAC,GAAkBC,IAC1B,OAAID,IAAkB,SACbE,EAAAA,IAAA,MAAA,CAAI,UAAU,gBAAiB,SAAAH,CAAS,CAAA,EAIhDI,EAAA,KAAC,MAAA,CACA,UAAWC,EACV,kEACAN,CACD,EACA,KAAK,SAEL,SAAA,CAAAI,MAAC,MAAI,CAAA,cAAY,OAChB,SAAAA,MAACG,GAAW,CAAA,EACb,EACCN,QACA,MAAI,CAAA,cAAY,OAChB,SAAAG,MAACG,GAAW,CAAA,EACb,CAAA,CAAA,CAAA,CAGH,CAEA,MAAMC,EAAa,sCACbC,EAAkB,IACvBD,EAAW,KAAK,MAAM,KAAK,SAAWA,EAAW,MAAM,CAAC,EAClD,SAASD,GAAa,CAC5B,KAAM,CAACG,EAAOC,CAAQ,EAAIC,EAAS,SAAAJ,EAAW,CAAC,CAAC,EAC1C,CAACK,EAAOC,CAAQ,EAAIF,EAAS,SAAAJ,EAAW,CAAC,CAAC,EAEhD,OAAAO,EAAY,IAAM,CACjBJ,EAASF,GAAiB,EAC1BK,EAASL,GAAiB,GACxB,EAAE,SAGH,OACC,CAAA,SAAA,CAAAC,EACAG,CACF,CAAA,CAAA,CAEF,CAEA,SAASE,EAAYC,EAAsBC,EAAuB,IAAM,CACjE,MAAAC,EAAgBC,SAAmB,IAAI,EAG7CC,EAAAA,UAAU,IAAM,CACfF,EAAc,QAAUF,CAAA,EACtB,CAACA,CAAQ,CAAC,EAGbI,EAAAA,UAAU,IAAM,CACf,SAASC,GAAO,QACfC,EAAAJ,EAAc,UAAd,MAAAI,EAAA,KAAAJ,EACD,CACA,GAAID,IAAU,KAAM,CACb,MAAAM,EAAK,YAAYF,EAAMJ,CAAK,EAC3B,MAAA,IAAM,cAAcM,CAAE,CAC9B,CAAA,EACE,CAACN,CAAK,CAAC,CACX"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as i,e as g,d as j,j as e}from"./index-CGzylDPY.js";import{u as v}from"./use-event-source-AZJtQsFX.js";import{a as y,B as b}from"./button-DhtjxLl5.js";import{L as N}from"./loading-
|
|
2
|
-
//# sourceMappingURL=login-
|
|
1
|
+
import{r as i,e as g,d as j,j as e}from"./index-CGzylDPY.js";import{u as v}from"./use-event-source-AZJtQsFX.js";import{a as y,B as b}from"./button-DhtjxLl5.js";import{L as N}from"./loading-D4V_nJZr.js";import{L as R}from"./product-CleOH5Nn.js";import{u as C}from"./workshop-config-oL_FWDKq.js";import{z as t}from"./index-BOO5UotZ.js";import{a as k,L as S}from"./components-DrvY4pal.js";import"./misc-D9k1wGip.js";import"./tooltip-CzrLrLJU.js";import"./pe-DXT2FOp1.js";const s={USER_CODE_RECEIVED:"USER_CODE_RECEIVED",AUTH_RESOLVED:"AUTH_RESOLVED",AUTH_REJECTED:"AUTH_REJECTED"},_=t.object({type:t.literal(s.USER_CODE_RECEIVED),code:t.string(),url:t.string()}),w=t.object({type:t.literal(s.AUTH_RESOLVED)}),D=t.object({type:t.literal(s.AUTH_REJECTED),error:t.string().optional().default("Unknown error")}),L=t.union([_,w,D]),W={getSitemapEntries:()=>null};function Y(){var m;const{product:{displayName:a}}=C(),l=k(),[f,x]=i.useState(!1),[c,p]=i.useState(null),[o,h]=i.useState(null),d=g(),u=j(),n=v("/login-sse");return i.useEffect(()=>{if(!n)return;const E=JSON.parse(n),r=L.safeParse(E);if(!r.success){console.error(r.error.flatten());return}switch(r.data.type){case s.USER_CODE_RECEIVED:{h(r.data);break}case s.AUTH_RESOLVED:{u.revalidate(),d("/");break}case s.AUTH_REJECTED:{p(r.data.error);break}}},[n,d,u]),e.jsx("main",{className:"flex h-full w-full flex-grow flex-col items-center justify-center p-10",children:e.jsxs("div",{className:"flex flex-col items-center",children:[e.jsx(R,{className:"h-16 w-16"}),e.jsxs("h1",{className:"pt-5 text-2xl font-semibold md:text-3xl",children:["Login to ",a]}),e.jsxs("div",{className:"flex w-full flex-col items-center pt-5",children:[o?e.jsxs("div",{className:"flex w-full max-w-md flex-col items-center gap-3",children:[e.jsxs("div",{className:"my-2 flex w-full flex-col items-center gap-2",children:[e.jsx("p",{className:"text-lg",children:"Your verification code is: "}),e.jsx("div",{className:"mb-3 w-full bg-gray-100 px-5 py-3 text-center text-lg font-bold dark:bg-black/40",children:e.jsx("code",{children:o.code})}),e.jsxs("p",{className:"text-base",children:["You'll use this to verify your device on ",a,". Click verify code below to open the verification page."]})]}),e.jsx(y,{varient:"primary",to:o.url,target:"_blank",rel:"noreferrer",onClick:()=>x(!0),children:"Verify Auth Code"}),f?e.jsxs("div",{className:"justify-center pt-5 text-center opacity-60",children:[e.jsx(N,{className:"justify-center",children:"Waiting for confirmation"}),e.jsxs("p",{className:"pt-2",children:["Please open"," ",e.jsx("a",{href:o.url,target:"_blank",className:"underline",children:"your auth page"})," ","in a new tab to continue."]})]}):null]}):e.jsxs("div",{className:"flex flex-col items-center gap-8",children:[e.jsxs("div",{className:"flex max-w-lg flex-col gap-3 pt-3 text-base text-gray-700 dark:text-gray-300",children:[e.jsxs("p",{children:["If you have access to this workshop on ",a,", you'll be able to watch videos, track progress, run tests, view the diffs, and more!"]}),e.jsxs("p",{children:["First you need to authenticate your device by requesting an access code and verifying on ",a,"."]})]}),e.jsx(l.Form,{method:"POST",children:e.jsx(b,{varient:"primary",type:"submit",children:l.state==="idle"&&((m=l.data)==null?void 0:m.status)!=="pending"?"Retrieve Auth Code":"Retrieving Auth Code..."})})]}),c?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"mt-4 text-red-500",children:["There was an error: ",e.jsx("pre",{children:c})]}),e.jsxs("div",{className:"mt-4 text-red-500",children:["Please try again or"," ",e.jsx(S,{to:"/support",className:"underline",children:"contact support"})," ","if the problem persists."]})]}):null]})]})})}export{Y as default,W as handle};
|
|
2
|
+
//# sourceMappingURL=login-DiEHnGfv.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login-RlImW-EE.js","sources":["../../../app/utils/auth-events.ts","../../../app/routes/login-sse.tsx","../../../app/routes/_app+/login.tsx"],"sourcesContent":["export const EVENTS = {\n\tUSER_CODE_RECEIVED: 'USER_CODE_RECEIVED',\n\tAUTH_RESOLVED: 'AUTH_RESOLVED',\n\tAUTH_REJECTED: 'AUTH_REJECTED',\n} as const\nexport type EventTypes = keyof typeof EVENTS\n","import { type LoaderFunctionArgs } from '@remix-run/node'\nimport { eventStream } from 'remix-utils/sse/server'\nimport { z } from 'zod'\nimport { EVENTS } from '#app/utils/auth-events.ts'\nimport { authEmitter } from '#app/utils/auth.server.ts'\nimport { ensureUndeployed } from '#app/utils/misc.tsx'\n\nconst CodeReceivedEventSchema = z.object({\n\ttype: z.literal(EVENTS.USER_CODE_RECEIVED),\n\tcode: z.string(),\n\turl: z.string(),\n})\nconst AuthResolvedEventSchema = z.object({\n\ttype: z.literal(EVENTS.AUTH_RESOLVED),\n})\nconst AuthRejectedEventSchema = z.object({\n\ttype: z.literal(EVENTS.AUTH_REJECTED),\n\terror: z.string().optional().default('Unknown error'),\n})\nexport const EventSchema = z.union([\n\tCodeReceivedEventSchema,\n\tAuthResolvedEventSchema,\n\tAuthRejectedEventSchema,\n])\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n\tensureUndeployed()\n\treturn eventStream(request.signal, function setup(send) {\n\t\tfunction handleCodeReceived(data: any) {\n\t\t\tsend({\n\t\t\t\tdata: JSON.stringify(\n\t\t\t\t\tCodeReceivedEventSchema.parse({\n\t\t\t\t\t\ttype: EVENTS.USER_CODE_RECEIVED,\n\t\t\t\t\t\t...data,\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t})\n\t\t}\n\t\tfunction handleAuthResolved() {\n\t\t\tsend({ data: JSON.stringify({ type: EVENTS.AUTH_RESOLVED }) })\n\t\t}\n\t\tfunction handleAuthRejected(data: any) {\n\t\t\tconst result = AuthRejectedEventSchema.safeParse({\n\t\t\t\ttype: EVENTS.AUTH_REJECTED,\n\t\t\t\t...data,\n\t\t\t})\n\t\t\tif (result.success) {\n\t\t\t\tsend({ data: JSON.stringify(result.data) })\n\t\t\t} else {\n\t\t\t\tconsole.error('Error parsing auth rejected event', result.error, data)\n\t\t\t}\n\t\t}\n\t\tauthEmitter.on(EVENTS.USER_CODE_RECEIVED, handleCodeReceived)\n\t\tauthEmitter.on(EVENTS.AUTH_RESOLVED, handleAuthResolved)\n\t\tauthEmitter.on(EVENTS.AUTH_REJECTED, handleAuthRejected)\n\t\treturn () => {\n\t\t\tauthEmitter.off(EVENTS.USER_CODE_RECEIVED, handleCodeReceived)\n\t\t\tauthEmitter.off(EVENTS.AUTH_RESOLVED, handleAuthResolved)\n\t\t\tauthEmitter.off(EVENTS.AUTH_REJECTED, handleAuthRejected)\n\t\t}\n\t})\n}\n","import { getAuthInfo } from '@epic-web/workshop-utils/db.server'\nimport { type SEOHandle } from '@nasa-gcn/remix-seo'\nimport { redirect } from '@remix-run/node'\nimport { Link, useFetcher, useNavigate, useRevalidator } from '@remix-run/react'\nimport { useEffect, useState } from 'react'\nimport { useEventSource } from 'remix-utils/sse/react'\nimport { Button, ButtonLink } from '#app/components/button.tsx'\nimport { Loading } from '#app/components/loading.tsx'\nimport { Logo } from '#app/components/product.tsx'\nimport { useWorkshopConfig } from '#app/components/workshop-config.js'\nimport { EVENTS } from '#app/utils/auth-events.ts'\nimport { registerDevice } from '#app/utils/auth.server.ts'\nimport { ensureUndeployed } from '#app/utils/misc.tsx'\nimport { EventSchema } from '../login-sse.tsx'\n\nexport const handle: SEOHandle = {\n\tgetSitemapEntries: () => null,\n}\n\nexport async function loader() {\n\tensureUndeployed()\n\tconst isAuthenticated = Boolean(await getAuthInfo())\n\tif (isAuthenticated) throw redirect('/account')\n\treturn {}\n}\n\nexport async function action() {\n\tensureUndeployed()\n\tvoid registerDevice()\n\treturn { status: 'pending' } as const\n}\n\nexport default function Login() {\n\tconst {\n\t\tproduct: { displayName },\n\t} = useWorkshopConfig()\n\tconst loginFetcher = useFetcher<typeof action>()\n\tconst [clickedVerificationLink, setClickedVerificationLink] = useState(false)\n\tconst [authError, setAuthError] = useState<null | string>(null)\n\tconst [userCodeInfo, setUserCodeInfo] = useState<null | {\n\t\tcode: string\n\t\turl: string\n\t}>(null)\n\tconst navigate = useNavigate()\n\tconst revalidator = useRevalidator()\n\tconst lastMessage = useEventSource(`/login-sse`)\n\tuseEffect(() => {\n\t\tif (!lastMessage) return\n\n\t\tconst parsed = JSON.parse(lastMessage)\n\t\tconst result = EventSchema.safeParse(parsed)\n\t\tif (!result.success) {\n\t\t\tconsole.error(result.error.flatten())\n\t\t\treturn\n\t\t}\n\t\tswitch (result.data.type) {\n\t\t\tcase EVENTS.USER_CODE_RECEIVED: {\n\t\t\t\tsetUserCodeInfo(result.data)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase EVENTS.AUTH_RESOLVED: {\n\t\t\t\trevalidator.revalidate()\n\t\t\t\tnavigate('/')\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase EVENTS.AUTH_REJECTED: {\n\t\t\t\tsetAuthError(result.data.error)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}, [lastMessage, navigate, revalidator])\n\n\treturn (\n\t\t<main className=\"flex h-full w-full flex-grow flex-col items-center justify-center p-10\">\n\t\t\t<div className=\"flex flex-col items-center\">\n\t\t\t\t<Logo className=\"h-16 w-16\" />\n\t\t\t\t<h1 className=\"pt-5 text-2xl font-semibold md:text-3xl\">\n\t\t\t\t\tLogin to {displayName}\n\t\t\t\t</h1>\n\t\t\t\t<div className=\"flex w-full flex-col items-center pt-5\">\n\t\t\t\t\t{userCodeInfo ? (\n\t\t\t\t\t\t<div className=\"flex w-full max-w-md flex-col items-center gap-3\">\n\t\t\t\t\t\t\t<div className=\"my-2 flex w-full flex-col items-center gap-2\">\n\t\t\t\t\t\t\t\t<p className=\"text-lg\">Your verification code is: </p>\n\t\t\t\t\t\t\t\t<div className=\"mb-3 w-full bg-gray-100 px-5 py-3 text-center text-lg font-bold dark:bg-black/40\">\n\t\t\t\t\t\t\t\t\t<code>{userCodeInfo.code}</code>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<p className=\"text-base\">\n\t\t\t\t\t\t\t\t\tYou'll use this to verify your device on {displayName}. Click\n\t\t\t\t\t\t\t\t\tverify code below to open the verification page.\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<ButtonLink\n\t\t\t\t\t\t\t\tvarient=\"primary\"\n\t\t\t\t\t\t\t\tto={userCodeInfo.url}\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noreferrer\"\n\t\t\t\t\t\t\t\tonClick={() => setClickedVerificationLink(true)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tVerify Auth Code\n\t\t\t\t\t\t\t</ButtonLink>\n\t\t\t\t\t\t\t{clickedVerificationLink ? (\n\t\t\t\t\t\t\t\t<div className=\"justify-center pt-5 text-center opacity-60\">\n\t\t\t\t\t\t\t\t\t<Loading className=\"justify-center\">\n\t\t\t\t\t\t\t\t\t\tWaiting for confirmation\n\t\t\t\t\t\t\t\t\t</Loading>\n\t\t\t\t\t\t\t\t\t<p className=\"pt-2\">\n\t\t\t\t\t\t\t\t\t\tPlease open{' '}\n\t\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\t\thref={userCodeInfo.url}\n\t\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"underline\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\tyour auth page\n\t\t\t\t\t\t\t\t\t\t</a>{' '}\n\t\t\t\t\t\t\t\t\t\tin a new tab to continue.\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<div className=\"flex flex-col items-center gap-8\">\n\t\t\t\t\t\t\t<div className=\"flex max-w-lg flex-col gap-3 pt-3 text-base text-gray-700 dark:text-gray-300\">\n\t\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\t\tIf you have access to this workshop on {displayName}, you'll\n\t\t\t\t\t\t\t\t\tbe able to watch videos, track progress, run tests, view the\n\t\t\t\t\t\t\t\t\tdiffs, and more!\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\t\tFirst you need to authenticate your device by requesting an\n\t\t\t\t\t\t\t\t\taccess code and verifying on {displayName}.\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<loginFetcher.Form method=\"POST\">\n\t\t\t\t\t\t\t\t<Button varient=\"primary\" type=\"submit\">\n\t\t\t\t\t\t\t\t\t{loginFetcher.state === 'idle' &&\n\t\t\t\t\t\t\t\t\tloginFetcher.data?.status !== 'pending'\n\t\t\t\t\t\t\t\t\t\t? `Retrieve Auth Code`\n\t\t\t\t\t\t\t\t\t\t: `Retrieving Auth Code...`}\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</loginFetcher.Form>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t{authError ? (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<div className=\"mt-4 text-red-500\">\n\t\t\t\t\t\t\t\tThere was an error: <pre>{authError}</pre>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div className=\"mt-4 text-red-500\">\n\t\t\t\t\t\t\t\tPlease try again or{' '}\n\t\t\t\t\t\t\t\t<Link to=\"/support\" className=\"underline\">\n\t\t\t\t\t\t\t\t\tcontact support\n\t\t\t\t\t\t\t\t</Link>{' '}\n\t\t\t\t\t\t\t\tif the problem persists.\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</main>\n\t)\n}\n"],"names":["EVENTS","CodeReceivedEventSchema","z","object","type","literal","USER_CODE_RECEIVED","code","string","url","AuthResolvedEventSchema","AUTH_RESOLVED","AuthRejectedEventSchema","AUTH_REJECTED","error","optional","default","EventSchema","union","handle","getSitemapEntries","Login","product","displayName","useWorkshopConfig","loginFetcher","useFetcher","clickedVerificationLink","setClickedVerificationLink","useState","authError","setAuthError","userCodeInfo","setUserCodeInfo","navigate","useNavigate","revalidator","useRevalidator","lastMessage","useEventSource","useEffect","parsed","JSON","parse","result","safeParse","success","console","flatten","data","revalidate","className","children","jsxs","jsx","Logo","ButtonLink","varient","to","target","rel","onClick","Loading","href","Form","method","Button","state","status","Fragment","Link"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"login-DiEHnGfv.js","sources":["../../../app/utils/auth-events.ts","../../../app/routes/login-sse.tsx","../../../app/routes/_app+/login.tsx"],"sourcesContent":["export const EVENTS = {\n\tUSER_CODE_RECEIVED: 'USER_CODE_RECEIVED',\n\tAUTH_RESOLVED: 'AUTH_RESOLVED',\n\tAUTH_REJECTED: 'AUTH_REJECTED',\n} as const\nexport type EventTypes = keyof typeof EVENTS\n","import { type LoaderFunctionArgs } from '@remix-run/node'\nimport { eventStream } from 'remix-utils/sse/server'\nimport { z } from 'zod'\nimport { EVENTS } from '#app/utils/auth-events.ts'\nimport { authEmitter } from '#app/utils/auth.server.ts'\nimport { ensureUndeployed } from '#app/utils/misc.tsx'\n\nconst CodeReceivedEventSchema = z.object({\n\ttype: z.literal(EVENTS.USER_CODE_RECEIVED),\n\tcode: z.string(),\n\turl: z.string(),\n})\nconst AuthResolvedEventSchema = z.object({\n\ttype: z.literal(EVENTS.AUTH_RESOLVED),\n})\nconst AuthRejectedEventSchema = z.object({\n\ttype: z.literal(EVENTS.AUTH_REJECTED),\n\terror: z.string().optional().default('Unknown error'),\n})\nexport const EventSchema = z.union([\n\tCodeReceivedEventSchema,\n\tAuthResolvedEventSchema,\n\tAuthRejectedEventSchema,\n])\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n\tensureUndeployed()\n\treturn eventStream(request.signal, function setup(send) {\n\t\tfunction handleCodeReceived(data: any) {\n\t\t\tsend({\n\t\t\t\tdata: JSON.stringify(\n\t\t\t\t\tCodeReceivedEventSchema.parse({\n\t\t\t\t\t\ttype: EVENTS.USER_CODE_RECEIVED,\n\t\t\t\t\t\t...data,\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t})\n\t\t}\n\t\tfunction handleAuthResolved() {\n\t\t\tsend({ data: JSON.stringify({ type: EVENTS.AUTH_RESOLVED }) })\n\t\t}\n\t\tfunction handleAuthRejected(data: any) {\n\t\t\tconst result = AuthRejectedEventSchema.safeParse({\n\t\t\t\ttype: EVENTS.AUTH_REJECTED,\n\t\t\t\t...data,\n\t\t\t})\n\t\t\tif (result.success) {\n\t\t\t\tsend({ data: JSON.stringify(result.data) })\n\t\t\t} else {\n\t\t\t\tconsole.error('Error parsing auth rejected event', result.error, data)\n\t\t\t}\n\t\t}\n\t\tauthEmitter.on(EVENTS.USER_CODE_RECEIVED, handleCodeReceived)\n\t\tauthEmitter.on(EVENTS.AUTH_RESOLVED, handleAuthResolved)\n\t\tauthEmitter.on(EVENTS.AUTH_REJECTED, handleAuthRejected)\n\t\treturn () => {\n\t\t\tauthEmitter.off(EVENTS.USER_CODE_RECEIVED, handleCodeReceived)\n\t\t\tauthEmitter.off(EVENTS.AUTH_RESOLVED, handleAuthResolved)\n\t\t\tauthEmitter.off(EVENTS.AUTH_REJECTED, handleAuthRejected)\n\t\t}\n\t})\n}\n","import { getAuthInfo } from '@epic-web/workshop-utils/db.server'\nimport { type SEOHandle } from '@nasa-gcn/remix-seo'\nimport { redirect } from '@remix-run/node'\nimport { Link, useFetcher, useNavigate, useRevalidator } from '@remix-run/react'\nimport { useEffect, useState } from 'react'\nimport { useEventSource } from 'remix-utils/sse/react'\nimport { Button, ButtonLink } from '#app/components/button.tsx'\nimport { Loading } from '#app/components/loading.tsx'\nimport { Logo } from '#app/components/product.tsx'\nimport { useWorkshopConfig } from '#app/components/workshop-config.js'\nimport { EVENTS } from '#app/utils/auth-events.ts'\nimport { registerDevice } from '#app/utils/auth.server.ts'\nimport { ensureUndeployed } from '#app/utils/misc.tsx'\nimport { EventSchema } from '../login-sse.tsx'\n\nexport const handle: SEOHandle = {\n\tgetSitemapEntries: () => null,\n}\n\nexport async function loader() {\n\tensureUndeployed()\n\tconst isAuthenticated = Boolean(await getAuthInfo())\n\tif (isAuthenticated) throw redirect('/account')\n\treturn {}\n}\n\nexport async function action() {\n\tensureUndeployed()\n\tvoid registerDevice()\n\treturn { status: 'pending' } as const\n}\n\nexport default function Login() {\n\tconst {\n\t\tproduct: { displayName },\n\t} = useWorkshopConfig()\n\tconst loginFetcher = useFetcher<typeof action>()\n\tconst [clickedVerificationLink, setClickedVerificationLink] = useState(false)\n\tconst [authError, setAuthError] = useState<null | string>(null)\n\tconst [userCodeInfo, setUserCodeInfo] = useState<null | {\n\t\tcode: string\n\t\turl: string\n\t}>(null)\n\tconst navigate = useNavigate()\n\tconst revalidator = useRevalidator()\n\tconst lastMessage = useEventSource(`/login-sse`)\n\tuseEffect(() => {\n\t\tif (!lastMessage) return\n\n\t\tconst parsed = JSON.parse(lastMessage)\n\t\tconst result = EventSchema.safeParse(parsed)\n\t\tif (!result.success) {\n\t\t\tconsole.error(result.error.flatten())\n\t\t\treturn\n\t\t}\n\t\tswitch (result.data.type) {\n\t\t\tcase EVENTS.USER_CODE_RECEIVED: {\n\t\t\t\tsetUserCodeInfo(result.data)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase EVENTS.AUTH_RESOLVED: {\n\t\t\t\trevalidator.revalidate()\n\t\t\t\tnavigate('/')\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase EVENTS.AUTH_REJECTED: {\n\t\t\t\tsetAuthError(result.data.error)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}, [lastMessage, navigate, revalidator])\n\n\treturn (\n\t\t<main className=\"flex h-full w-full flex-grow flex-col items-center justify-center p-10\">\n\t\t\t<div className=\"flex flex-col items-center\">\n\t\t\t\t<Logo className=\"h-16 w-16\" />\n\t\t\t\t<h1 className=\"pt-5 text-2xl font-semibold md:text-3xl\">\n\t\t\t\t\tLogin to {displayName}\n\t\t\t\t</h1>\n\t\t\t\t<div className=\"flex w-full flex-col items-center pt-5\">\n\t\t\t\t\t{userCodeInfo ? (\n\t\t\t\t\t\t<div className=\"flex w-full max-w-md flex-col items-center gap-3\">\n\t\t\t\t\t\t\t<div className=\"my-2 flex w-full flex-col items-center gap-2\">\n\t\t\t\t\t\t\t\t<p className=\"text-lg\">Your verification code is: </p>\n\t\t\t\t\t\t\t\t<div className=\"mb-3 w-full bg-gray-100 px-5 py-3 text-center text-lg font-bold dark:bg-black/40\">\n\t\t\t\t\t\t\t\t\t<code>{userCodeInfo.code}</code>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<p className=\"text-base\">\n\t\t\t\t\t\t\t\t\tYou'll use this to verify your device on {displayName}. Click\n\t\t\t\t\t\t\t\t\tverify code below to open the verification page.\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<ButtonLink\n\t\t\t\t\t\t\t\tvarient=\"primary\"\n\t\t\t\t\t\t\t\tto={userCodeInfo.url}\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noreferrer\"\n\t\t\t\t\t\t\t\tonClick={() => setClickedVerificationLink(true)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tVerify Auth Code\n\t\t\t\t\t\t\t</ButtonLink>\n\t\t\t\t\t\t\t{clickedVerificationLink ? (\n\t\t\t\t\t\t\t\t<div className=\"justify-center pt-5 text-center opacity-60\">\n\t\t\t\t\t\t\t\t\t<Loading className=\"justify-center\">\n\t\t\t\t\t\t\t\t\t\tWaiting for confirmation\n\t\t\t\t\t\t\t\t\t</Loading>\n\t\t\t\t\t\t\t\t\t<p className=\"pt-2\">\n\t\t\t\t\t\t\t\t\t\tPlease open{' '}\n\t\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\t\thref={userCodeInfo.url}\n\t\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"underline\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\tyour auth page\n\t\t\t\t\t\t\t\t\t\t</a>{' '}\n\t\t\t\t\t\t\t\t\t\tin a new tab to continue.\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<div className=\"flex flex-col items-center gap-8\">\n\t\t\t\t\t\t\t<div className=\"flex max-w-lg flex-col gap-3 pt-3 text-base text-gray-700 dark:text-gray-300\">\n\t\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\t\tIf you have access to this workshop on {displayName}, you'll\n\t\t\t\t\t\t\t\t\tbe able to watch videos, track progress, run tests, view the\n\t\t\t\t\t\t\t\t\tdiffs, and more!\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\t\tFirst you need to authenticate your device by requesting an\n\t\t\t\t\t\t\t\t\taccess code and verifying on {displayName}.\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<loginFetcher.Form method=\"POST\">\n\t\t\t\t\t\t\t\t<Button varient=\"primary\" type=\"submit\">\n\t\t\t\t\t\t\t\t\t{loginFetcher.state === 'idle' &&\n\t\t\t\t\t\t\t\t\tloginFetcher.data?.status !== 'pending'\n\t\t\t\t\t\t\t\t\t\t? `Retrieve Auth Code`\n\t\t\t\t\t\t\t\t\t\t: `Retrieving Auth Code...`}\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</loginFetcher.Form>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t{authError ? (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<div className=\"mt-4 text-red-500\">\n\t\t\t\t\t\t\t\tThere was an error: <pre>{authError}</pre>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div className=\"mt-4 text-red-500\">\n\t\t\t\t\t\t\t\tPlease try again or{' '}\n\t\t\t\t\t\t\t\t<Link to=\"/support\" className=\"underline\">\n\t\t\t\t\t\t\t\t\tcontact support\n\t\t\t\t\t\t\t\t</Link>{' '}\n\t\t\t\t\t\t\t\tif the problem persists.\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</main>\n\t)\n}\n"],"names":["EVENTS","CodeReceivedEventSchema","z","object","type","literal","USER_CODE_RECEIVED","code","string","url","AuthResolvedEventSchema","AUTH_RESOLVED","AuthRejectedEventSchema","AUTH_REJECTED","error","optional","default","EventSchema","union","handle","getSitemapEntries","Login","product","displayName","useWorkshopConfig","loginFetcher","useFetcher","clickedVerificationLink","setClickedVerificationLink","useState","authError","setAuthError","userCodeInfo","setUserCodeInfo","navigate","useNavigate","revalidator","useRevalidator","lastMessage","useEventSource","useEffect","parsed","JSON","parse","result","safeParse","success","console","flatten","data","revalidate","className","children","jsxs","jsx","Logo","ButtonLink","varient","to","target","rel","onClick","Loading","href","Form","method","Button","state","status","Fragment","Link"],"mappings":"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"}
|