@epic-web/workshop-app 6.72.0 → 6.72.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.
Files changed (42) hide show
  1. package/build/client/assets/{_-dWSrc4Ox.js → _-C4AWl_1u.js} +2 -2
  2. package/build/client/assets/{_-dWSrc4Ox.js.map → _-C4AWl_1u.js.map} +1 -1
  3. package/build/client/assets/{_exerciseNumber-FdJ9Amz3.js → _exerciseNumber-DUwXPNgl.js} +2 -2
  4. package/build/client/assets/{_exerciseNumber-FdJ9Amz3.js.map → _exerciseNumber-DUwXPNgl.js.map} +1 -1
  5. package/build/client/assets/{_exerciseNumber_._stepNumber.index-WLW-sVZ0.js → _exerciseNumber_._stepNumber.index-BPqMtmN8.js} +2 -2
  6. package/build/client/assets/{_exerciseNumber_._stepNumber.index-WLW-sVZ0.js.map → _exerciseNumber_._stepNumber.index-BPqMtmN8.js.map} +1 -1
  7. package/build/client/assets/{_exerciseNumber_.finished-Z7QZ9wk-.js → _exerciseNumber_.finished-C4sVkuEq.js} +2 -2
  8. package/build/client/assets/{_exerciseNumber_.finished-Z7QZ9wk-.js.map → _exerciseNumber_.finished-C4sVkuEq.js.map} +1 -1
  9. package/build/client/assets/{_extra-UkIdJCI9.js → _extra-DkiDFYVP.js} +2 -2
  10. package/build/client/assets/{_extra-UkIdJCI9.js.map → _extra-DkiDFYVP.js.map} +1 -1
  11. package/build/client/assets/{_layout-CjWZzKD8.js → _layout-C9vSX5Ti.js} +2 -2
  12. package/build/client/assets/{_layout-CjWZzKD8.js.map → _layout-C9vSX5Ti.js.map} +1 -1
  13. package/build/client/assets/{_layout-DQM0capK.js → _layout-DDTXkx2z.js} +2 -2
  14. package/build/client/assets/{_layout-DQM0capK.js.map → _layout-DDTXkx2z.js.map} +1 -1
  15. package/build/client/assets/{entry.client-DSlySyW6.js → entry.client-DdbTov6T.js} +3 -3
  16. package/build/client/assets/{entry.client-DSlySyW6.js.map → entry.client-DdbTov6T.js.map} +1 -1
  17. package/build/client/assets/{error-boundary-DVp6wKiz.js → error-boundary-DxHqAEHX.js} +3 -3
  18. package/build/client/assets/{error-boundary-DVp6wKiz.js.map → error-boundary-DxHqAEHX.js.map} +1 -1
  19. package/build/client/assets/{finished-PFz2gTpr.js → finished-BW_4ow3n.js} +2 -2
  20. package/build/client/assets/{finished-PFz2gTpr.js.map → finished-BW_4ow3n.js.map} +1 -1
  21. package/build/client/assets/{index-DkDXAMak.js → index-BgExztV_.js} +2 -2
  22. package/build/client/assets/{index-DkDXAMak.js.map → index-BgExztV_.js.map} +1 -1
  23. package/build/client/assets/{index-DuFMeXSm.js → index-DroM8fzS.js} +2 -2
  24. package/build/client/assets/{index-DuFMeXSm.js.map → index-DroM8fzS.js.map} +1 -1
  25. package/build/client/assets/{index-CATk0s1i.js → index-IgBUhsxX.js} +2 -2
  26. package/build/client/assets/{index-CATk0s1i.js.map → index-IgBUhsxX.js.map} +1 -1
  27. package/build/client/assets/{index-DfT8l3cx.js → index-PKjGT1Rp.js} +2 -2
  28. package/build/client/assets/{index-DfT8l3cx.js.map → index-PKjGT1Rp.js.map} +1 -1
  29. package/build/client/assets/index.client-CsIUPfXc.js +500 -0
  30. package/build/client/assets/index.client-CsIUPfXc.js.map +1 -0
  31. package/build/client/assets/{manifest-6451f320.js → manifest-9e0c0428.js} +1 -1
  32. package/build/client/assets/progress-BcA4i0iU.js +2 -0
  33. package/build/client/assets/progress-BcA4i0iU.js.map +1 -0
  34. package/build/client/assets/{root-CB01jtcy.js → root-CKcj2hDc.js} +3 -3
  35. package/build/client/assets/{root-CB01jtcy.js.map → root-CKcj2hDc.js.map} +1 -1
  36. package/build/server/index.js +16 -2
  37. package/build/server/index.js.map +1 -1
  38. package/package.json +9 -9
  39. package/build/client/assets/index.client-B5fbXzJV.js +0 -500
  40. package/build/client/assets/index.client-B5fbXzJV.js.map +0 -1
  41. package/build/client/assets/progress-ILaVQtOO.js +0 -2
  42. package/build/client/assets/progress-ILaVQtOO.js.map +0 -1
@@ -1,2 +1,2 @@
1
- import{w as e,a as s,u as t,L as n}from"./chunk-EPOLDU6W-BCLmut3y.js";import{j as r}from"./jsx-runtime-C5WNSv3b.js";import{G as i}from"./error-boundary-DVp6wKiz.js";import{I as l}from"./misc-W4055b-0.js";import"./index-CqIc3cxq.js";import"./preload-helper-BXl3LOEh.js";const h=e(function(){return r.jsx(c,{})}),c=s(function(){const a=t();return r.jsx(i,{statusHandlers:{404:()=>r.jsxs("div",{className:"flex flex-col gap-6",children:[r.jsxs("div",{className:"flex flex-col gap-3",children:[r.jsx("h1",{children:"We can't find this page:"}),r.jsx("pre",{className:"text-body-lg break-all whitespace-pre-wrap",children:a.pathname})]}),r.jsx(n,{to:"/",className:"text-body-md underline",children:r.jsx(l,{name:"ArrowLeft",children:"Back to home"})})]})}})});export{c as ErrorBoundary,h as default};
2
- //# sourceMappingURL=_-dWSrc4Ox.js.map
1
+ import{w as e,a as s,u as t,L as n}from"./chunk-EPOLDU6W-BCLmut3y.js";import{j as r}from"./jsx-runtime-C5WNSv3b.js";import{G as i}from"./error-boundary-DxHqAEHX.js";import{I as l}from"./misc-W4055b-0.js";import"./index-CqIc3cxq.js";import"./preload-helper-BXl3LOEh.js";const h=e(function(){return r.jsx(c,{})}),c=s(function(){const a=t();return r.jsx(i,{statusHandlers:{404:()=>r.jsxs("div",{className:"flex flex-col gap-6",children:[r.jsxs("div",{className:"flex flex-col gap-3",children:[r.jsx("h1",{children:"We can't find this page:"}),r.jsx("pre",{className:"text-body-lg break-all whitespace-pre-wrap",children:a.pathname})]}),r.jsx(n,{to:"/",className:"text-body-md underline",children:r.jsx(l,{name:"ArrowLeft",children:"Back to home"})})]})}})});export{c as ErrorBoundary,h as default};
2
+ //# sourceMappingURL=_-C4AWl_1u.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"_-dWSrc4Ox.js","sources":["../../../app/routes/$.tsx"],"sourcesContent":["// This is called a \"splat route\" and as it's in the root `/app/routes/`\n// directory, it's a catchall. If no other routes match, this one will and we\n// can know that the user is hitting a URL that doesn't exist. By throwing a\n// 404 from the loader, we can force the error boundary to render which will\n// ensure the user gets the right status code and we can display a nicer error\n// message for them than the Remix and/or browser default.\n\nimport { type LoaderFunctionArgs, Link, useLocation } from 'react-router'\nimport { GeneralErrorBoundary } from '#app/components/error-boundary.tsx'\nimport { Icon } from '#app/components/icons.tsx'\n\nexport async function loader({ params }: LoaderFunctionArgs) {\n\tconst splat = params['*']\n\tconst segments = splat?.split('/') ?? []\n\tif (segments.length > 0 && !isNaN(Number(segments[0]))) {\n\t\tconst newPath = `/exercise/${splat}`\n\t\treturn new Response(null, {\n\t\t\tstatus: 302,\n\t\t\theaders: {\n\t\t\t\tLocation: newPath,\n\t\t\t},\n\t\t})\n\t}\n\n\tthrow new Response('Not found', { status: 404 })\n}\n\nexport default function NotFound() {\n\t// due to the loader, this component will never be rendered, but we'll return\n\t// the error boundary just in case.\n\treturn <ErrorBoundary />\n}\n\nexport function ErrorBoundary() {\n\tconst location = useLocation()\n\treturn (\n\t\t<GeneralErrorBoundary\n\t\t\tstatusHandlers={{\n\t\t\t\t404: () => (\n\t\t\t\t\t<div className=\"flex flex-col gap-6\">\n\t\t\t\t\t\t<div className=\"flex flex-col gap-3\">\n\t\t\t\t\t\t\t<h1>We can't find this page:</h1>\n\t\t\t\t\t\t\t<pre className=\"text-body-lg break-all whitespace-pre-wrap\">\n\t\t\t\t\t\t\t\t{location.pathname}\n\t\t\t\t\t\t\t</pre>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<Link to=\"/\" className=\"text-body-md underline\">\n\t\t\t\t\t\t\t<Icon name=\"ArrowLeft\">Back to home</Icon>\n\t\t\t\t\t\t</Link>\n\t\t\t\t\t</div>\n\t\t\t\t),\n\t\t\t}}\n\t\t/>\n\t)\n}\n"],"names":["$","_UNSAFE_withComponentProps","ErrorBoundary","_UNSAFE_withErrorBoundaryProps","location","useLocation","jsx","GeneralErrorBoundary","statusHandlers","jsxs","className","children","pathname","Link","to","Icon","name"],"mappings":"6QA2BA,MAAAA,EAAAC,EAAA,UAAmC,CAGlC,aAAQC,EAAA,EAAc,CACvB,CAAA,EAEOA,EAAAC,EAAA,UAAyB,CAC/B,MAAMC,EAAWC,EAAA,EACjB,OACCC,EAAAA,IAACC,EAAA,CACAC,eAAgB,CACf,IAAK,IACJC,EAAAA,KAAC,MAAA,CAAIC,UAAU,sBACdC,SAAA,CAAAF,EAAAA,KAAC,MAAA,CAAIC,UAAU,sBACdC,SAAA,CAAAL,EAAAA,IAAC,MAAGK,SAAA,0BAAA,CAAwB,EAC5BL,EAAAA,IAAC,MAAA,CAAII,UAAU,6CACbC,WAASC,QAAA,CACX,CAAA,CAAA,CACD,EACAN,EAAAA,IAACO,EAAA,CAAKC,GAAG,IAAIJ,UAAU,yBACtBC,SAAAL,EAAAA,IAACS,EAAA,CAAKC,KAAK,YAAYL,SAAA,eAAY,CAAA,CACpC,CAAA,EACD,CAEF,CAAA,CACD,CAEF,CAAA"}
1
+ {"version":3,"file":"_-C4AWl_1u.js","sources":["../../../app/routes/$.tsx"],"sourcesContent":["// This is called a \"splat route\" and as it's in the root `/app/routes/`\n// directory, it's a catchall. If no other routes match, this one will and we\n// can know that the user is hitting a URL that doesn't exist. By throwing a\n// 404 from the loader, we can force the error boundary to render which will\n// ensure the user gets the right status code and we can display a nicer error\n// message for them than the Remix and/or browser default.\n\nimport { type LoaderFunctionArgs, Link, useLocation } from 'react-router'\nimport { GeneralErrorBoundary } from '#app/components/error-boundary.tsx'\nimport { Icon } from '#app/components/icons.tsx'\n\nexport async function loader({ params }: LoaderFunctionArgs) {\n\tconst splat = params['*']\n\tconst segments = splat?.split('/') ?? []\n\tif (segments.length > 0 && !isNaN(Number(segments[0]))) {\n\t\tconst newPath = `/exercise/${splat}`\n\t\treturn new Response(null, {\n\t\t\tstatus: 302,\n\t\t\theaders: {\n\t\t\t\tLocation: newPath,\n\t\t\t},\n\t\t})\n\t}\n\n\tthrow new Response('Not found', { status: 404 })\n}\n\nexport default function NotFound() {\n\t// due to the loader, this component will never be rendered, but we'll return\n\t// the error boundary just in case.\n\treturn <ErrorBoundary />\n}\n\nexport function ErrorBoundary() {\n\tconst location = useLocation()\n\treturn (\n\t\t<GeneralErrorBoundary\n\t\t\tstatusHandlers={{\n\t\t\t\t404: () => (\n\t\t\t\t\t<div className=\"flex flex-col gap-6\">\n\t\t\t\t\t\t<div className=\"flex flex-col gap-3\">\n\t\t\t\t\t\t\t<h1>We can't find this page:</h1>\n\t\t\t\t\t\t\t<pre className=\"text-body-lg break-all whitespace-pre-wrap\">\n\t\t\t\t\t\t\t\t{location.pathname}\n\t\t\t\t\t\t\t</pre>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<Link to=\"/\" className=\"text-body-md underline\">\n\t\t\t\t\t\t\t<Icon name=\"ArrowLeft\">Back to home</Icon>\n\t\t\t\t\t\t</Link>\n\t\t\t\t\t</div>\n\t\t\t\t),\n\t\t\t}}\n\t\t/>\n\t)\n}\n"],"names":["$","_UNSAFE_withComponentProps","ErrorBoundary","_UNSAFE_withErrorBoundaryProps","location","useLocation","jsx","GeneralErrorBoundary","statusHandlers","jsxs","className","children","pathname","Link","to","Icon","name"],"mappings":"6QA2BA,MAAAA,EAAAC,EAAA,UAAmC,CAGlC,aAAQC,EAAA,EAAc,CACvB,CAAA,EAEOA,EAAAC,EAAA,UAAyB,CAC/B,MAAMC,EAAWC,EAAA,EACjB,OACCC,EAAAA,IAACC,EAAA,CACAC,eAAgB,CACf,IAAK,IACJC,EAAAA,KAAC,MAAA,CAAIC,UAAU,sBACdC,SAAA,CAAAF,EAAAA,KAAC,MAAA,CAAIC,UAAU,sBACdC,SAAA,CAAAL,EAAAA,IAAC,MAAGK,SAAA,0BAAA,CAAwB,EAC5BL,EAAAA,IAAC,MAAA,CAAII,UAAU,6CACbC,WAASC,QAAA,CACX,CAAA,CAAA,CACD,EACAN,EAAAA,IAACO,EAAA,CAAKC,GAAG,IAAIJ,UAAU,yBACtBC,SAAAL,EAAAA,IAACS,EAAA,CAAKC,KAAK,YAAYL,SAAA,eAAY,CAAA,CACpC,CAAA,EACD,CAEF,CAAA,CACD,CAEF,CAAA"}
@@ -1,2 +1,2 @@
1
- import{w as s,a as n,L as c}from"./chunk-EPOLDU6W-BCLmut3y.js";import{j as r}from"./jsx-runtime-C5WNSv3b.js";import{E as m}from"./index-CEVyDj51.js";import{E as l}from"./epic-video-DnXXh6qE.js";import{G as p}from"./error-boundary-DVp6wKiz.js";import{u as a}from"./revalidation-ws-BJWJviUX.js";import{E as x}from"./launch-editor-D2exGfVu.js";import{P as u}from"./progress-ILaVQtOO.js";import{M as f}from"./mdx-Dun3ONG_.js";import{g as d}from"./root-loader-BOzEMapJ.js";import{g as b}from"./seo-t5J-DRxw.js";import{E as h}from"./error-boundary-BDqWjIFP.js";import"./index-CqIc3cxq.js";import"./use-event-source-BuD4_2SF.js";import"./index-DzdDahau.js";import"./index-vDCSPjrM.js";import"./index-CdzVFL-Z.js";import"./misc-W4055b-0.js";import"./tooltip-Tlsyx2YO.js";import"./pe-CIZUOJMr.js";import"./schemas-Uj5SZtvt.js";import"./online-DiNLkgTC.js";import"./loading-CDNzW5oO.js";import"./user-BsPobzjB.js";import"./workshop-config-Zfc8zU0x.js";import"./preload-helper-BXl3LOEh.js";import"./progress-bar-DpWhcyhC.js";import"./chunk-FNSCYPCZ-BHgC5cdx.js";import"./coerce-CkHW0SMv.js";const K=({loaderData:t,matches:e})=>{const o=t?.exercise.exerciseNumber.toString().padStart(2,"0"),i=d(e);return!t||!i?[{title:"🦉 | Error"}]:b({title:`📝 | ${o}. ${t.exercise.title} | ${i?.workshopTitle}`,description:`Introduction for ${o}. ${t.exercise.title}`,ogTitle:t.exercise.title,ogDescription:`Introduction for exercise ${Number(o)}`,instructor:i.instructor,requestInfo:i.requestInfo})},g={h1:()=>null},U=s(function({loaderData:e}){a({watchPaths:[e.exerciseReadme.file]});const o=String(e.firstStep?.stepNumber??"01").padStart(2,"0");return r.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:[r.jsxs("article",{id:e.articleId,className:"shadow-on-scrollbox scrollbar-thin scrollbar-thumb-scrollbar flex w-full flex-1 flex-col gap-12 overflow-y-scroll px-3 py-4 pt-6 md:px-10 md:py-12 md:pt-16",children:[r.jsx("div",{children:r.jsx("h1",{className:"text-[clamp(3rem,6vw,7.5rem)] leading-none font-extrabold",children:e.exercise.title})}),r.jsx("div",{children:e.exercise.instructionsCode?r.jsx(l,{epicVideoInfosPromise:e.epicVideoInfosPromise,children:r.jsx("div",{className:"prose dark:prose-invert sm:prose-lg",children:r.jsx(f,{code:e.exercise.instructionsCode,components:g})})}):"No instructions yet..."})]},e.articleId),r.jsx(m,{elementQuery:`#${e.articleId}`},`scroll-${e.articleId}`),r.jsx(u,{type:"instructions",exerciseNumber:e.exerciseNumber,className:"h-14 border-t px-6"}),r.jsxs("div",{className:"@container flex h-16 justify-between border-t border-b-4 lg:border-b-0",children:[r.jsx("div",{}),r.jsx(x,{file:e.exerciseReadme.file,relativePath:e.exerciseReadme.relativePath}),r.jsx(c,{to:`${o}/${e.firstType}`,prefetch:"intent",className:"bg-foreground text-background flex h-full items-center justify-center px-7","data-keyboard-action":"g+n",children:"Start Learning"})]})]})}),X=n(function(){return r.jsx(p,{className:"container flex items-center justify-center",statusHandlers:{404:h}})});export{X as ErrorBoundary,U as default,K as meta};
2
- //# sourceMappingURL=_exerciseNumber-FdJ9Amz3.js.map
1
+ import{w as s,a as n,L as c}from"./chunk-EPOLDU6W-BCLmut3y.js";import{j as r}from"./jsx-runtime-C5WNSv3b.js";import{E as m}from"./index-CEVyDj51.js";import{E as l}from"./epic-video-DnXXh6qE.js";import{G as p}from"./error-boundary-DxHqAEHX.js";import{u as a}from"./revalidation-ws-BJWJviUX.js";import{E as x}from"./launch-editor-D2exGfVu.js";import{P as u}from"./progress-BcA4i0iU.js";import{M as f}from"./mdx-Dun3ONG_.js";import{g as d}from"./root-loader-BOzEMapJ.js";import{g as b}from"./seo-t5J-DRxw.js";import{E as h}from"./error-boundary-BDqWjIFP.js";import"./index-CqIc3cxq.js";import"./use-event-source-BuD4_2SF.js";import"./index-DzdDahau.js";import"./index-vDCSPjrM.js";import"./index-CdzVFL-Z.js";import"./misc-W4055b-0.js";import"./tooltip-Tlsyx2YO.js";import"./pe-CIZUOJMr.js";import"./schemas-Uj5SZtvt.js";import"./online-DiNLkgTC.js";import"./loading-CDNzW5oO.js";import"./user-BsPobzjB.js";import"./workshop-config-Zfc8zU0x.js";import"./preload-helper-BXl3LOEh.js";import"./progress-bar-DpWhcyhC.js";import"./chunk-FNSCYPCZ-BHgC5cdx.js";import"./coerce-CkHW0SMv.js";const K=({loaderData:t,matches:e})=>{const o=t?.exercise.exerciseNumber.toString().padStart(2,"0"),i=d(e);return!t||!i?[{title:"🦉 | Error"}]:b({title:`📝 | ${o}. ${t.exercise.title} | ${i?.workshopTitle}`,description:`Introduction for ${o}. ${t.exercise.title}`,ogTitle:t.exercise.title,ogDescription:`Introduction for exercise ${Number(o)}`,instructor:i.instructor,requestInfo:i.requestInfo})},g={h1:()=>null},U=s(function({loaderData:e}){a({watchPaths:[e.exerciseReadme.file]});const o=String(e.firstStep?.stepNumber??"01").padStart(2,"0");return r.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:[r.jsxs("article",{id:e.articleId,className:"shadow-on-scrollbox scrollbar-thin scrollbar-thumb-scrollbar flex w-full flex-1 flex-col gap-12 overflow-y-scroll px-3 py-4 pt-6 md:px-10 md:py-12 md:pt-16",children:[r.jsx("div",{children:r.jsx("h1",{className:"text-[clamp(3rem,6vw,7.5rem)] leading-none font-extrabold",children:e.exercise.title})}),r.jsx("div",{children:e.exercise.instructionsCode?r.jsx(l,{epicVideoInfosPromise:e.epicVideoInfosPromise,children:r.jsx("div",{className:"prose dark:prose-invert sm:prose-lg",children:r.jsx(f,{code:e.exercise.instructionsCode,components:g})})}):"No instructions yet..."})]},e.articleId),r.jsx(m,{elementQuery:`#${e.articleId}`},`scroll-${e.articleId}`),r.jsx(u,{type:"instructions",exerciseNumber:e.exerciseNumber,className:"h-14 border-t px-6"}),r.jsxs("div",{className:"@container flex h-16 justify-between border-t border-b-4 lg:border-b-0",children:[r.jsx("div",{}),r.jsx(x,{file:e.exerciseReadme.file,relativePath:e.exerciseReadme.relativePath}),r.jsx(c,{to:`${o}/${e.firstType}`,prefetch:"intent",className:"bg-foreground text-background flex h-full items-center justify-center px-7","data-keyboard-action":"g+n",children:"Start Learning"})]})]})}),X=n(function(){return r.jsx(p,{className:"container flex items-center justify-center",statusHandlers:{404:h}})});export{X as ErrorBoundary,U as default,K as meta};
2
+ //# sourceMappingURL=_exerciseNumber-DUwXPNgl.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"_exerciseNumber-FdJ9Amz3.js","sources":["../../../app/routes/_app+/exercise+/$exerciseNumber.tsx"],"sourcesContent":["import path from 'path'\nimport { invariantResponse } from '@epic-web/invariant'\nimport { ElementScrollRestoration } from '@epic-web/restore-scroll'\nimport {\n\tgetExercises,\n\tgetWorkshopRoot,\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 slugify from '@sindresorhus/slugify'\nimport { data, type HeadersFunction, Link } from 'react-router'\nimport { EpicVideoInfoProvider } from '#app/components/epic-video.tsx'\nimport { GeneralErrorBoundary } from '#app/components/error-boundary.tsx'\nimport { useRevalidationWS } from '#app/components/revalidation-ws.tsx'\nimport { EditFileOnGitHub } from '#app/routes/launch-editor.tsx'\nimport { ProgressToggle } from '#app/routes/progress.tsx'\nimport { Mdx } from '#app/utils/mdx.tsx'\nimport { getRootMatchLoaderData } from '#app/utils/root-loader.ts'\nimport { getSeoMetaTags } from '#app/utils/seo.ts'\nimport { type Route } from './+types/$exerciseNumber.tsx'\nimport { getExercise404Data } from './__shared/error-boundary.server.ts'\nimport { Exercise404ErrorBoundary } from './__shared/error-boundary.tsx'\n\nexport const meta: Route.MetaFunction = ({ loaderData, matches }) => {\n\tconst number = loaderData?.exercise.exerciseNumber.toString().padStart(2, '0')\n\n\tconst rootData = getRootMatchLoaderData(matches)\n\tif (!loaderData || !rootData) return [{ title: '🦉 | Error' }]\n\n\treturn getSeoMetaTags({\n\t\ttitle: `📝 | ${number}. ${loaderData.exercise.title} | ${rootData?.workshopTitle}`,\n\t\tdescription: `Introduction for ${number}. ${loaderData.exercise.title}`,\n\t\togTitle: loaderData.exercise.title,\n\t\togDescription: `Introduction for exercise ${Number(number)}`,\n\t\tinstructor: rootData.instructor,\n\t\trequestInfo: rootData.requestInfo,\n\t})\n}\n\nexport async function loader({ request, params }: Route.LoaderArgs) {\n\tconst timings = makeTimings('exerciseNumberLoader')\n\tinvariantResponse(params.exerciseNumber, 'exerciseNumber is required')\n\tconst { title: workshopTitle } = getWorkshopConfig()\n\tconst exercises = await time(() => getExercises({ request, timings }), {\n\t\ttimings,\n\t\ttype: 'getExercises',\n\t\tdesc: 'getExercises in $exerciseNumber.tsx',\n\t})\n\tconst exercise = exercises.find(\n\t\t(e) => e.exerciseNumber === Number(params.exerciseNumber),\n\t)\n\tif (!exercise) {\n\t\tthrow Response.json(getExercise404Data({ exercises }), { status: 404 })\n\t}\n\n\tconst readmeFilepath = path.join(\n\t\tgetWorkshopRoot(),\n\t\t'exercises',\n\t\texercise.dirName,\n\t\t'README.mdx',\n\t)\n\n\tconst firstStep = exercise.steps.find(Boolean)\n\n\tconst articleId = `workshop-${slugify(workshopTitle)}-${\n\t\texercise.exerciseNumber\n\t}-instructions`\n\n\treturn data(\n\t\t{\n\t\t\tarticleId,\n\t\t\texercise,\n\t\t\texerciseNumber: exercise.exerciseNumber,\n\t\t\texerciseReadme: {\n\t\t\t\tfile: readmeFilepath,\n\t\t\t\trelativePath: `exercises/${exercise.dirName}/README.mdx`,\n\t\t\t},\n\t\t\texerciseTitle: exercise.title,\n\t\t\tfirstStep,\n\t\t\tfirstType: firstStep?.problem ? 'problem' : 'solution',\n\t\t\ttitle: workshopTitle,\n\t\t\tepicVideoInfosPromise: getEpicVideoInfos(\n\t\t\t\texercise.instructionsEpicVideoEmbeds,\n\t\t\t\t{ request },\n\t\t\t),\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\n// we'll render the title ourselves thank you\nconst mdxComponents = { h1: () => null }\n\nexport default function ExerciseNumberRoute({\n\tloaderData: data,\n}: Route.ComponentProps) {\n\tuseRevalidationWS({\n\t\twatchPaths: [data.exerciseReadme.file],\n\t})\n\n\tconst firstStepNumber = String(data.firstStep?.stepNumber ?? '01').padStart(\n\t\t2,\n\t\t'0',\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\tkey={data.articleId}\n\t\t\t\tclassName=\"shadow-on-scrollbox scrollbar-thin scrollbar-thumb-scrollbar flex w-full flex-1 flex-col gap-12 overflow-y-scroll px-3 py-4 pt-6 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=\"text-[clamp(3rem,6vw,7.5rem)] leading-none font-extrabold\">\n\t\t\t\t\t\t{data.exercise.title}\n\t\t\t\t\t</h1>\n\t\t\t\t</div>\n\t\t\t\t<div>\n\t\t\t\t\t{data.exercise.instructionsCode ? (\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.exercise.instructionsCode}\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) : (\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</article>\n\t\t\t<ElementScrollRestoration\n\t\t\t\telementQuery={`#${data.articleId}`}\n\t\t\t\tkey={`scroll-${data.articleId}`}\n\t\t\t/>\n\t\t\t<ProgressToggle\n\t\t\t\ttype=\"instructions\"\n\t\t\t\texerciseNumber={data.exerciseNumber}\n\t\t\t\tclassName=\"h-14 border-t px-6\"\n\t\t\t/>\n\t\t\t<div className=\"@container flex h-16 justify-between border-t border-b-4 lg:border-b-0\">\n\t\t\t\t<div />\n\t\t\t\t<EditFileOnGitHub\n\t\t\t\t\tfile={data.exerciseReadme.file}\n\t\t\t\t\trelativePath={data.exerciseReadme.relativePath}\n\t\t\t\t/>\n\t\t\t\t<Link\n\t\t\t\t\tto={`${firstStepNumber}/${data.firstType}`}\n\t\t\t\t\tprefetch=\"intent\"\n\t\t\t\t\tclassName=\"bg-foreground text-background flex h-full items-center justify-center px-7\"\n\t\t\t\t\tdata-keyboard-action=\"g+n\"\n\t\t\t\t>\n\t\t\t\t\tStart Learning\n\t\t\t\t</Link>\n\t\t\t</div>\n\t\t</main>\n\t)\n}\n\nexport function ErrorBoundary() {\n\treturn (\n\t\t<GeneralErrorBoundary\n\t\t\tclassName=\"container flex items-center justify-center\"\n\t\t\tstatusHandlers={{\n\t\t\t\t404: Exercise404ErrorBoundary,\n\t\t\t}}\n\t\t/>\n\t)\n}\n"],"names":["meta","loaderData","matches","number","exercise","exerciseNumber","toString","padStart","rootData","getRootMatchLoaderData","title","getSeoMetaTags","workshopTitle","description","ogTitle","ogDescription","Number","instructor","requestInfo","mdxComponents","h1","$exerciseNumber","_UNSAFE_withComponentProps","data","useRevalidationWS","watchPaths","exerciseReadme","file","firstStepNumber","String","firstStep","stepNumber","jsxs","className","children","id","articleId","jsx","instructionsCode","EpicVideoInfoProvider","epicVideoInfosPromise","Mdx","code","components","ElementScrollRestoration","elementQuery","ProgressToggle","type","EditFileOnGitHub","relativePath","Link","to","firstType","prefetch","ErrorBoundary","_UNSAFE_withErrorBoundaryProps","GeneralErrorBoundary","statusHandlers","Exercise404ErrorBoundary"],"mappings":"wjCA6BO,MAAMA,EAA2BA,CAAC,CAAEC,WAAAA,EAAYC,QAAAA,CAAQ,IAAM,CACpE,MAAMC,EAASF,GAAYG,SAASC,eAAeC,WAAWC,SAAS,EAAG,GAAG,EAEvEC,EAAWC,EAAuBP,CAAO,EAC/C,MAAI,CAACD,GAAc,CAACO,EAAiB,CAAC,CAAEE,MAAO,YAAa,CAAC,EAEtDC,EAAe,CACrBD,MAAO,QAAQP,CAAM,KAAKF,EAAWG,SAASM,KAAK,MAAMF,GAAUI,aAAa,GAChFC,YAAa,oBAAoBV,CAAM,KAAKF,EAAWG,SAASM,KAAK,GACrEI,QAASb,EAAWG,SAASM,MAC7BK,cAAe,6BAA6BC,OAAOb,CAAM,CAAC,GAC1Dc,WAAYT,EAASS,WACrBC,YAAaV,EAASU,WACvB,CAAC,CACF,EAkEMC,EAAgB,CAAEC,GAAIA,IAAM,IAAK,EAEvCC,EAAAC,EAAA,SAA4C,CAC3CrB,WAAYsB,CACb,EAAyB,CACxBC,EAAkB,CACjBC,WAAY,CAACF,EAAKG,eAAeC,IAAI,CACtC,CAAC,EAED,MAAMC,EAAkBC,OAAON,EAAKO,WAAWC,YAAc,IAAI,EAAExB,SAClE,EACA,GACD,EACA,OACCyB,EAAAA,KAAC,OAAA,CAAKC,UAAU,4FACfC,SAAA,CAAAF,EAAAA,KAAC,UAAA,CACAG,GAAIZ,EAAKa,UAETH,UAAU,8JAEVC,SAAA,CAAAG,EAAAA,IAAC,MAAA,CACAH,eAAC,KAAA,CAAGD,UAAU,4DACZC,SAAAX,EAAKnB,SAASM,MAChB,CAAA,CACD,EACA2B,EAAAA,IAAC,MAAA,CACCH,SAAAX,EAAKnB,SAASkC,iBACdD,EAAAA,IAACE,EAAA,CACAC,sBAAuBjB,EAAKiB,sBAE5BN,SAAAG,EAAAA,IAAC,MAAA,CAAIJ,UAAU,sCACdC,SAAAG,EAAAA,IAACI,EAAA,CACAC,KAAMnB,EAAKnB,SAASkC,iBACpBK,WAAYxB,EACb,EACD,CAAA,CACD,EAEA,wBAAA,CAEF,CAAA,GAvBKI,EAAKa,SAwBX,EACAC,EAAAA,IAACO,EAAA,CACAC,aAAc,IAAItB,EAAKa,SAAS,EAAA,EAC3B,UAAUb,EAAKa,SAAS,EAC9B,EACAC,EAAAA,IAACS,EAAA,CACAC,KAAK,eACL1C,eAAgBkB,EAAKlB,eACrB4B,UAAU,oBAAA,CACX,EACAD,EAAAA,KAAC,MAAA,CAAIC,UAAU,yEACdC,SAAA,CAAAG,EAAAA,IAAC,MAAA,CAAA,CAAI,EACLA,EAAAA,IAACW,EAAA,CACArB,KAAMJ,EAAKG,eAAeC,KAC1BsB,aAAc1B,EAAKG,eAAeuB,YAAA,CACnC,EACAZ,EAAAA,IAACa,EAAA,CACAC,GAAI,GAAGvB,CAAe,IAAIL,EAAK6B,SAAS,GACxCC,SAAS,SACTpB,UAAU,6EACV,uBAAqB,MACrBC,SAAA,gBAAA,CAED,CAAA,CAAA,CACD,CAAA,CAAA,CACD,CAEF,CAAA,EAEOoB,EAAAC,EAAA,UAAyB,CAC/B,OACClB,EAAAA,IAACmB,EAAA,CACAvB,UAAU,6CACVwB,eAAgB,CACf,IAAKC,CACN,CAAA,CACD,CAEF,CAAA"}
1
+ {"version":3,"file":"_exerciseNumber-DUwXPNgl.js","sources":["../../../app/routes/_app+/exercise+/$exerciseNumber.tsx"],"sourcesContent":["import path from 'path'\nimport { invariantResponse } from '@epic-web/invariant'\nimport { ElementScrollRestoration } from '@epic-web/restore-scroll'\nimport {\n\tgetExercises,\n\tgetWorkshopRoot,\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 slugify from '@sindresorhus/slugify'\nimport { data, type HeadersFunction, Link } from 'react-router'\nimport { EpicVideoInfoProvider } from '#app/components/epic-video.tsx'\nimport { GeneralErrorBoundary } from '#app/components/error-boundary.tsx'\nimport { useRevalidationWS } from '#app/components/revalidation-ws.tsx'\nimport { EditFileOnGitHub } from '#app/routes/launch-editor.tsx'\nimport { ProgressToggle } from '#app/routes/progress.tsx'\nimport { Mdx } from '#app/utils/mdx.tsx'\nimport { getRootMatchLoaderData } from '#app/utils/root-loader.ts'\nimport { getSeoMetaTags } from '#app/utils/seo.ts'\nimport { type Route } from './+types/$exerciseNumber.tsx'\nimport { getExercise404Data } from './__shared/error-boundary.server.ts'\nimport { Exercise404ErrorBoundary } from './__shared/error-boundary.tsx'\n\nexport const meta: Route.MetaFunction = ({ loaderData, matches }) => {\n\tconst number = loaderData?.exercise.exerciseNumber.toString().padStart(2, '0')\n\n\tconst rootData = getRootMatchLoaderData(matches)\n\tif (!loaderData || !rootData) return [{ title: '🦉 | Error' }]\n\n\treturn getSeoMetaTags({\n\t\ttitle: `📝 | ${number}. ${loaderData.exercise.title} | ${rootData?.workshopTitle}`,\n\t\tdescription: `Introduction for ${number}. ${loaderData.exercise.title}`,\n\t\togTitle: loaderData.exercise.title,\n\t\togDescription: `Introduction for exercise ${Number(number)}`,\n\t\tinstructor: rootData.instructor,\n\t\trequestInfo: rootData.requestInfo,\n\t})\n}\n\nexport async function loader({ request, params }: Route.LoaderArgs) {\n\tconst timings = makeTimings('exerciseNumberLoader')\n\tinvariantResponse(params.exerciseNumber, 'exerciseNumber is required')\n\tconst { title: workshopTitle } = getWorkshopConfig()\n\tconst exercises = await time(() => getExercises({ request, timings }), {\n\t\ttimings,\n\t\ttype: 'getExercises',\n\t\tdesc: 'getExercises in $exerciseNumber.tsx',\n\t})\n\tconst exercise = exercises.find(\n\t\t(e) => e.exerciseNumber === Number(params.exerciseNumber),\n\t)\n\tif (!exercise) {\n\t\tthrow Response.json(getExercise404Data({ exercises }), { status: 404 })\n\t}\n\n\tconst readmeFilepath = path.join(\n\t\tgetWorkshopRoot(),\n\t\t'exercises',\n\t\texercise.dirName,\n\t\t'README.mdx',\n\t)\n\n\tconst firstStep = exercise.steps.find(Boolean)\n\n\tconst articleId = `workshop-${slugify(workshopTitle)}-${\n\t\texercise.exerciseNumber\n\t}-instructions`\n\n\treturn data(\n\t\t{\n\t\t\tarticleId,\n\t\t\texercise,\n\t\t\texerciseNumber: exercise.exerciseNumber,\n\t\t\texerciseReadme: {\n\t\t\t\tfile: readmeFilepath,\n\t\t\t\trelativePath: `exercises/${exercise.dirName}/README.mdx`,\n\t\t\t},\n\t\t\texerciseTitle: exercise.title,\n\t\t\tfirstStep,\n\t\t\tfirstType: firstStep?.problem ? 'problem' : 'solution',\n\t\t\ttitle: workshopTitle,\n\t\t\tepicVideoInfosPromise: getEpicVideoInfos(\n\t\t\t\texercise.instructionsEpicVideoEmbeds,\n\t\t\t\t{ request },\n\t\t\t),\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\n// we'll render the title ourselves thank you\nconst mdxComponents = { h1: () => null }\n\nexport default function ExerciseNumberRoute({\n\tloaderData: data,\n}: Route.ComponentProps) {\n\tuseRevalidationWS({\n\t\twatchPaths: [data.exerciseReadme.file],\n\t})\n\n\tconst firstStepNumber = String(data.firstStep?.stepNumber ?? '01').padStart(\n\t\t2,\n\t\t'0',\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\tkey={data.articleId}\n\t\t\t\tclassName=\"shadow-on-scrollbox scrollbar-thin scrollbar-thumb-scrollbar flex w-full flex-1 flex-col gap-12 overflow-y-scroll px-3 py-4 pt-6 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=\"text-[clamp(3rem,6vw,7.5rem)] leading-none font-extrabold\">\n\t\t\t\t\t\t{data.exercise.title}\n\t\t\t\t\t</h1>\n\t\t\t\t</div>\n\t\t\t\t<div>\n\t\t\t\t\t{data.exercise.instructionsCode ? (\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.exercise.instructionsCode}\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) : (\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</article>\n\t\t\t<ElementScrollRestoration\n\t\t\t\telementQuery={`#${data.articleId}`}\n\t\t\t\tkey={`scroll-${data.articleId}`}\n\t\t\t/>\n\t\t\t<ProgressToggle\n\t\t\t\ttype=\"instructions\"\n\t\t\t\texerciseNumber={data.exerciseNumber}\n\t\t\t\tclassName=\"h-14 border-t px-6\"\n\t\t\t/>\n\t\t\t<div className=\"@container flex h-16 justify-between border-t border-b-4 lg:border-b-0\">\n\t\t\t\t<div />\n\t\t\t\t<EditFileOnGitHub\n\t\t\t\t\tfile={data.exerciseReadme.file}\n\t\t\t\t\trelativePath={data.exerciseReadme.relativePath}\n\t\t\t\t/>\n\t\t\t\t<Link\n\t\t\t\t\tto={`${firstStepNumber}/${data.firstType}`}\n\t\t\t\t\tprefetch=\"intent\"\n\t\t\t\t\tclassName=\"bg-foreground text-background flex h-full items-center justify-center px-7\"\n\t\t\t\t\tdata-keyboard-action=\"g+n\"\n\t\t\t\t>\n\t\t\t\t\tStart Learning\n\t\t\t\t</Link>\n\t\t\t</div>\n\t\t</main>\n\t)\n}\n\nexport function ErrorBoundary() {\n\treturn (\n\t\t<GeneralErrorBoundary\n\t\t\tclassName=\"container flex items-center justify-center\"\n\t\t\tstatusHandlers={{\n\t\t\t\t404: Exercise404ErrorBoundary,\n\t\t\t}}\n\t\t/>\n\t)\n}\n"],"names":["meta","loaderData","matches","number","exercise","exerciseNumber","toString","padStart","rootData","getRootMatchLoaderData","title","getSeoMetaTags","workshopTitle","description","ogTitle","ogDescription","Number","instructor","requestInfo","mdxComponents","h1","$exerciseNumber","_UNSAFE_withComponentProps","data","useRevalidationWS","watchPaths","exerciseReadme","file","firstStepNumber","String","firstStep","stepNumber","jsxs","className","children","id","articleId","jsx","instructionsCode","EpicVideoInfoProvider","epicVideoInfosPromise","Mdx","code","components","ElementScrollRestoration","elementQuery","ProgressToggle","type","EditFileOnGitHub","relativePath","Link","to","firstType","prefetch","ErrorBoundary","_UNSAFE_withErrorBoundaryProps","GeneralErrorBoundary","statusHandlers","Exercise404ErrorBoundary"],"mappings":"wjCA6BO,MAAMA,EAA2BA,CAAC,CAAEC,WAAAA,EAAYC,QAAAA,CAAQ,IAAM,CACpE,MAAMC,EAASF,GAAYG,SAASC,eAAeC,WAAWC,SAAS,EAAG,GAAG,EAEvEC,EAAWC,EAAuBP,CAAO,EAC/C,MAAI,CAACD,GAAc,CAACO,EAAiB,CAAC,CAAEE,MAAO,YAAa,CAAC,EAEtDC,EAAe,CACrBD,MAAO,QAAQP,CAAM,KAAKF,EAAWG,SAASM,KAAK,MAAMF,GAAUI,aAAa,GAChFC,YAAa,oBAAoBV,CAAM,KAAKF,EAAWG,SAASM,KAAK,GACrEI,QAASb,EAAWG,SAASM,MAC7BK,cAAe,6BAA6BC,OAAOb,CAAM,CAAC,GAC1Dc,WAAYT,EAASS,WACrBC,YAAaV,EAASU,WACvB,CAAC,CACF,EAkEMC,EAAgB,CAAEC,GAAIA,IAAM,IAAK,EAEvCC,EAAAC,EAAA,SAA4C,CAC3CrB,WAAYsB,CACb,EAAyB,CACxBC,EAAkB,CACjBC,WAAY,CAACF,EAAKG,eAAeC,IAAI,CACtC,CAAC,EAED,MAAMC,EAAkBC,OAAON,EAAKO,WAAWC,YAAc,IAAI,EAAExB,SAClE,EACA,GACD,EACA,OACCyB,EAAAA,KAAC,OAAA,CAAKC,UAAU,4FACfC,SAAA,CAAAF,EAAAA,KAAC,UAAA,CACAG,GAAIZ,EAAKa,UAETH,UAAU,8JAEVC,SAAA,CAAAG,EAAAA,IAAC,MAAA,CACAH,eAAC,KAAA,CAAGD,UAAU,4DACZC,SAAAX,EAAKnB,SAASM,MAChB,CAAA,CACD,EACA2B,EAAAA,IAAC,MAAA,CACCH,SAAAX,EAAKnB,SAASkC,iBACdD,EAAAA,IAACE,EAAA,CACAC,sBAAuBjB,EAAKiB,sBAE5BN,SAAAG,EAAAA,IAAC,MAAA,CAAIJ,UAAU,sCACdC,SAAAG,EAAAA,IAACI,EAAA,CACAC,KAAMnB,EAAKnB,SAASkC,iBACpBK,WAAYxB,EACb,EACD,CAAA,CACD,EAEA,wBAAA,CAEF,CAAA,GAvBKI,EAAKa,SAwBX,EACAC,EAAAA,IAACO,EAAA,CACAC,aAAc,IAAItB,EAAKa,SAAS,EAAA,EAC3B,UAAUb,EAAKa,SAAS,EAC9B,EACAC,EAAAA,IAACS,EAAA,CACAC,KAAK,eACL1C,eAAgBkB,EAAKlB,eACrB4B,UAAU,oBAAA,CACX,EACAD,EAAAA,KAAC,MAAA,CAAIC,UAAU,yEACdC,SAAA,CAAAG,EAAAA,IAAC,MAAA,CAAA,CAAI,EACLA,EAAAA,IAACW,EAAA,CACArB,KAAMJ,EAAKG,eAAeC,KAC1BsB,aAAc1B,EAAKG,eAAeuB,YAAA,CACnC,EACAZ,EAAAA,IAACa,EAAA,CACAC,GAAI,GAAGvB,CAAe,IAAIL,EAAK6B,SAAS,GACxCC,SAAS,SACTpB,UAAU,6EACV,uBAAqB,MACrBC,SAAA,gBAAA,CAED,CAAA,CAAA,CACD,CAAA,CAAA,CACD,CAEF,CAAA,EAEOoB,EAAAC,EAAA,UAAyB,CAC/B,OACClB,EAAAA,IAACmB,EAAA,CACAvB,UAAU,6CACVwB,eAAgB,CACf,IAAKC,CACN,CAAA,CACD,CAEF,CAAA"}
@@ -1,2 +1,2 @@
1
- import{a as r}from"./chunk-EPOLDU6W-BCLmut3y.js";import{j as o}from"./jsx-runtime-C5WNSv3b.js";import{G as t}from"./error-boundary-DVp6wKiz.js";import{E as i}from"./error-boundary-BDqWjIFP.js";import"./index-CqIc3cxq.js";import"./preload-helper-BXl3LOEh.js";import"./misc-W4055b-0.js";import"./chunk-FNSCYPCZ-BHgC5cdx.js";import"./schemas-Uj5SZtvt.js";import"./coerce-CkHW0SMv.js";const x=r(function(){return o.jsx(t,{className:"container flex items-center justify-center",statusHandlers:{404:i}})});export{x as ErrorBoundary};
2
- //# sourceMappingURL=_exerciseNumber_._stepNumber.index-WLW-sVZ0.js.map
1
+ import{a as r}from"./chunk-EPOLDU6W-BCLmut3y.js";import{j as o}from"./jsx-runtime-C5WNSv3b.js";import{G as t}from"./error-boundary-DxHqAEHX.js";import{E as i}from"./error-boundary-BDqWjIFP.js";import"./index-CqIc3cxq.js";import"./preload-helper-BXl3LOEh.js";import"./misc-W4055b-0.js";import"./chunk-FNSCYPCZ-BHgC5cdx.js";import"./schemas-Uj5SZtvt.js";import"./coerce-CkHW0SMv.js";const x=r(function(){return o.jsx(t,{className:"container flex items-center justify-center",statusHandlers:{404:i}})});export{x as ErrorBoundary};
2
+ //# sourceMappingURL=_exerciseNumber_._stepNumber.index-BPqMtmN8.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"_exerciseNumber_._stepNumber.index-WLW-sVZ0.js","sources":["../../../app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.index.tsx"],"sourcesContent":["import { invariantResponse } from '@epic-web/invariant'\nimport {\n\tgetExerciseApp,\n\tisProblemApp,\n\tisSolutionApp,\n} from '@epic-web/workshop-utils/apps.server'\nimport { redirect, type LoaderFunctionArgs } from 'react-router'\nimport { GeneralErrorBoundary } from '#app/components/error-boundary.tsx'\nimport { getStep404Data } from './__shared/error-boundary.server.ts'\nimport { Exercise404ErrorBoundary } from './__shared/error-boundary.tsx'\n\nexport async function loader({ params }: LoaderFunctionArgs) {\n\tinvariantResponse(params.exerciseNumber, 'exerciseNumber is required')\n\tconst problemApp = await getExerciseApp({ ...params, type: 'problem' }).then(\n\t\t(a) => (isProblemApp(a) ? a : null),\n\t)\n\tif (problemApp) {\n\t\treturn redirect(\n\t\t\t`/exercise/${params.exerciseNumber}/${params.stepNumber}/problem`,\n\t\t)\n\t}\n\tconst solutionApp = await getExerciseApp({\n\t\t...params,\n\t\ttype: 'solution',\n\t}).then((a) => (isSolutionApp(a) ? a : null))\n\tif (solutionApp) {\n\t\treturn redirect(\n\t\t\t`/exercise/${params.exerciseNumber}/${params.stepNumber}/solution`,\n\t\t)\n\t}\n\tthrow Response.json(\n\t\tawait getStep404Data({ exerciseNumber: params.exerciseNumber }),\n\t\t{ status: 404 },\n\t)\n}\n\nexport function ErrorBoundary() {\n\treturn (\n\t\t<GeneralErrorBoundary\n\t\t\tclassName=\"container flex items-center justify-center\"\n\t\t\tstatusHandlers={{\n\t\t\t\t404: Exercise404ErrorBoundary,\n\t\t\t}}\n\t\t/>\n\t)\n}\n"],"names":["ErrorBoundary","_UNSAFE_withErrorBoundaryProps","jsx","GeneralErrorBoundary","className","statusHandlers","Exercise404ErrorBoundary"],"mappings":"6XAoCO,MAAAA,EAAAC,EAAA,UAAyB,CAC/B,OACCC,EAAAA,IAACC,EAAA,CACAC,UAAU,6CACVC,eAAgB,CACf,IAAKC,CACN,CAAA,CACD,CAEF,CAAA"}
1
+ {"version":3,"file":"_exerciseNumber_._stepNumber.index-BPqMtmN8.js","sources":["../../../app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.index.tsx"],"sourcesContent":["import { invariantResponse } from '@epic-web/invariant'\nimport {\n\tgetExerciseApp,\n\tisProblemApp,\n\tisSolutionApp,\n} from '@epic-web/workshop-utils/apps.server'\nimport { redirect, type LoaderFunctionArgs } from 'react-router'\nimport { GeneralErrorBoundary } from '#app/components/error-boundary.tsx'\nimport { getStep404Data } from './__shared/error-boundary.server.ts'\nimport { Exercise404ErrorBoundary } from './__shared/error-boundary.tsx'\n\nexport async function loader({ params }: LoaderFunctionArgs) {\n\tinvariantResponse(params.exerciseNumber, 'exerciseNumber is required')\n\tconst problemApp = await getExerciseApp({ ...params, type: 'problem' }).then(\n\t\t(a) => (isProblemApp(a) ? a : null),\n\t)\n\tif (problemApp) {\n\t\treturn redirect(\n\t\t\t`/exercise/${params.exerciseNumber}/${params.stepNumber}/problem`,\n\t\t)\n\t}\n\tconst solutionApp = await getExerciseApp({\n\t\t...params,\n\t\ttype: 'solution',\n\t}).then((a) => (isSolutionApp(a) ? a : null))\n\tif (solutionApp) {\n\t\treturn redirect(\n\t\t\t`/exercise/${params.exerciseNumber}/${params.stepNumber}/solution`,\n\t\t)\n\t}\n\tthrow Response.json(\n\t\tawait getStep404Data({ exerciseNumber: params.exerciseNumber }),\n\t\t{ status: 404 },\n\t)\n}\n\nexport function ErrorBoundary() {\n\treturn (\n\t\t<GeneralErrorBoundary\n\t\t\tclassName=\"container flex items-center justify-center\"\n\t\t\tstatusHandlers={{\n\t\t\t\t404: Exercise404ErrorBoundary,\n\t\t\t}}\n\t\t/>\n\t)\n}\n"],"names":["ErrorBoundary","_UNSAFE_withErrorBoundaryProps","jsx","GeneralErrorBoundary","className","statusHandlers","Exercise404ErrorBoundary"],"mappings":"6XAoCO,MAAAA,EAAAC,EAAA,UAAyB,CAC/B,OACCC,EAAAA,IAACC,EAAA,CACAC,UAAU,6CACVC,eAAgB,CACf,IAAKC,CACN,CAAA,CACD,CAEF,CAAA"}
@@ -1,2 +1,2 @@
1
- import{w as n,L as l}from"./chunk-EPOLDU6W-BCLmut3y.js";import{j as e}from"./jsx-runtime-C5WNSv3b.js";import{E as c}from"./index-CEVyDj51.js";import{r as m}from"./index-CqIc3cxq.js";import{E as a}from"./epic-video-DnXXh6qE.js";import{I as x,c as d}from"./misc-W4055b-0.js";import{L as f}from"./loading-CDNzW5oO.js";import{N as p}from"./nav-chevrons-Dk4GtZwQ.js";import{u as h}from"./revalidation-ws-BJWJviUX.js";import{E as u}from"./launch-editor-D2exGfVu.js";import{P as b}from"./progress-ILaVQtOO.js";import{u as j}from"./index-CdzVFL-Z.js";import{M as g}from"./mdx-Dun3ONG_.js";import{u as N}from"./online-DiNLkgTC.js";import{g as v}from"./root-loader-BOzEMapJ.js";import{g as w}from"./seo-t5J-DRxw.js";import"./use-event-source-BuD4_2SF.js";import"./index-DzdDahau.js";import"./index-vDCSPjrM.js";import"./schemas-Uj5SZtvt.js";import"./tooltip-Tlsyx2YO.js";import"./user-BsPobzjB.js";import"./workshop-config-Zfc8zU0x.js";import"./progress-bar-DpWhcyhC.js";import"./pe-CIZUOJMr.js";import"./preload-helper-BXl3LOEh.js";const Y=({loaderData:s,matches:r})=>{const i=s?.exercise.exerciseNumber.toString().padStart(2,"0"),o=v(r);return!s||!o?[{title:"🦉 | Error"}]:w({title:`🦉 | ${i}. ${s.exercise.title} | ${o?.workshopTitle}`,description:`Elaboration for ${i}. ${s.exercise.title}`,ogTitle:`Finished: ${s.exercise.title}`,ogDescription:`Elaboration for exercise ${Number(i)}`,instructor:o.instructor,requestInfo:o.requestInfo})},E={h1:()=>null},Z=n(function({loaderData:r}){const i=r.exercise.exerciseNumber.toString().padStart(2,"0");return h({watchPaths:[`./exercises/${i}/FINISHED.mdx`]}),e.jsx("div",{className:"flex max-w-full grow flex-col",children:e.jsxs("main",{className:"flex grow flex-col sm:grid sm:h-full sm:min-h-[800px] sm:grid-cols-1 sm:grid-rows-2 md:min-h-[unset] lg:grid-cols-2 lg:grid-rows-1",children:[e.jsxs("div",{className:"relative flex flex-col sm:col-span-1 sm:row-span-1 sm:h-full lg:border-r",children:[e.jsx("h1",{className:"h-14 border-b pr-5 pl-10 text-sm leading-tight font-medium",children:e.jsx("div",{className:"flex h-14 flex-wrap items-center justify-between gap-x-2 py-2",children:e.jsxs("div",{className:"flex items-center justify-start gap-x-2",children:[e.jsx(l,{to:`/${i}`,className:"hover:underline",children:`${i}. ${r.exercise.title}`}),e.jsx("span",{children:"/"}),e.jsx("span",{children:"Elaboration"})]})})}),e.jsx("article",{className:"shadow-on-scrollbox scrollbar-thin scrollbar-thumb-scrollbar h-full w-full max-w-none flex-1 scroll-pt-6 space-y-6 overflow-y-auto p-2 sm:p-10 sm:pt-8",id:r.articleId,children:r.exercise.finishedCode?e.jsx(a,{epicVideoInfosPromise:r.epicVideoInfosPromise,children:e.jsx("div",{className:"prose dark:prose-invert sm:prose-lg",children:e.jsx(g,{code:r.exercise.finishedCode,components:E})})}):"No finished instructions yet..."}),e.jsx(c,{elementQuery:`#${r.articleId}`}),e.jsx(b,{type:"finished",exerciseNumber:r.exercise.exerciseNumber,className:"h-14 border-t px-6"}),e.jsxs("div",{className:"@container flex h-16 justify-between border-t border-b-4 lg:border-b-0",children:[e.jsx("div",{}),e.jsx(u,{file:r.exerciseFinished.file,relativePath:r.exerciseFinished.relativePath}),e.jsx(p,{prev:r.prevStepLink,next:r.nextStepLink})]})]}),e.jsx(y,{exerciseFormEmbedUrl:r.exerciseFormEmbedUrl,exerciseTitle:r.exercise.title})]})})});function y({exerciseFormEmbedUrl:s,exerciseTitle:r}){const i=j(),[o,t]=m.useState(!1);return N()?e.jsxs("div",{className:"relative min-h-full sm:min-h-[unset] sm:shrink-0",children:[o?null:e.jsx("div",{className:"absolute inset-0 z-10 flex items-center justify-center",children:e.jsx(f,{children:e.jsxs("span",{children:["Loading ",r," Elaboration form"]})})}),e.jsx("iframe",{onLoad:()=>t(!0),onError:()=>t(!0),title:"Elaboration",src:s,className:d("absolute inset-0 flex h-full w-full transition-opacity duration-300",o?"opacity-100":"opacity-0"),style:{colorScheme:i}})]}):e.jsx("div",{className:"relative shrink-0",children:e.jsx("div",{className:"text-body-md text-foreground-destructive absolute inset-0 z-10 flex items-center justify-center",children:e.jsx(x,{name:"WifiNoConnection",size:"xl",children:e.jsxs("span",{children:["Unable to load the ",e.jsx("a",{href:s,className:"underline",children:`${r} feedback form`})," when offline"]})})})})}export{Z as default,Y as meta};
2
- //# sourceMappingURL=_exerciseNumber_.finished-Z7QZ9wk-.js.map
1
+ import{w as n,L as l}from"./chunk-EPOLDU6W-BCLmut3y.js";import{j as e}from"./jsx-runtime-C5WNSv3b.js";import{E as c}from"./index-CEVyDj51.js";import{r as m}from"./index-CqIc3cxq.js";import{E as a}from"./epic-video-DnXXh6qE.js";import{I as x,c as d}from"./misc-W4055b-0.js";import{L as f}from"./loading-CDNzW5oO.js";import{N as p}from"./nav-chevrons-Dk4GtZwQ.js";import{u as h}from"./revalidation-ws-BJWJviUX.js";import{E as u}from"./launch-editor-D2exGfVu.js";import{P as b}from"./progress-BcA4i0iU.js";import{u as j}from"./index-CdzVFL-Z.js";import{M as g}from"./mdx-Dun3ONG_.js";import{u as N}from"./online-DiNLkgTC.js";import{g as v}from"./root-loader-BOzEMapJ.js";import{g as w}from"./seo-t5J-DRxw.js";import"./use-event-source-BuD4_2SF.js";import"./index-DzdDahau.js";import"./index-vDCSPjrM.js";import"./schemas-Uj5SZtvt.js";import"./tooltip-Tlsyx2YO.js";import"./user-BsPobzjB.js";import"./workshop-config-Zfc8zU0x.js";import"./progress-bar-DpWhcyhC.js";import"./pe-CIZUOJMr.js";import"./preload-helper-BXl3LOEh.js";const Y=({loaderData:s,matches:r})=>{const i=s?.exercise.exerciseNumber.toString().padStart(2,"0"),o=v(r);return!s||!o?[{title:"🦉 | Error"}]:w({title:`🦉 | ${i}. ${s.exercise.title} | ${o?.workshopTitle}`,description:`Elaboration for ${i}. ${s.exercise.title}`,ogTitle:`Finished: ${s.exercise.title}`,ogDescription:`Elaboration for exercise ${Number(i)}`,instructor:o.instructor,requestInfo:o.requestInfo})},E={h1:()=>null},Z=n(function({loaderData:r}){const i=r.exercise.exerciseNumber.toString().padStart(2,"0");return h({watchPaths:[`./exercises/${i}/FINISHED.mdx`]}),e.jsx("div",{className:"flex max-w-full grow flex-col",children:e.jsxs("main",{className:"flex grow flex-col sm:grid sm:h-full sm:min-h-[800px] sm:grid-cols-1 sm:grid-rows-2 md:min-h-[unset] lg:grid-cols-2 lg:grid-rows-1",children:[e.jsxs("div",{className:"relative flex flex-col sm:col-span-1 sm:row-span-1 sm:h-full lg:border-r",children:[e.jsx("h1",{className:"h-14 border-b pr-5 pl-10 text-sm leading-tight font-medium",children:e.jsx("div",{className:"flex h-14 flex-wrap items-center justify-between gap-x-2 py-2",children:e.jsxs("div",{className:"flex items-center justify-start gap-x-2",children:[e.jsx(l,{to:`/${i}`,className:"hover:underline",children:`${i}. ${r.exercise.title}`}),e.jsx("span",{children:"/"}),e.jsx("span",{children:"Elaboration"})]})})}),e.jsx("article",{className:"shadow-on-scrollbox scrollbar-thin scrollbar-thumb-scrollbar h-full w-full max-w-none flex-1 scroll-pt-6 space-y-6 overflow-y-auto p-2 sm:p-10 sm:pt-8",id:r.articleId,children:r.exercise.finishedCode?e.jsx(a,{epicVideoInfosPromise:r.epicVideoInfosPromise,children:e.jsx("div",{className:"prose dark:prose-invert sm:prose-lg",children:e.jsx(g,{code:r.exercise.finishedCode,components:E})})}):"No finished instructions yet..."}),e.jsx(c,{elementQuery:`#${r.articleId}`}),e.jsx(b,{type:"finished",exerciseNumber:r.exercise.exerciseNumber,className:"h-14 border-t px-6"}),e.jsxs("div",{className:"@container flex h-16 justify-between border-t border-b-4 lg:border-b-0",children:[e.jsx("div",{}),e.jsx(u,{file:r.exerciseFinished.file,relativePath:r.exerciseFinished.relativePath}),e.jsx(p,{prev:r.prevStepLink,next:r.nextStepLink})]})]}),e.jsx(y,{exerciseFormEmbedUrl:r.exerciseFormEmbedUrl,exerciseTitle:r.exercise.title})]})})});function y({exerciseFormEmbedUrl:s,exerciseTitle:r}){const i=j(),[o,t]=m.useState(!1);return N()?e.jsxs("div",{className:"relative min-h-full sm:min-h-[unset] sm:shrink-0",children:[o?null:e.jsx("div",{className:"absolute inset-0 z-10 flex items-center justify-center",children:e.jsx(f,{children:e.jsxs("span",{children:["Loading ",r," Elaboration form"]})})}),e.jsx("iframe",{onLoad:()=>t(!0),onError:()=>t(!0),title:"Elaboration",src:s,className:d("absolute inset-0 flex h-full w-full transition-opacity duration-300",o?"opacity-100":"opacity-0"),style:{colorScheme:i}})]}):e.jsx("div",{className:"relative shrink-0",children:e.jsx("div",{className:"text-body-md text-foreground-destructive absolute inset-0 z-10 flex items-center justify-center",children:e.jsx(x,{name:"WifiNoConnection",size:"xl",children:e.jsxs("span",{children:["Unable to load the ",e.jsx("a",{href:s,className:"underline",children:`${r} feedback form`})," when offline"]})})})})}export{Z as default,Y as meta};
2
+ //# sourceMappingURL=_exerciseNumber_.finished-C4sVkuEq.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"_exerciseNumber_.finished-Z7QZ9wk-.js","sources":["../../../app/routes/_app+/exercise+/$exerciseNumber_.finished.tsx"],"sourcesContent":["import path from 'path'\nimport { invariantResponse } from '@epic-web/invariant'\nimport { ElementScrollRestoration } from '@epic-web/restore-scroll'\nimport {\n\tgetAppPageRoute,\n\tgetApps,\n\tgetExercise,\n\tgetWorkshopRoot,\n\tisExtraApp,\n\tisExerciseStepApp,\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} from '@epic-web/workshop-utils/timing.server'\nimport slugify from '@sindresorhus/slugify'\nimport * as React from 'react'\nimport { data, type HeadersFunction, Link } from 'react-router'\nimport { EpicVideoInfoProvider } from '#app/components/epic-video.tsx'\nimport { Icon } from '#app/components/icons.tsx'\nimport { Loading } from '#app/components/loading.tsx'\nimport { NavChevrons } from '#app/components/nav-chevrons.tsx'\nimport { useRevalidationWS } from '#app/components/revalidation-ws.tsx'\nimport { EditFileOnGitHub } from '#app/routes/launch-editor.tsx'\nimport { ProgressToggle } from '#app/routes/progress.tsx'\nimport { useTheme } from '#app/routes/theme/index.tsx'\nimport { Mdx } from '#app/utils/mdx.tsx'\nimport { cn } from '#app/utils/misc.tsx'\nimport { useIsOnline } from '#app/utils/online.ts'\nimport { getRootMatchLoaderData } from '#app/utils/root-loader.ts'\nimport { getSeoMetaTags } from '#app/utils/seo.ts'\nimport { type Route } from './+types/$exerciseNumber_.finished.tsx'\n\nexport const meta: Route.MetaFunction = ({ loaderData, matches }) => {\n\tconst number = loaderData?.exercise.exerciseNumber.toString().padStart(2, '0')\n\n\tconst rootData = getRootMatchLoaderData(matches)\n\tif (!loaderData || !rootData) return [{ title: '🦉 | Error' }]\n\n\treturn getSeoMetaTags({\n\t\ttitle: `🦉 | ${number}. ${loaderData.exercise.title} | ${rootData?.workshopTitle}`,\n\t\tdescription: `Elaboration for ${number}. ${loaderData.exercise.title}`,\n\t\togTitle: `Finished: ${loaderData.exercise.title}`,\n\t\togDescription: `Elaboration for exercise ${Number(number)}`,\n\t\tinstructor: rootData.instructor,\n\t\trequestInfo: rootData.requestInfo,\n\t})\n}\n\nexport async function loader({ request, params }: Route.LoaderArgs) {\n\tconst timings = makeTimings('exerciseFinishedLoader')\n\tinvariantResponse(params.exerciseNumber, 'exerciseNumber is required')\n\tconst exercise = await getExercise(params.exerciseNumber, {\n\t\ttimings,\n\t\trequest,\n\t})\n\tif (!exercise) {\n\t\tthrow new Response('Not found', { status: 404 })\n\t}\n\tconst workshopConfig = getWorkshopConfig()\n\tconst exerciseFormTemplate = workshopConfig.forms.exercise\n\tconst exerciseFormEmbedUrl = exerciseFormTemplate\n\t\t.replace('{workshopTitle}', encodeURIComponent(workshopConfig.title))\n\t\t.replace('{exerciseTitle}', encodeURIComponent(exercise.title))\n\tconst nextExercise = await getExercise(exercise.exerciseNumber + 1, {\n\t\ttimings,\n\t\trequest,\n\t})\n\n\tconst finishedFilepath = path.join(\n\t\tgetWorkshopRoot(),\n\t\t'exercises',\n\t\texercise.dirName,\n\t\t'FINISHED.mdx',\n\t)\n\n\tconst apps = await getApps({ request, timings })\n\tconst hasExtras = apps.some(isExtraApp)\n\tconst exerciseApps = apps\n\t\t.filter(isExerciseStepApp)\n\t\t.filter((app) => app.exerciseNumber === exercise.exerciseNumber)\n\tconst prevApp = exerciseApps[exerciseApps.length - 1]\n\n\tconst articleId = `workshop-${slugify(workshopConfig.title)}-${\n\t\texercise.exerciseNumber\n\t}-finished`\n\n\treturn data(\n\t\t{\n\t\t\tarticleId,\n\t\t\tworkshopTitle: workshopConfig.title,\n\t\t\texercise,\n\t\t\texerciseFormEmbedUrl,\n\t\t\tepicVideoInfosPromise: getEpicVideoInfos(\n\t\t\t\texercise.finishedEpicVideoEmbeds,\n\t\t\t\t{ request },\n\t\t\t),\n\t\t\texerciseFinished: {\n\t\t\t\tfile: finishedFilepath,\n\t\t\t\trelativePath: `exercises/${exercise.dirName}/FINISHED.mdx`,\n\t\t\t},\n\t\t\tprevStepLink: prevApp\n\t\t\t\t? {\n\t\t\t\t\t\tto: getAppPageRoute(prevApp),\n\t\t\t\t\t\t'aria-label': `${prevApp.title} (${prevApp.type})`,\n\t\t\t\t\t}\n\t\t\t\t: null,\n\t\t\tnextStepLink: nextExercise\n\t\t\t\t? {\n\t\t\t\t\t\tto: `/exercise/${nextExercise.exerciseNumber.toString().padStart(2, '0')}`,\n\t\t\t\t\t\t'aria-label': `${nextExercise.title}`,\n\t\t\t\t\t}\n\t\t\t\t: hasExtras\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tto: '/extra',\n\t\t\t\t\t\t\t'aria-label': 'Extras',\n\t\t\t\t\t\t}\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tto: '/finished',\n\t\t\t\t\t\t\t'aria-label': 'Finished! 🎉',\n\t\t\t\t\t\t},\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\nconst mdxComponents = { h1: () => null }\nexport default function ExerciseFinished({\n\tloaderData: data,\n}: Route.ComponentProps) {\n\tconst exerciseNumber = data.exercise.exerciseNumber\n\t\t.toString()\n\t\t.padStart(2, '0')\n\n\tuseRevalidationWS({\n\t\twatchPaths: [`./exercises/${exerciseNumber}/FINISHED.mdx`],\n\t})\n\n\treturn (\n\t\t<div className=\"flex max-w-full grow flex-col\">\n\t\t\t<main className=\"flex grow flex-col sm:grid sm:h-full sm:min-h-[800px] sm:grid-cols-1 sm:grid-rows-2 md:min-h-[unset] lg:grid-cols-2 lg:grid-rows-1\">\n\t\t\t\t<div className=\"relative flex flex-col sm:col-span-1 sm:row-span-1 sm:h-full lg:border-r\">\n\t\t\t\t\t<h1 className=\"h-14 border-b pr-5 pl-10 text-sm leading-tight font-medium\">\n\t\t\t\t\t\t<div className=\"flex h-14 flex-wrap items-center justify-between gap-x-2 py-2\">\n\t\t\t\t\t\t\t<div className=\"flex items-center justify-start gap-x-2\">\n\t\t\t\t\t\t\t\t<Link to={`/${exerciseNumber}`} className=\"hover:underline\">\n\t\t\t\t\t\t\t\t\t{`${exerciseNumber}. ${data.exercise.title}`}\n\t\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t\t<span>/</span>\n\t\t\t\t\t\t\t\t<span>Elaboration</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</h1>\n\n\t\t\t\t\t<article\n\t\t\t\t\t\tclassName=\"shadow-on-scrollbox scrollbar-thin scrollbar-thumb-scrollbar h-full w-full max-w-none flex-1 scroll-pt-6 space-y-6 overflow-y-auto p-2 sm:p-10 sm:pt-8\"\n\t\t\t\t\t\tid={data.articleId}\n\t\t\t\t\t>\n\t\t\t\t\t\t{data.exercise.finishedCode ? (\n\t\t\t\t\t\t\t<EpicVideoInfoProvider\n\t\t\t\t\t\t\t\tepicVideoInfosPromise={data.epicVideoInfosPromise}\n\t\t\t\t\t\t\t>\n\t\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\t<Mdx\n\t\t\t\t\t\t\t\t\t\tcode={data.exercise.finishedCode}\n\t\t\t\t\t\t\t\t\t\tcomponents={mdxComponents}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</EpicVideoInfoProvider>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t// TODO: render a random dad joke...\n\t\t\t\t\t\t\t'No finished instructions yet...'\n\t\t\t\t\t\t)}\n\t\t\t\t\t</article>\n\t\t\t\t\t<ElementScrollRestoration elementQuery={`#${data.articleId}`} />\n\t\t\t\t\t<ProgressToggle\n\t\t\t\t\t\ttype=\"finished\"\n\t\t\t\t\t\texerciseNumber={data.exercise.exerciseNumber}\n\t\t\t\t\t\tclassName=\"h-14 border-t px-6\"\n\t\t\t\t\t/>\n\t\t\t\t\t<div className=\"@container flex h-16 justify-between border-t border-b-4 lg:border-b-0\">\n\t\t\t\t\t\t<div />\n\t\t\t\t\t\t<EditFileOnGitHub\n\t\t\t\t\t\t\tfile={data.exerciseFinished.file}\n\t\t\t\t\t\t\trelativePath={data.exerciseFinished.relativePath}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<NavChevrons prev={data.prevStepLink} next={data.nextStepLink} />\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<Survey\n\t\t\t\t\texerciseFormEmbedUrl={data.exerciseFormEmbedUrl}\n\t\t\t\t\texerciseTitle={data.exercise.title}\n\t\t\t\t/>\n\t\t\t</main>\n\t\t</div>\n\t)\n}\n\nfunction Survey({\n\texerciseFormEmbedUrl,\n\texerciseTitle,\n}: {\n\texerciseFormEmbedUrl: string\n\texerciseTitle: string\n}) {\n\tconst theme = useTheme()\n\tconst [iframeLoaded, setIframeLoaded] = React.useState(false)\n\tconst isOnline = useIsOnline()\n\tif (!isOnline) {\n\t\treturn (\n\t\t\t<div className=\"relative shrink-0\">\n\t\t\t\t<div className=\"text-body-md text-foreground-destructive absolute inset-0 z-10 flex items-center justify-center\">\n\t\t\t\t\t<Icon name=\"WifiNoConnection\" size=\"xl\">\n\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t{'Unable to load the '}\n\t\t\t\t\t\t\t<a href={exerciseFormEmbedUrl} className=\"underline\">\n\t\t\t\t\t\t\t\t{`${exerciseTitle} feedback form`}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t{' when offline'}\n\t\t\t\t\t\t</span>\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=\"relative min-h-full sm:min-h-[unset] sm:shrink-0\">\n\t\t\t{!iframeLoaded ? (\n\t\t\t\t<div className=\"absolute inset-0 z-10 flex items-center justify-center\">\n\t\t\t\t\t<Loading>\n\t\t\t\t\t\t<span>Loading {exerciseTitle} Elaboration form</span>\n\t\t\t\t\t</Loading>\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t\t<iframe\n\t\t\t\tonLoad={() => setIframeLoaded(true)}\n\t\t\t\t// show what would have shown if there is an error\n\t\t\t\tonError={() => setIframeLoaded(true)}\n\t\t\t\ttitle=\"Elaboration\"\n\t\t\t\tsrc={exerciseFormEmbedUrl}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t'absolute inset-0 flex h-full w-full transition-opacity duration-300',\n\t\t\t\t\tiframeLoaded ? 'opacity-100' : 'opacity-0',\n\t\t\t\t)}\n\t\t\t\tstyle={{ colorScheme: theme }}\n\t\t\t/>\n\t\t</div>\n\t)\n}\n"],"names":["meta","loaderData","matches","number","exercise","exerciseNumber","toString","padStart","rootData","getRootMatchLoaderData","title","getSeoMetaTags","workshopTitle","description","ogTitle","ogDescription","Number","instructor","requestInfo","mdxComponents","h1","$exerciseNumber__finished","_UNSAFE_withComponentProps","data","useRevalidationWS","watchPaths","className","children","jsxs","jsx","Link","to","id","articleId","finishedCode","EpicVideoInfoProvider","epicVideoInfosPromise","Mdx","code","components","ElementScrollRestoration","elementQuery","ProgressToggle","type","EditFileOnGitHub","file","exerciseFinished","relativePath","NavChevrons","prev","prevStepLink","next","nextStepLink","Survey","exerciseFormEmbedUrl","exerciseTitle","theme","useTheme","iframeLoaded","setIframeLoaded","React","useIsOnline","Loading","onLoad","onError","src","cn","style","colorScheme","Icon","name","size","href"],"mappings":"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"}
1
+ {"version":3,"file":"_exerciseNumber_.finished-C4sVkuEq.js","sources":["../../../app/routes/_app+/exercise+/$exerciseNumber_.finished.tsx"],"sourcesContent":["import path from 'path'\nimport { invariantResponse } from '@epic-web/invariant'\nimport { ElementScrollRestoration } from '@epic-web/restore-scroll'\nimport {\n\tgetAppPageRoute,\n\tgetApps,\n\tgetExercise,\n\tgetWorkshopRoot,\n\tisExtraApp,\n\tisExerciseStepApp,\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} from '@epic-web/workshop-utils/timing.server'\nimport slugify from '@sindresorhus/slugify'\nimport * as React from 'react'\nimport { data, type HeadersFunction, Link } from 'react-router'\nimport { EpicVideoInfoProvider } from '#app/components/epic-video.tsx'\nimport { Icon } from '#app/components/icons.tsx'\nimport { Loading } from '#app/components/loading.tsx'\nimport { NavChevrons } from '#app/components/nav-chevrons.tsx'\nimport { useRevalidationWS } from '#app/components/revalidation-ws.tsx'\nimport { EditFileOnGitHub } from '#app/routes/launch-editor.tsx'\nimport { ProgressToggle } from '#app/routes/progress.tsx'\nimport { useTheme } from '#app/routes/theme/index.tsx'\nimport { Mdx } from '#app/utils/mdx.tsx'\nimport { cn } from '#app/utils/misc.tsx'\nimport { useIsOnline } from '#app/utils/online.ts'\nimport { getRootMatchLoaderData } from '#app/utils/root-loader.ts'\nimport { getSeoMetaTags } from '#app/utils/seo.ts'\nimport { type Route } from './+types/$exerciseNumber_.finished.tsx'\n\nexport const meta: Route.MetaFunction = ({ loaderData, matches }) => {\n\tconst number = loaderData?.exercise.exerciseNumber.toString().padStart(2, '0')\n\n\tconst rootData = getRootMatchLoaderData(matches)\n\tif (!loaderData || !rootData) return [{ title: '🦉 | Error' }]\n\n\treturn getSeoMetaTags({\n\t\ttitle: `🦉 | ${number}. ${loaderData.exercise.title} | ${rootData?.workshopTitle}`,\n\t\tdescription: `Elaboration for ${number}. ${loaderData.exercise.title}`,\n\t\togTitle: `Finished: ${loaderData.exercise.title}`,\n\t\togDescription: `Elaboration for exercise ${Number(number)}`,\n\t\tinstructor: rootData.instructor,\n\t\trequestInfo: rootData.requestInfo,\n\t})\n}\n\nexport async function loader({ request, params }: Route.LoaderArgs) {\n\tconst timings = makeTimings('exerciseFinishedLoader')\n\tinvariantResponse(params.exerciseNumber, 'exerciseNumber is required')\n\tconst exercise = await getExercise(params.exerciseNumber, {\n\t\ttimings,\n\t\trequest,\n\t})\n\tif (!exercise) {\n\t\tthrow new Response('Not found', { status: 404 })\n\t}\n\tconst workshopConfig = getWorkshopConfig()\n\tconst exerciseFormTemplate = workshopConfig.forms.exercise\n\tconst exerciseFormEmbedUrl = exerciseFormTemplate\n\t\t.replace('{workshopTitle}', encodeURIComponent(workshopConfig.title))\n\t\t.replace('{exerciseTitle}', encodeURIComponent(exercise.title))\n\tconst nextExercise = await getExercise(exercise.exerciseNumber + 1, {\n\t\ttimings,\n\t\trequest,\n\t})\n\n\tconst finishedFilepath = path.join(\n\t\tgetWorkshopRoot(),\n\t\t'exercises',\n\t\texercise.dirName,\n\t\t'FINISHED.mdx',\n\t)\n\n\tconst apps = await getApps({ request, timings })\n\tconst hasExtras = apps.some(isExtraApp)\n\tconst exerciseApps = apps\n\t\t.filter(isExerciseStepApp)\n\t\t.filter((app) => app.exerciseNumber === exercise.exerciseNumber)\n\tconst prevApp = exerciseApps[exerciseApps.length - 1]\n\n\tconst articleId = `workshop-${slugify(workshopConfig.title)}-${\n\t\texercise.exerciseNumber\n\t}-finished`\n\n\treturn data(\n\t\t{\n\t\t\tarticleId,\n\t\t\tworkshopTitle: workshopConfig.title,\n\t\t\texercise,\n\t\t\texerciseFormEmbedUrl,\n\t\t\tepicVideoInfosPromise: getEpicVideoInfos(\n\t\t\t\texercise.finishedEpicVideoEmbeds,\n\t\t\t\t{ request },\n\t\t\t),\n\t\t\texerciseFinished: {\n\t\t\t\tfile: finishedFilepath,\n\t\t\t\trelativePath: `exercises/${exercise.dirName}/FINISHED.mdx`,\n\t\t\t},\n\t\t\tprevStepLink: prevApp\n\t\t\t\t? {\n\t\t\t\t\t\tto: getAppPageRoute(prevApp),\n\t\t\t\t\t\t'aria-label': `${prevApp.title} (${prevApp.type})`,\n\t\t\t\t\t}\n\t\t\t\t: null,\n\t\t\tnextStepLink: nextExercise\n\t\t\t\t? {\n\t\t\t\t\t\tto: `/exercise/${nextExercise.exerciseNumber.toString().padStart(2, '0')}`,\n\t\t\t\t\t\t'aria-label': `${nextExercise.title}`,\n\t\t\t\t\t}\n\t\t\t\t: hasExtras\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tto: '/extra',\n\t\t\t\t\t\t\t'aria-label': 'Extras',\n\t\t\t\t\t\t}\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tto: '/finished',\n\t\t\t\t\t\t\t'aria-label': 'Finished! 🎉',\n\t\t\t\t\t\t},\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\nconst mdxComponents = { h1: () => null }\nexport default function ExerciseFinished({\n\tloaderData: data,\n}: Route.ComponentProps) {\n\tconst exerciseNumber = data.exercise.exerciseNumber\n\t\t.toString()\n\t\t.padStart(2, '0')\n\n\tuseRevalidationWS({\n\t\twatchPaths: [`./exercises/${exerciseNumber}/FINISHED.mdx`],\n\t})\n\n\treturn (\n\t\t<div className=\"flex max-w-full grow flex-col\">\n\t\t\t<main className=\"flex grow flex-col sm:grid sm:h-full sm:min-h-[800px] sm:grid-cols-1 sm:grid-rows-2 md:min-h-[unset] lg:grid-cols-2 lg:grid-rows-1\">\n\t\t\t\t<div className=\"relative flex flex-col sm:col-span-1 sm:row-span-1 sm:h-full lg:border-r\">\n\t\t\t\t\t<h1 className=\"h-14 border-b pr-5 pl-10 text-sm leading-tight font-medium\">\n\t\t\t\t\t\t<div className=\"flex h-14 flex-wrap items-center justify-between gap-x-2 py-2\">\n\t\t\t\t\t\t\t<div className=\"flex items-center justify-start gap-x-2\">\n\t\t\t\t\t\t\t\t<Link to={`/${exerciseNumber}`} className=\"hover:underline\">\n\t\t\t\t\t\t\t\t\t{`${exerciseNumber}. ${data.exercise.title}`}\n\t\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t\t<span>/</span>\n\t\t\t\t\t\t\t\t<span>Elaboration</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</h1>\n\n\t\t\t\t\t<article\n\t\t\t\t\t\tclassName=\"shadow-on-scrollbox scrollbar-thin scrollbar-thumb-scrollbar h-full w-full max-w-none flex-1 scroll-pt-6 space-y-6 overflow-y-auto p-2 sm:p-10 sm:pt-8\"\n\t\t\t\t\t\tid={data.articleId}\n\t\t\t\t\t>\n\t\t\t\t\t\t{data.exercise.finishedCode ? (\n\t\t\t\t\t\t\t<EpicVideoInfoProvider\n\t\t\t\t\t\t\t\tepicVideoInfosPromise={data.epicVideoInfosPromise}\n\t\t\t\t\t\t\t>\n\t\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\t<Mdx\n\t\t\t\t\t\t\t\t\t\tcode={data.exercise.finishedCode}\n\t\t\t\t\t\t\t\t\t\tcomponents={mdxComponents}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</EpicVideoInfoProvider>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t// TODO: render a random dad joke...\n\t\t\t\t\t\t\t'No finished instructions yet...'\n\t\t\t\t\t\t)}\n\t\t\t\t\t</article>\n\t\t\t\t\t<ElementScrollRestoration elementQuery={`#${data.articleId}`} />\n\t\t\t\t\t<ProgressToggle\n\t\t\t\t\t\ttype=\"finished\"\n\t\t\t\t\t\texerciseNumber={data.exercise.exerciseNumber}\n\t\t\t\t\t\tclassName=\"h-14 border-t px-6\"\n\t\t\t\t\t/>\n\t\t\t\t\t<div className=\"@container flex h-16 justify-between border-t border-b-4 lg:border-b-0\">\n\t\t\t\t\t\t<div />\n\t\t\t\t\t\t<EditFileOnGitHub\n\t\t\t\t\t\t\tfile={data.exerciseFinished.file}\n\t\t\t\t\t\t\trelativePath={data.exerciseFinished.relativePath}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<NavChevrons prev={data.prevStepLink} next={data.nextStepLink} />\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<Survey\n\t\t\t\t\texerciseFormEmbedUrl={data.exerciseFormEmbedUrl}\n\t\t\t\t\texerciseTitle={data.exercise.title}\n\t\t\t\t/>\n\t\t\t</main>\n\t\t</div>\n\t)\n}\n\nfunction Survey({\n\texerciseFormEmbedUrl,\n\texerciseTitle,\n}: {\n\texerciseFormEmbedUrl: string\n\texerciseTitle: string\n}) {\n\tconst theme = useTheme()\n\tconst [iframeLoaded, setIframeLoaded] = React.useState(false)\n\tconst isOnline = useIsOnline()\n\tif (!isOnline) {\n\t\treturn (\n\t\t\t<div className=\"relative shrink-0\">\n\t\t\t\t<div className=\"text-body-md text-foreground-destructive absolute inset-0 z-10 flex items-center justify-center\">\n\t\t\t\t\t<Icon name=\"WifiNoConnection\" size=\"xl\">\n\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t{'Unable to load the '}\n\t\t\t\t\t\t\t<a href={exerciseFormEmbedUrl} className=\"underline\">\n\t\t\t\t\t\t\t\t{`${exerciseTitle} feedback form`}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t{' when offline'}\n\t\t\t\t\t\t</span>\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=\"relative min-h-full sm:min-h-[unset] sm:shrink-0\">\n\t\t\t{!iframeLoaded ? (\n\t\t\t\t<div className=\"absolute inset-0 z-10 flex items-center justify-center\">\n\t\t\t\t\t<Loading>\n\t\t\t\t\t\t<span>Loading {exerciseTitle} Elaboration form</span>\n\t\t\t\t\t</Loading>\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t\t<iframe\n\t\t\t\tonLoad={() => setIframeLoaded(true)}\n\t\t\t\t// show what would have shown if there is an error\n\t\t\t\tonError={() => setIframeLoaded(true)}\n\t\t\t\ttitle=\"Elaboration\"\n\t\t\t\tsrc={exerciseFormEmbedUrl}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t'absolute inset-0 flex h-full w-full transition-opacity duration-300',\n\t\t\t\t\tiframeLoaded ? 'opacity-100' : 'opacity-0',\n\t\t\t\t)}\n\t\t\t\tstyle={{ colorScheme: theme }}\n\t\t\t/>\n\t\t</div>\n\t)\n}\n"],"names":["meta","loaderData","matches","number","exercise","exerciseNumber","toString","padStart","rootData","getRootMatchLoaderData","title","getSeoMetaTags","workshopTitle","description","ogTitle","ogDescription","Number","instructor","requestInfo","mdxComponents","h1","$exerciseNumber__finished","_UNSAFE_withComponentProps","data","useRevalidationWS","watchPaths","className","children","jsxs","jsx","Link","to","id","articleId","finishedCode","EpicVideoInfoProvider","epicVideoInfosPromise","Mdx","code","components","ElementScrollRestoration","elementQuery","ProgressToggle","type","EditFileOnGitHub","file","exerciseFinished","relativePath","NavChevrons","prev","prevStepLink","next","nextStepLink","Survey","exerciseFormEmbedUrl","exerciseTitle","theme","useTheme","iframeLoaded","setIframeLoaded","React","useIsOnline","Loading","onLoad","onError","src","cn","style","colorScheme","Icon","name","size","href"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{w as R,a as S,b as T,d as k,L as l}from"./chunk-EPOLDU6W-BCLmut3y.js";import{j as e}from"./jsx-runtime-C5WNSv3b.js";import{E as $}from"./index-CEVyDj51.js";import{g as A,R as L,P as M,C as n,a as B,D as H}from"./playground-DmEAkxG1.js";import{r as i}from"./index-CqIc3cxq.js";import{U as O}from"./diff-HIgoC1PO.js";import{E as U}from"./epic-video-DnXXh6qE.js";import{G as V}from"./error-boundary-DVp6wKiz.js";import{N as z}from"./nav-chevrons-Dk4GtZwQ.js";import{u as X}from"./revalidation-ws-BJWJviUX.js";import{u as F}from"./workshop-config-Zfc8zU0x.js";import{P as G}from"./preview-fhmjENlm.js";import{E as W}from"./launch-editor-D2exGfVu.js";import{S as q}from"./set-playground-BSGwH9dH.js";import{c as Y,M as _}from"./mdx-Dun3ONG_.js";import{g as Q,u as J}from"./root-loader-BOzEMapJ.js";import{g as K}from"./seo-t5J-DRxw.js";import{s as w,a as Z}from"./split-layout-DnRER1bP.js";import"./misc-W4055b-0.js";import"./loading-CDNzW5oO.js";import"./index-CdzVFL-Z.js";import"./tooltip-Tlsyx2YO.js";import"./index-vDCSPjrM.js";import"./pe-CIZUOJMr.js";import"./schemas-Uj5SZtvt.js";import"./discord-BJkw0IrB.js";import"./user-BsPobzjB.js";import"./online-DiNLkgTC.js";import"./index-CJDOQ1bl.js";import"./index-ynYvVAOK.js";import"./status-indicator-C6DiLYL5.js";import"./index-DzdDahau.js";import"./playground-window-x2mQ5o1O.js";import"./accordion-CQ7oujC6.js";import"./use-event-source-BuD4_2SF.js";import"./preload-helper-BXl3LOEh.js";import"./button-Cd-ekki5.js";import"./progress-bar-DpWhcyhC.js";import"./onboarding-indicator-B-XR90_G.js";import"./dialog-CzO65Z5w.js";const Ue=o=>{const r=o.data,s=Q(o.matches);return!r||!s?[{title:"🦉 | Error"}]:K({title:`📚 | ${r.extra.title} | ${s.workshopTitle}`,description:`Extra: ${r.extra.title}`,ogTitle:r.extra.title,ogDescription:`Extra: ${r.extra.title}`,instructor:s.instructor,requestInfo:s.requestInfo})},Ve=R(function(){const r=T(),s=J(),h=i.useRef(null),c=i.useRef(null),g=i.useRef(null),[P,d]=i.useState(r.splitPercent),[j]=k(),b=F(),y=i.useMemo(()=>Promise.resolve(s.userHasAccess??!1),[s.userHasAccess]),v=r.playground?.appName!==r.extra.name,N=v||r.playground?.isUpToDate===!1,m=["playground","extra","diff","chat"],p=j.get("preview"),x=r.previousExtra?{to:`/extra/${r.previousExtra.dirName}`,"aria-label":"Previous Extra"}:{to:"/extra","aria-label":"Extras"},u=r.nextExtra?{to:`/extra/${r.nextExtra.dirName}`,"aria-label":"Next Extra"}:{to:"/finished","aria-label":"Workshop finished"};function E(t){return!!(t&&m.includes(t))}function f(t){if(t==="playground")return ENV.EPICSHOP_DEPLOYED;if(t==="extra"&&ENV.EPICSHOP_DEPLOYED){const a=r.extra.dev.type;return a!=="browser"&&a!=="export"&&!r.extra.stackBlitzUrl}return t==="chat"?!b.product.discordChannelId:!1}const D=E(p)&&!f(p)?p:m.find(t=>!f(t))??"playground",C=m.map(t=>{const a=f(t);return{id:t,label:t,hidden:a,to:`?${A(j,t,"playground")}`}}),I=i.useMemo(()=>{const t=Y(()=>({name:r.extra.name,fullPath:r.extra.fullPath}));return{h1:()=>null,InlineFile:t}},[r.extra.name,r.extra.fullPath]);return X({watchPaths:[r.extraReadme.file]}),e.jsx("div",{className:"flex max-w-full grow flex-col",children:e.jsxs("main",{ref:c,className:"flex grow flex-col sm:h-full sm:min-h-[800px] md:min-h-[unset] lg:flex-row",children:[e.jsxs("div",{className:"relative flex min-w-0 flex-none basis-full flex-col sm:col-span-1 sm:row-span-1 sm:h-full lg:basis-(--split-pct)",style:{"--split-pct":`${P}%`},ref:g,children:[e.jsx("h1",{className:"@container h-14 border-b pr-5 pl-10 text-sm leading-tight font-medium",children:e.jsxs("div",{className:"flex h-14 items-center justify-between gap-x-2 py-2 whitespace-nowrap",children:[e.jsxs("div",{className:"flex items-center justify-start gap-x-2 uppercase",children:[e.jsx(l,{to:"/extra",className:"hover:underline",children:e.jsx("span",{children:"Extras"})}),e.jsx("span",{children:"/"}),e.jsx(l,{to:".",className:"hover:underline",children:e.jsx("span",{children:r.extra.title})})]}),N?e.jsx(q,{appName:r.extra.name,isOutdated:r.playground?.isUpToDate===!1,hideTextOnNarrow:!0,showOnboardingIndicator:v}):null]})}),e.jsxs("article",{id:r.articleId,className:"shadow-on-scrollbox scrollbar-thin scrollbar-thumb-scrollbar flex h-full w-full max-w-none flex-1 scroll-pt-6 flex-col justify-between space-y-6 overflow-y-auto p-2 sm:p-10 sm:pt-8",children:[r.extra.instructionsCode?e.jsx(U,{epicVideoInfosPromise:r.epicVideoInfosPromise,children:e.jsx("div",{className:"prose dark:prose-invert sm:prose-lg",children:e.jsx(_,{code:r.extra.instructionsCode,components:I})})}):e.jsx("div",{className:"flex h-full items-center justify-center text-lg",children:e.jsx("p",{children:"No instructions yet..."})}),e.jsxs("div",{className:"mt-auto flex justify-between",children:[e.jsxs(l,{to:x.to,"aria-label":x["aria-label"],prefetch:"intent",children:[e.jsx("span",{"aria-hidden":!0,children:"←"}),e.jsx("span",{className:"hidden xl:inline",children:" Previous"})]}),e.jsxs(l,{to:u.to,"aria-label":u["aria-label"],prefetch:"intent",children:[e.jsx("span",{className:"hidden xl:inline",children:"Next "}),e.jsx("span",{"aria-hidden":!0,children:"→"})]})]})]},r.articleId),e.jsx($,{elementQuery:`#${r.articleId}`},`scroll-${r.articleId}`),e.jsxs("div",{className:"@container flex h-16 justify-between border-t border-b-4 lg:border-b-0",children:[e.jsx("div",{}),e.jsx(W,{appName:r.extra.name,relativePath:r.extraReadme.relativePath}),e.jsx(z,{prev:x,next:u})]})]}),e.jsx("div",{role:"separator","aria-orientation":"vertical",title:"Drag to resize",className:"bg-border hover:bg-muted hidden w-1 cursor-col-resize lg:block",onMouseDown:t=>w({container:c.current,initialClientX:t.clientX,setSplitPercent:d}),onDoubleClick:()=>{d(Z(50))},onTouchStart:t=>{const a=t.touches?.[0];a&&w({container:c.current,initialClientX:a.clientX,setSplitPercent:d})}}),e.jsxs(L,{className:"relative flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden",value:D,children:[e.jsx(M,{tabs:C}),e.jsxs("div",{className:"relative z-10 flex min-h-0 flex-1 flex-col overflow-hidden",children:[e.jsx(n,{value:"playground",className:"radix-state-inactive:hidden flex min-h-0 w-full grow basis-0 items-stretch justify-center self-start",forceMount:!0,children:e.jsx(B,{appInfo:r.playground,problemAppName:r.extra.name,allApps:r.allApps??[],isUpToDate:r.playground?.isUpToDate??!1,inBrowserBrowserRef:h})}),e.jsx(n,{value:"extra",className:"radix-state-inactive:hidden flex min-h-0 w-full grow basis-0 items-stretch justify-center self-start",forceMount:!0,children:e.jsx(G,{appInfo:r.extra,inBrowserBrowserRef:h})}),e.jsx(n,{value:"diff",className:"radix-state-inactive:hidden flex h-full min-h-0 w-full grow basis-0 items-stretch justify-center self-start",children:e.jsx(O,{diff:r.diff,allApps:r.allApps,userHasAccessPromise:y})}),e.jsx(n,{value:"chat",className:"radix-state-inactive:hidden flex h-full min-h-0 w-full grow basis-0 items-stretch justify-center self-start",children:e.jsx(H,{discordPostsPromise:r.discordPostsPromise})})]})]})]})})}),ze=S(function(){return e.jsx(V,{})});export{ze as ErrorBoundary,Ve as default,Ue as meta};
2
- //# sourceMappingURL=_extra-UkIdJCI9.js.map
1
+ import{w as R,a as S,b as T,d as k,L as l}from"./chunk-EPOLDU6W-BCLmut3y.js";import{j as e}from"./jsx-runtime-C5WNSv3b.js";import{E as $}from"./index-CEVyDj51.js";import{g as A,R as L,P as M,C as n,a as B,D as H}from"./playground-DmEAkxG1.js";import{r as i}from"./index-CqIc3cxq.js";import{U as O}from"./diff-HIgoC1PO.js";import{E as U}from"./epic-video-DnXXh6qE.js";import{G as V}from"./error-boundary-DxHqAEHX.js";import{N as z}from"./nav-chevrons-Dk4GtZwQ.js";import{u as X}from"./revalidation-ws-BJWJviUX.js";import{u as F}from"./workshop-config-Zfc8zU0x.js";import{P as G}from"./preview-fhmjENlm.js";import{E as W}from"./launch-editor-D2exGfVu.js";import{S as q}from"./set-playground-BSGwH9dH.js";import{c as Y,M as _}from"./mdx-Dun3ONG_.js";import{g as Q,u as J}from"./root-loader-BOzEMapJ.js";import{g as K}from"./seo-t5J-DRxw.js";import{s as w,a as Z}from"./split-layout-DnRER1bP.js";import"./misc-W4055b-0.js";import"./loading-CDNzW5oO.js";import"./index-CdzVFL-Z.js";import"./tooltip-Tlsyx2YO.js";import"./index-vDCSPjrM.js";import"./pe-CIZUOJMr.js";import"./schemas-Uj5SZtvt.js";import"./discord-BJkw0IrB.js";import"./user-BsPobzjB.js";import"./online-DiNLkgTC.js";import"./index-CJDOQ1bl.js";import"./index-ynYvVAOK.js";import"./status-indicator-C6DiLYL5.js";import"./index-DzdDahau.js";import"./playground-window-x2mQ5o1O.js";import"./accordion-CQ7oujC6.js";import"./use-event-source-BuD4_2SF.js";import"./preload-helper-BXl3LOEh.js";import"./button-Cd-ekki5.js";import"./progress-bar-DpWhcyhC.js";import"./onboarding-indicator-B-XR90_G.js";import"./dialog-CzO65Z5w.js";const Ue=o=>{const r=o.data,s=Q(o.matches);return!r||!s?[{title:"🦉 | Error"}]:K({title:`📚 | ${r.extra.title} | ${s.workshopTitle}`,description:`Extra: ${r.extra.title}`,ogTitle:r.extra.title,ogDescription:`Extra: ${r.extra.title}`,instructor:s.instructor,requestInfo:s.requestInfo})},Ve=R(function(){const r=T(),s=J(),h=i.useRef(null),c=i.useRef(null),g=i.useRef(null),[P,d]=i.useState(r.splitPercent),[j]=k(),b=F(),y=i.useMemo(()=>Promise.resolve(s.userHasAccess??!1),[s.userHasAccess]),v=r.playground?.appName!==r.extra.name,N=v||r.playground?.isUpToDate===!1,m=["playground","extra","diff","chat"],p=j.get("preview"),x=r.previousExtra?{to:`/extra/${r.previousExtra.dirName}`,"aria-label":"Previous Extra"}:{to:"/extra","aria-label":"Extras"},u=r.nextExtra?{to:`/extra/${r.nextExtra.dirName}`,"aria-label":"Next Extra"}:{to:"/finished","aria-label":"Workshop finished"};function E(t){return!!(t&&m.includes(t))}function f(t){if(t==="playground")return ENV.EPICSHOP_DEPLOYED;if(t==="extra"&&ENV.EPICSHOP_DEPLOYED){const a=r.extra.dev.type;return a!=="browser"&&a!=="export"&&!r.extra.stackBlitzUrl}return t==="chat"?!b.product.discordChannelId:!1}const D=E(p)&&!f(p)?p:m.find(t=>!f(t))??"playground",C=m.map(t=>{const a=f(t);return{id:t,label:t,hidden:a,to:`?${A(j,t,"playground")}`}}),I=i.useMemo(()=>{const t=Y(()=>({name:r.extra.name,fullPath:r.extra.fullPath}));return{h1:()=>null,InlineFile:t}},[r.extra.name,r.extra.fullPath]);return X({watchPaths:[r.extraReadme.file]}),e.jsx("div",{className:"flex max-w-full grow flex-col",children:e.jsxs("main",{ref:c,className:"flex grow flex-col sm:h-full sm:min-h-[800px] md:min-h-[unset] lg:flex-row",children:[e.jsxs("div",{className:"relative flex min-w-0 flex-none basis-full flex-col sm:col-span-1 sm:row-span-1 sm:h-full lg:basis-(--split-pct)",style:{"--split-pct":`${P}%`},ref:g,children:[e.jsx("h1",{className:"@container h-14 border-b pr-5 pl-10 text-sm leading-tight font-medium",children:e.jsxs("div",{className:"flex h-14 items-center justify-between gap-x-2 py-2 whitespace-nowrap",children:[e.jsxs("div",{className:"flex items-center justify-start gap-x-2 uppercase",children:[e.jsx(l,{to:"/extra",className:"hover:underline",children:e.jsx("span",{children:"Extras"})}),e.jsx("span",{children:"/"}),e.jsx(l,{to:".",className:"hover:underline",children:e.jsx("span",{children:r.extra.title})})]}),N?e.jsx(q,{appName:r.extra.name,isOutdated:r.playground?.isUpToDate===!1,hideTextOnNarrow:!0,showOnboardingIndicator:v}):null]})}),e.jsxs("article",{id:r.articleId,className:"shadow-on-scrollbox scrollbar-thin scrollbar-thumb-scrollbar flex h-full w-full max-w-none flex-1 scroll-pt-6 flex-col justify-between space-y-6 overflow-y-auto p-2 sm:p-10 sm:pt-8",children:[r.extra.instructionsCode?e.jsx(U,{epicVideoInfosPromise:r.epicVideoInfosPromise,children:e.jsx("div",{className:"prose dark:prose-invert sm:prose-lg",children:e.jsx(_,{code:r.extra.instructionsCode,components:I})})}):e.jsx("div",{className:"flex h-full items-center justify-center text-lg",children:e.jsx("p",{children:"No instructions yet..."})}),e.jsxs("div",{className:"mt-auto flex justify-between",children:[e.jsxs(l,{to:x.to,"aria-label":x["aria-label"],prefetch:"intent",children:[e.jsx("span",{"aria-hidden":!0,children:"←"}),e.jsx("span",{className:"hidden xl:inline",children:" Previous"})]}),e.jsxs(l,{to:u.to,"aria-label":u["aria-label"],prefetch:"intent",children:[e.jsx("span",{className:"hidden xl:inline",children:"Next "}),e.jsx("span",{"aria-hidden":!0,children:"→"})]})]})]},r.articleId),e.jsx($,{elementQuery:`#${r.articleId}`},`scroll-${r.articleId}`),e.jsxs("div",{className:"@container flex h-16 justify-between border-t border-b-4 lg:border-b-0",children:[e.jsx("div",{}),e.jsx(W,{appName:r.extra.name,relativePath:r.extraReadme.relativePath}),e.jsx(z,{prev:x,next:u})]})]}),e.jsx("div",{role:"separator","aria-orientation":"vertical",title:"Drag to resize",className:"bg-border hover:bg-muted hidden w-1 cursor-col-resize lg:block",onMouseDown:t=>w({container:c.current,initialClientX:t.clientX,setSplitPercent:d}),onDoubleClick:()=>{d(Z(50))},onTouchStart:t=>{const a=t.touches?.[0];a&&w({container:c.current,initialClientX:a.clientX,setSplitPercent:d})}}),e.jsxs(L,{className:"relative flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden",value:D,children:[e.jsx(M,{tabs:C}),e.jsxs("div",{className:"relative z-10 flex min-h-0 flex-1 flex-col overflow-hidden",children:[e.jsx(n,{value:"playground",className:"radix-state-inactive:hidden flex min-h-0 w-full grow basis-0 items-stretch justify-center self-start",forceMount:!0,children:e.jsx(B,{appInfo:r.playground,problemAppName:r.extra.name,allApps:r.allApps??[],isUpToDate:r.playground?.isUpToDate??!1,inBrowserBrowserRef:h})}),e.jsx(n,{value:"extra",className:"radix-state-inactive:hidden flex min-h-0 w-full grow basis-0 items-stretch justify-center self-start",forceMount:!0,children:e.jsx(G,{appInfo:r.extra,inBrowserBrowserRef:h})}),e.jsx(n,{value:"diff",className:"radix-state-inactive:hidden flex h-full min-h-0 w-full grow basis-0 items-stretch justify-center self-start",children:e.jsx(O,{diff:r.diff,allApps:r.allApps,userHasAccessPromise:y})}),e.jsx(n,{value:"chat",className:"radix-state-inactive:hidden flex h-full min-h-0 w-full grow basis-0 items-stretch justify-center self-start",children:e.jsx(H,{discordPostsPromise:r.discordPostsPromise})})]})]})]})})}),ze=S(function(){return e.jsx(V,{})});export{ze as ErrorBoundary,Ve as default,Ue as meta};
2
+ //# sourceMappingURL=_extra-DkiDFYVP.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"_extra-UkIdJCI9.js","sources":["../../../app/routes/_app+/extra+/$extra.tsx"],"sourcesContent":["import path from 'path'\nimport { invariantResponse } from '@epic-web/invariant'\nimport { ElementScrollRestoration } from '@epic-web/restore-scroll'\nimport {\n\tgetAppByName,\n\tgetAppDisplayName,\n\tgetApps,\n\tisExtraApp,\n\tisPlaygroundApp,\n\ttype ExtraApp,\n} from '@epic-web/workshop-utils/apps.server'\nimport { getWorkshopConfig } from '@epic-web/workshop-utils/config.server'\nimport { getDiffCode } from '@epic-web/workshop-utils/diff.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 * as Tabs from '@radix-ui/react-tabs'\nimport slugify from '@sindresorhus/slugify'\nimport { useMemo, useRef, useState } from 'react'\nimport {\n\tLink,\n\tdata,\n\ttype HeadersFunction,\n\ttype LoaderFunctionArgs,\n\ttype MetaFunction,\n\tuseLoaderData,\n\tuseSearchParams,\n} from 'react-router'\nimport { Diff } from '#app/components/diff.tsx'\nimport { DiscordChat } from '#app/components/discord-chat.tsx'\nimport { EpicVideoInfoProvider } from '#app/components/epic-video.tsx'\nimport { GeneralErrorBoundary } from '#app/components/error-boundary.tsx'\nimport { type InBrowserBrowserRef } from '#app/components/in-browser-browser.tsx'\nimport { NavChevrons } from '#app/components/nav-chevrons.tsx'\nimport {\n\tgetPreviewSearchParams,\n\tPreviewTabsList,\n} from '#app/components/preview-tabs.tsx'\nimport { useRevalidationWS } from '#app/components/revalidation-ws.tsx'\nimport { useWorkshopConfig } from '#app/components/workshop-config.tsx'\nimport { Playground } from '#app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/__shared/playground.tsx'\nimport { Preview } from '#app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/__shared/preview.tsx'\nimport { getAppRunningState } from '#app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/__shared/utils.tsx'\nimport { EditFileOnGitHub } from '#app/routes/launch-editor.tsx'\nimport { SetAppToPlayground } from '#app/routes/set-playground.tsx'\nimport { fetchDiscordPosts } from '#app/utils/discord.server.ts'\nimport { createInlineFileComponent, Mdx } from '#app/utils/mdx.tsx'\nimport {\n\tgetRootMatchLoaderData,\n\tuseRootLoaderData,\n} from '#app/utils/root-loader.ts'\nimport { getSeoMetaTags } from '#app/utils/seo.ts'\nimport {\n\tgetSplitPercentFromRequest,\n\tsetSplitPercentCookie,\n\tstartSplitDrag,\n} from '#app/utils/split-layout.ts'\n\nfunction sortExtras(extras: ExtraApp[]) {\n\treturn extras.sort((a, b) =>\n\t\ta.title.localeCompare(b.title, undefined, {\n\t\t\tnumeric: true,\n\t\t\tsensitivity: 'base',\n\t\t}),\n\t)\n}\n\nexport const meta: MetaFunction<typeof loader> = (args) => {\n\tconst loaderData = args.data\n\tconst rootData = getRootMatchLoaderData(args.matches)\n\tif (!loaderData || !rootData) return [{ title: '🦉 | Error' }]\n\n\treturn getSeoMetaTags({\n\t\ttitle: `📚 | ${loaderData.extra.title} | ${rootData.workshopTitle}`,\n\t\tdescription: `Extra: ${loaderData.extra.title}`,\n\t\togTitle: loaderData.extra.title,\n\t\togDescription: `Extra: ${loaderData.extra.title}`,\n\t\tinstructor: rootData.instructor,\n\t\trequestInfo: rootData.requestInfo,\n\t})\n}\n\nexport async function loader({ request, params }: LoaderFunctionArgs) {\n\tconst timings = makeTimings('extraLoader')\n\tinvariantResponse(params.extra, 'extra is required')\n\n\tconst { title: workshopTitle } = getWorkshopConfig()\n\tconst cacheOptions = { request, timings }\n\tconst apps = await time(() => getApps(cacheOptions), {\n\t\ttimings,\n\t\ttype: 'getApps',\n\t\tdesc: 'getApps in extra loader',\n\t})\n\tconst extras = sortExtras(apps.filter(isExtraApp))\n\tconst playgroundApp = apps.find(isPlaygroundApp)\n\tconst extraIndex = extras.findIndex((extra) => extra.dirName === params.extra)\n\tconst extra = extras[extraIndex]\n\tif (!extra) {\n\t\tthrow new Response('Extra not found', { status: 404 })\n\t}\n\n\tconst readmeFilepath = path.join(extra.fullPath, 'README.mdx')\n\tconst previousExtra = extras[extraIndex - 1]\n\tconst nextExtra = extras[extraIndex + 1]\n\tconst reqUrl = new URL(request.url)\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 ?? extra)\n\tconst app2 = app2Name ? await getAppByName(app2Name) : extra\n\tconst splitPercent = getSplitPercentFromRequest(request, 50)\n\n\tconst { isRunning, portIsAvailable } = await getAppRunningState(extra)\n\n\tasync function getDiffProp() {\n\t\tif (!app1 || !app2) {\n\t\t\treturn { app1: app1?.name, app2: app2?.name, diffCode: null }\n\t\t}\n\t\tconst diffCode = await getDiffCode(app1, app2, {\n\t\t\t...cacheOptions,\n\t\t\tforceFresh: reqUrl.searchParams.get('forceFresh') === 'diff',\n\t\t}).catch((error) => {\n\t\t\tconsole.error(error)\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\tconst allApps = apps\n\t\t.filter(\n\t\t\t(app, index, list) =>\n\t\t\t\tlist.findIndex((item) => item.name === app.name) === index,\n\t\t)\n\t\t.map((app) => ({\n\t\t\tname: app.name,\n\t\t\tdisplayName: getAppDisplayName(app, apps),\n\t\t}))\n\t\t.sort((a, b) =>\n\t\t\ta.displayName.localeCompare(b.displayName, undefined, {\n\t\t\t\tnumeric: true,\n\t\t\t\tsensitivity: 'base',\n\t\t\t}),\n\t\t)\n\n\treturn data(\n\t\t{\n\t\t\tarticleId: `workshop-${slugify(workshopTitle)}-${slugify(\n\t\t\t\textra.title,\n\t\t\t)}-extra`,\n\t\t\tsplitPercent,\n\t\t\textra: {\n\t\t\t\ttype: 'extra',\n\t\t\t\tname: extra.name,\n\t\t\t\ttitle: extra.title,\n\t\t\t\tdirName: extra.dirName,\n\t\t\t\tfullPath: extra.fullPath,\n\t\t\t\trelativePath: extra.relativePath,\n\t\t\t\tdev: extra.dev,\n\t\t\t\ttest: extra.test,\n\t\t\t\tstackBlitzUrl: extra.stackBlitzUrl,\n\t\t\t\tisRunning,\n\t\t\t\tportIsAvailable,\n\t\t\t\tepicVideoEmbeds: extra.epicVideoEmbeds,\n\t\t\t\tinstructionsCode: extra.instructionsCode,\n\t\t\t},\n\t\t\textraReadme: {\n\t\t\t\tfile: readmeFilepath,\n\t\t\t\trelativePath: path.join(extra.relativePath, 'README.mdx'),\n\t\t\t},\n\t\t\tplayground: playgroundApp\n\t\t\t\t? ({\n\t\t\t\t\t\ttype: 'playground',\n\t\t\t\t\t\tappName: playgroundApp.appName,\n\t\t\t\t\t\tname: playgroundApp.name,\n\t\t\t\t\t\ttitle: playgroundApp.title,\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\tstackBlitzUrl: playgroundApp.stackBlitzUrl,\n\t\t\t\t\t\tisUpToDate: playgroundApp.isUpToDate,\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\tallApps,\n\t\t\tdiff: getDiffProp(),\n\t\t\tdiscordPostsPromise: fetchDiscordPosts({ request }),\n\t\t\tpreviousExtra: previousExtra\n\t\t\t\t? { dirName: previousExtra.dirName, title: previousExtra.title }\n\t\t\t\t: null,\n\t\t\tnextExtra: nextExtra\n\t\t\t\t? { dirName: nextExtra.dirName, title: nextExtra.title }\n\t\t\t\t: null,\n\t\t\tepicVideoInfosPromise: getEpicVideoInfos(extra.epicVideoEmbeds, {\n\t\t\t\trequest,\n\t\t\t}),\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\nexport default function ExtraRoute() {\n\tconst data = useLoaderData<typeof loader>()\n\tconst rootData = useRootLoaderData()\n\tconst inBrowserBrowserRef = useRef<InBrowserBrowserRef>(null)\n\tconst containerRef = useRef<HTMLDivElement>(null)\n\tconst leftPaneRef = useRef<HTMLDivElement>(null)\n\tconst [splitPercent, setSplitPercent] = useState<number>(data.splitPercent)\n\tconst [searchParams] = useSearchParams()\n\tconst workshopConfig = useWorkshopConfig()\n\tconst userHasAccessPromise = useMemo(\n\t\t() => Promise.resolve(rootData.userHasAccess ?? false),\n\t\t[rootData.userHasAccess],\n\t)\n\tconst showPlaygroundIndicator = data.playground?.appName !== data.extra.name\n\tconst shouldShowSetPlayground =\n\t\tshowPlaygroundIndicator || data.playground?.isUpToDate === false\n\tconst tabs = ['playground', 'extra', 'diff', 'chat'] as const\n\tconst preview = searchParams.get('preview')\n\tconst previousExtraLink = data.previousExtra\n\t\t? {\n\t\t\t\tto: `/extra/${data.previousExtra.dirName}`,\n\t\t\t\t'aria-label': 'Previous Extra',\n\t\t\t}\n\t\t: {\n\t\t\t\tto: '/extra',\n\t\t\t\t'aria-label': 'Extras',\n\t\t\t}\n\tconst nextExtraLink = data.nextExtra\n\t\t? {\n\t\t\t\tto: `/extra/${data.nextExtra.dirName}`,\n\t\t\t\t'aria-label': 'Next Extra',\n\t\t\t}\n\t\t: {\n\t\t\t\tto: '/finished',\n\t\t\t\t'aria-label': 'Workshop finished',\n\t\t\t}\n\n\tfunction isValidPreview(\n\t\tvalue: string | null,\n\t): value is (typeof tabs)[number] {\n\t\treturn Boolean(value && tabs.includes(value as (typeof tabs)[number]))\n\t}\n\n\tfunction shouldHideTab(tab: (typeof tabs)[number]) {\n\t\tif (tab === 'playground') {\n\t\t\treturn ENV.EPICSHOP_DEPLOYED\n\t\t}\n\t\tif (tab === 'extra') {\n\t\t\tif (ENV.EPICSHOP_DEPLOYED) {\n\t\t\t\tconst devType = data.extra.dev.type\n\t\t\t\treturn (\n\t\t\t\t\tdevType !== 'browser' &&\n\t\t\t\t\tdevType !== 'export' &&\n\t\t\t\t\t!data.extra.stackBlitzUrl\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\tif (tab === 'chat') {\n\t\t\treturn !workshopConfig.product.discordChannelId\n\t\t}\n\t\treturn false\n\t}\n\n\tconst activeTab =\n\t\tisValidPreview(preview) && !shouldHideTab(preview)\n\t\t\t? preview\n\t\t\t: (tabs.find((tab) => !shouldHideTab(tab)) ?? 'playground')\n\n\tconst previewTabs = tabs.map((tab) => {\n\t\tconst hidden = shouldHideTab(tab)\n\t\treturn {\n\t\t\tid: tab,\n\t\t\tlabel: tab,\n\t\t\thidden,\n\t\t\tto: `?${getPreviewSearchParams(searchParams, tab, 'playground')}`,\n\t\t}\n\t})\n\n\t// Create MDX components with extra-specific InlineFile\n\tconst mdxComponents = useMemo(() => {\n\t\tconst InlineFile = createInlineFileComponent(() => ({\n\t\t\tname: data.extra.name,\n\t\t\tfullPath: data.extra.fullPath,\n\t\t}))\n\t\treturn {\n\t\t\t// we'll render the title ourselves thank you\n\t\t\th1: () => null,\n\t\t\tInlineFile,\n\t\t}\n\t}, [data.extra.name, data.extra.fullPath])\n\n\tuseRevalidationWS({\n\t\twatchPaths: [data.extraReadme.file],\n\t})\n\n\treturn (\n\t\t<div className=\"flex max-w-full grow flex-col\">\n\t\t\t<main\n\t\t\t\tref={containerRef}\n\t\t\t\tclassName=\"flex grow flex-col sm:h-full sm:min-h-[800px] md:min-h-[unset] lg:flex-row\"\n\t\t\t>\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"relative flex min-w-0 flex-none basis-full flex-col sm:col-span-1 sm:row-span-1 sm:h-full lg:basis-(--split-pct)\"\n\t\t\t\t\tstyle={{ ['--split-pct' as any]: `${splitPercent}%` }}\n\t\t\t\t\tref={leftPaneRef}\n\t\t\t\t>\n\t\t\t\t\t<h1 className=\"@container h-14 border-b pr-5 pl-10 text-sm leading-tight font-medium\">\n\t\t\t\t\t\t<div className=\"flex h-14 items-center justify-between gap-x-2 py-2 whitespace-nowrap\">\n\t\t\t\t\t\t\t<div className=\"flex items-center justify-start gap-x-2 uppercase\">\n\t\t\t\t\t\t\t\t<Link to=\"/extra\" className=\"hover:underline\">\n\t\t\t\t\t\t\t\t\t<span>Extras</span>\n\t\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t\t<span>/</span>\n\t\t\t\t\t\t\t\t<Link to=\".\" className=\"hover:underline\">\n\t\t\t\t\t\t\t\t\t<span>{data.extra.title}</span>\n\t\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t{shouldShowSetPlayground ? (\n\t\t\t\t\t\t\t\t<SetAppToPlayground\n\t\t\t\t\t\t\t\t\tappName={data.extra.name}\n\t\t\t\t\t\t\t\t\tisOutdated={data.playground?.isUpToDate === false}\n\t\t\t\t\t\t\t\t\thideTextOnNarrow\n\t\t\t\t\t\t\t\t\tshowOnboardingIndicator={showPlaygroundIndicator}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</h1>\n\t\t\t\t\t<article\n\t\t\t\t\t\tid={data.articleId}\n\t\t\t\t\t\tkey={data.articleId}\n\t\t\t\t\t\tclassName=\"shadow-on-scrollbox scrollbar-thin scrollbar-thumb-scrollbar flex h-full w-full max-w-none flex-1 scroll-pt-6 flex-col justify-between space-y-6 overflow-y-auto p-2 sm:p-10 sm:pt-8\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{data.extra.instructionsCode ? (\n\t\t\t\t\t\t\t<EpicVideoInfoProvider\n\t\t\t\t\t\t\t\tepicVideoInfosPromise={data.epicVideoInfosPromise}\n\t\t\t\t\t\t\t>\n\t\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\t<Mdx\n\t\t\t\t\t\t\t\t\t\tcode={data.extra.instructionsCode}\n\t\t\t\t\t\t\t\t\t\tcomponents={mdxComponents}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</EpicVideoInfoProvider>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<div className=\"flex h-full items-center justify-center text-lg\">\n\t\t\t\t\t\t\t\t<p>No instructions yet...</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t<div className=\"mt-auto flex justify-between\">\n\t\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\t\tto={previousExtraLink.to}\n\t\t\t\t\t\t\t\taria-label={previousExtraLink['aria-label']}\n\t\t\t\t\t\t\t\tprefetch=\"intent\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span aria-hidden>←</span>\n\t\t\t\t\t\t\t\t<span className=\"hidden xl:inline\"> Previous</span>\n\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\t\tto={nextExtraLink.to}\n\t\t\t\t\t\t\t\taria-label={nextExtraLink['aria-label']}\n\t\t\t\t\t\t\t\tprefetch=\"intent\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span className=\"hidden xl:inline\">Next </span>\n\t\t\t\t\t\t\t\t<span aria-hidden>→</span>\n\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</article>\n\t\t\t\t\t<ElementScrollRestoration\n\t\t\t\t\t\telementQuery={`#${data.articleId}`}\n\t\t\t\t\t\tkey={`scroll-${data.articleId}`}\n\t\t\t\t\t/>\n\t\t\t\t\t<div className=\"@container flex h-16 justify-between border-t border-b-4 lg:border-b-0\">\n\t\t\t\t\t\t<div />\n\t\t\t\t\t\t<EditFileOnGitHub\n\t\t\t\t\t\t\tappName={data.extra.name}\n\t\t\t\t\t\t\trelativePath={data.extraReadme.relativePath}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<NavChevrons prev={previousExtraLink} next={nextExtraLink} />\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div\n\t\t\t\t\trole=\"separator\"\n\t\t\t\t\taria-orientation=\"vertical\"\n\t\t\t\t\ttitle=\"Drag to resize\"\n\t\t\t\t\tclassName=\"bg-border hover:bg-muted hidden w-1 cursor-col-resize lg:block\"\n\t\t\t\t\tonMouseDown={(event) =>\n\t\t\t\t\t\tstartSplitDrag({\n\t\t\t\t\t\t\tcontainer: containerRef.current,\n\t\t\t\t\t\t\tinitialClientX: event.clientX,\n\t\t\t\t\t\t\tsetSplitPercent,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\tonDoubleClick={() => {\n\t\t\t\t\t\tsetSplitPercent(setSplitPercentCookie(50))\n\t\t\t\t\t}}\n\t\t\t\t\tonTouchStart={(event) => {\n\t\t\t\t\t\tconst firstTouch = event.touches?.[0]\n\t\t\t\t\t\tif (!firstTouch) return\n\t\t\t\t\t\tstartSplitDrag({\n\t\t\t\t\t\t\tcontainer: containerRef.current,\n\t\t\t\t\t\t\tinitialClientX: firstTouch.clientX,\n\t\t\t\t\t\t\tsetSplitPercent,\n\t\t\t\t\t\t})\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t\t<Tabs.Root\n\t\t\t\t\tclassName=\"relative flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\"\n\t\t\t\t\tvalue={activeTab}\n\t\t\t\t>\n\t\t\t\t\t<PreviewTabsList tabs={previewTabs} />\n\t\t\t\t\t<div className=\"relative z-10 flex min-h-0 flex-1 flex-col overflow-hidden\">\n\t\t\t\t\t\t<Tabs.Content\n\t\t\t\t\t\t\tvalue=\"playground\"\n\t\t\t\t\t\t\tclassName=\"radix-state-inactive:hidden flex min-h-0 w-full grow basis-0 items-stretch justify-center self-start\"\n\t\t\t\t\t\t\tforceMount\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Playground\n\t\t\t\t\t\t\t\tappInfo={data.playground}\n\t\t\t\t\t\t\t\tproblemAppName={data.extra.name}\n\t\t\t\t\t\t\t\tallApps={data.allApps ?? []}\n\t\t\t\t\t\t\t\tisUpToDate={data.playground?.isUpToDate ?? false}\n\t\t\t\t\t\t\t\tinBrowserBrowserRef={inBrowserBrowserRef}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</Tabs.Content>\n\t\t\t\t\t\t<Tabs.Content\n\t\t\t\t\t\t\tvalue=\"extra\"\n\t\t\t\t\t\t\tclassName=\"radix-state-inactive:hidden flex min-h-0 w-full grow basis-0 items-stretch justify-center self-start\"\n\t\t\t\t\t\t\tforceMount\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Preview\n\t\t\t\t\t\t\t\tappInfo={data.extra}\n\t\t\t\t\t\t\t\tinBrowserBrowserRef={inBrowserBrowserRef}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</Tabs.Content>\n\t\t\t\t\t\t<Tabs.Content\n\t\t\t\t\t\t\tvalue=\"diff\"\n\t\t\t\t\t\t\tclassName=\"radix-state-inactive:hidden flex h-full min-h-0 w-full grow basis-0 items-stretch justify-center self-start\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Diff\n\t\t\t\t\t\t\t\tdiff={data.diff}\n\t\t\t\t\t\t\t\tallApps={data.allApps}\n\t\t\t\t\t\t\t\tuserHasAccessPromise={userHasAccessPromise}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</Tabs.Content>\n\t\t\t\t\t\t<Tabs.Content\n\t\t\t\t\t\t\tvalue=\"chat\"\n\t\t\t\t\t\t\tclassName=\"radix-state-inactive:hidden flex h-full min-h-0 w-full grow basis-0 items-stretch justify-center self-start\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<DiscordChat discordPostsPromise={data.discordPostsPromise} />\n\t\t\t\t\t\t</Tabs.Content>\n\t\t\t\t\t</div>\n\t\t\t\t</Tabs.Root>\n\t\t\t</main>\n\t\t</div>\n\t)\n}\n\nexport function ErrorBoundary() {\n\treturn <GeneralErrorBoundary />\n}\n"],"names":["meta","args","loaderData","data","rootData","getRootMatchLoaderData","matches","title","getSeoMetaTags","extra","workshopTitle","description","ogTitle","ogDescription","instructor","requestInfo","$extra","_UNSAFE_withComponentProps","useLoaderData","useRootLoaderData","inBrowserBrowserRef","useRef","containerRef","leftPaneRef","splitPercent","setSplitPercent","useState","searchParams","useSearchParams","workshopConfig","useWorkshopConfig","userHasAccessPromise","useMemo","Promise","resolve","userHasAccess","showPlaygroundIndicator","playground","appName","name","shouldShowSetPlayground","isUpToDate","tabs","preview","get","previousExtraLink","previousExtra","to","dirName","nextExtraLink","nextExtra","isValidPreview","value","Boolean","includes","shouldHideTab","tab","ENV","EPICSHOP_DEPLOYED","devType","dev","type","stackBlitzUrl","product","discordChannelId","activeTab","find","previewTabs","map","hidden","id","label","getPreviewSearchParams","mdxComponents","InlineFile","createInlineFileComponent","fullPath","h1","useRevalidationWS","watchPaths","extraReadme","file","jsx","className","children","jsxs","ref","style","Link","SetAppToPlayground","isOutdated","hideTextOnNarrow","showOnboardingIndicator","articleId","instructionsCode","EpicVideoInfoProvider","epicVideoInfosPromise","Mdx","code","components","prefetch","ElementScrollRestoration","elementQuery","EditFileOnGitHub","relativePath","NavChevrons","prev","next","role","onMouseDown","event","startSplitDrag","container","current","initialClientX","clientX","onDoubleClick","setSplitPercentCookie","onTouchStart","firstTouch","touches","Tabs","PreviewTabsList","forceMount","Playground","appInfo","problemAppName","allApps","Preview","Diff","diff","DiscordChat","discordPostsPromise","ErrorBoundary","_UNSAFE_withErrorBoundaryProps","GeneralErrorBoundary"],"mappings":"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"}
1
+ {"version":3,"file":"_extra-DkiDFYVP.js","sources":["../../../app/routes/_app+/extra+/$extra.tsx"],"sourcesContent":["import path from 'path'\nimport { invariantResponse } from '@epic-web/invariant'\nimport { ElementScrollRestoration } from '@epic-web/restore-scroll'\nimport {\n\tgetAppByName,\n\tgetAppDisplayName,\n\tgetApps,\n\tisExtraApp,\n\tisPlaygroundApp,\n\ttype ExtraApp,\n} from '@epic-web/workshop-utils/apps.server'\nimport { getWorkshopConfig } from '@epic-web/workshop-utils/config.server'\nimport { getDiffCode } from '@epic-web/workshop-utils/diff.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 * as Tabs from '@radix-ui/react-tabs'\nimport slugify from '@sindresorhus/slugify'\nimport { useMemo, useRef, useState } from 'react'\nimport {\n\tLink,\n\tdata,\n\ttype HeadersFunction,\n\ttype LoaderFunctionArgs,\n\ttype MetaFunction,\n\tuseLoaderData,\n\tuseSearchParams,\n} from 'react-router'\nimport { Diff } from '#app/components/diff.tsx'\nimport { DiscordChat } from '#app/components/discord-chat.tsx'\nimport { EpicVideoInfoProvider } from '#app/components/epic-video.tsx'\nimport { GeneralErrorBoundary } from '#app/components/error-boundary.tsx'\nimport { type InBrowserBrowserRef } from '#app/components/in-browser-browser.tsx'\nimport { NavChevrons } from '#app/components/nav-chevrons.tsx'\nimport {\n\tgetPreviewSearchParams,\n\tPreviewTabsList,\n} from '#app/components/preview-tabs.tsx'\nimport { useRevalidationWS } from '#app/components/revalidation-ws.tsx'\nimport { useWorkshopConfig } from '#app/components/workshop-config.tsx'\nimport { Playground } from '#app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/__shared/playground.tsx'\nimport { Preview } from '#app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/__shared/preview.tsx'\nimport { getAppRunningState } from '#app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/__shared/utils.tsx'\nimport { EditFileOnGitHub } from '#app/routes/launch-editor.tsx'\nimport { SetAppToPlayground } from '#app/routes/set-playground.tsx'\nimport { fetchDiscordPosts } from '#app/utils/discord.server.ts'\nimport { createInlineFileComponent, Mdx } from '#app/utils/mdx.tsx'\nimport {\n\tgetRootMatchLoaderData,\n\tuseRootLoaderData,\n} from '#app/utils/root-loader.ts'\nimport { getSeoMetaTags } from '#app/utils/seo.ts'\nimport {\n\tgetSplitPercentFromRequest,\n\tsetSplitPercentCookie,\n\tstartSplitDrag,\n} from '#app/utils/split-layout.ts'\n\nfunction sortExtras(extras: ExtraApp[]) {\n\treturn extras.sort((a, b) =>\n\t\ta.title.localeCompare(b.title, undefined, {\n\t\t\tnumeric: true,\n\t\t\tsensitivity: 'base',\n\t\t}),\n\t)\n}\n\nexport const meta: MetaFunction<typeof loader> = (args) => {\n\tconst loaderData = args.data\n\tconst rootData = getRootMatchLoaderData(args.matches)\n\tif (!loaderData || !rootData) return [{ title: '🦉 | Error' }]\n\n\treturn getSeoMetaTags({\n\t\ttitle: `📚 | ${loaderData.extra.title} | ${rootData.workshopTitle}`,\n\t\tdescription: `Extra: ${loaderData.extra.title}`,\n\t\togTitle: loaderData.extra.title,\n\t\togDescription: `Extra: ${loaderData.extra.title}`,\n\t\tinstructor: rootData.instructor,\n\t\trequestInfo: rootData.requestInfo,\n\t})\n}\n\nexport async function loader({ request, params }: LoaderFunctionArgs) {\n\tconst timings = makeTimings('extraLoader')\n\tinvariantResponse(params.extra, 'extra is required')\n\n\tconst { title: workshopTitle } = getWorkshopConfig()\n\tconst cacheOptions = { request, timings }\n\tconst apps = await time(() => getApps(cacheOptions), {\n\t\ttimings,\n\t\ttype: 'getApps',\n\t\tdesc: 'getApps in extra loader',\n\t})\n\tconst extras = sortExtras(apps.filter(isExtraApp))\n\tconst playgroundApp = apps.find(isPlaygroundApp)\n\tconst extraIndex = extras.findIndex((extra) => extra.dirName === params.extra)\n\tconst extra = extras[extraIndex]\n\tif (!extra) {\n\t\tthrow new Response('Extra not found', { status: 404 })\n\t}\n\n\tconst readmeFilepath = path.join(extra.fullPath, 'README.mdx')\n\tconst previousExtra = extras[extraIndex - 1]\n\tconst nextExtra = extras[extraIndex + 1]\n\tconst reqUrl = new URL(request.url)\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 ?? extra)\n\tconst app2 = app2Name ? await getAppByName(app2Name) : extra\n\tconst splitPercent = getSplitPercentFromRequest(request, 50)\n\n\tconst { isRunning, portIsAvailable } = await getAppRunningState(extra)\n\n\tasync function getDiffProp() {\n\t\tif (!app1 || !app2) {\n\t\t\treturn { app1: app1?.name, app2: app2?.name, diffCode: null }\n\t\t}\n\t\tconst diffCode = await getDiffCode(app1, app2, {\n\t\t\t...cacheOptions,\n\t\t\tforceFresh: reqUrl.searchParams.get('forceFresh') === 'diff',\n\t\t}).catch((error) => {\n\t\t\tconsole.error(error)\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\tconst allApps = apps\n\t\t.filter(\n\t\t\t(app, index, list) =>\n\t\t\t\tlist.findIndex((item) => item.name === app.name) === index,\n\t\t)\n\t\t.map((app) => ({\n\t\t\tname: app.name,\n\t\t\tdisplayName: getAppDisplayName(app, apps),\n\t\t}))\n\t\t.sort((a, b) =>\n\t\t\ta.displayName.localeCompare(b.displayName, undefined, {\n\t\t\t\tnumeric: true,\n\t\t\t\tsensitivity: 'base',\n\t\t\t}),\n\t\t)\n\n\treturn data(\n\t\t{\n\t\t\tarticleId: `workshop-${slugify(workshopTitle)}-${slugify(\n\t\t\t\textra.title,\n\t\t\t)}-extra`,\n\t\t\tsplitPercent,\n\t\t\textra: {\n\t\t\t\ttype: 'extra',\n\t\t\t\tname: extra.name,\n\t\t\t\ttitle: extra.title,\n\t\t\t\tdirName: extra.dirName,\n\t\t\t\tfullPath: extra.fullPath,\n\t\t\t\trelativePath: extra.relativePath,\n\t\t\t\tdev: extra.dev,\n\t\t\t\ttest: extra.test,\n\t\t\t\tstackBlitzUrl: extra.stackBlitzUrl,\n\t\t\t\tisRunning,\n\t\t\t\tportIsAvailable,\n\t\t\t\tepicVideoEmbeds: extra.epicVideoEmbeds,\n\t\t\t\tinstructionsCode: extra.instructionsCode,\n\t\t\t},\n\t\t\textraReadme: {\n\t\t\t\tfile: readmeFilepath,\n\t\t\t\trelativePath: path.join(extra.relativePath, 'README.mdx'),\n\t\t\t},\n\t\t\tplayground: playgroundApp\n\t\t\t\t? ({\n\t\t\t\t\t\ttype: 'playground',\n\t\t\t\t\t\tappName: playgroundApp.appName,\n\t\t\t\t\t\tname: playgroundApp.name,\n\t\t\t\t\t\ttitle: playgroundApp.title,\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\tstackBlitzUrl: playgroundApp.stackBlitzUrl,\n\t\t\t\t\t\tisUpToDate: playgroundApp.isUpToDate,\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\tallApps,\n\t\t\tdiff: getDiffProp(),\n\t\t\tdiscordPostsPromise: fetchDiscordPosts({ request }),\n\t\t\tpreviousExtra: previousExtra\n\t\t\t\t? { dirName: previousExtra.dirName, title: previousExtra.title }\n\t\t\t\t: null,\n\t\t\tnextExtra: nextExtra\n\t\t\t\t? { dirName: nextExtra.dirName, title: nextExtra.title }\n\t\t\t\t: null,\n\t\t\tepicVideoInfosPromise: getEpicVideoInfos(extra.epicVideoEmbeds, {\n\t\t\t\trequest,\n\t\t\t}),\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\nexport default function ExtraRoute() {\n\tconst data = useLoaderData<typeof loader>()\n\tconst rootData = useRootLoaderData()\n\tconst inBrowserBrowserRef = useRef<InBrowserBrowserRef>(null)\n\tconst containerRef = useRef<HTMLDivElement>(null)\n\tconst leftPaneRef = useRef<HTMLDivElement>(null)\n\tconst [splitPercent, setSplitPercent] = useState<number>(data.splitPercent)\n\tconst [searchParams] = useSearchParams()\n\tconst workshopConfig = useWorkshopConfig()\n\tconst userHasAccessPromise = useMemo(\n\t\t() => Promise.resolve(rootData.userHasAccess ?? false),\n\t\t[rootData.userHasAccess],\n\t)\n\tconst showPlaygroundIndicator = data.playground?.appName !== data.extra.name\n\tconst shouldShowSetPlayground =\n\t\tshowPlaygroundIndicator || data.playground?.isUpToDate === false\n\tconst tabs = ['playground', 'extra', 'diff', 'chat'] as const\n\tconst preview = searchParams.get('preview')\n\tconst previousExtraLink = data.previousExtra\n\t\t? {\n\t\t\t\tto: `/extra/${data.previousExtra.dirName}`,\n\t\t\t\t'aria-label': 'Previous Extra',\n\t\t\t}\n\t\t: {\n\t\t\t\tto: '/extra',\n\t\t\t\t'aria-label': 'Extras',\n\t\t\t}\n\tconst nextExtraLink = data.nextExtra\n\t\t? {\n\t\t\t\tto: `/extra/${data.nextExtra.dirName}`,\n\t\t\t\t'aria-label': 'Next Extra',\n\t\t\t}\n\t\t: {\n\t\t\t\tto: '/finished',\n\t\t\t\t'aria-label': 'Workshop finished',\n\t\t\t}\n\n\tfunction isValidPreview(\n\t\tvalue: string | null,\n\t): value is (typeof tabs)[number] {\n\t\treturn Boolean(value && tabs.includes(value as (typeof tabs)[number]))\n\t}\n\n\tfunction shouldHideTab(tab: (typeof tabs)[number]) {\n\t\tif (tab === 'playground') {\n\t\t\treturn ENV.EPICSHOP_DEPLOYED\n\t\t}\n\t\tif (tab === 'extra') {\n\t\t\tif (ENV.EPICSHOP_DEPLOYED) {\n\t\t\t\tconst devType = data.extra.dev.type\n\t\t\t\treturn (\n\t\t\t\t\tdevType !== 'browser' &&\n\t\t\t\t\tdevType !== 'export' &&\n\t\t\t\t\t!data.extra.stackBlitzUrl\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\tif (tab === 'chat') {\n\t\t\treturn !workshopConfig.product.discordChannelId\n\t\t}\n\t\treturn false\n\t}\n\n\tconst activeTab =\n\t\tisValidPreview(preview) && !shouldHideTab(preview)\n\t\t\t? preview\n\t\t\t: (tabs.find((tab) => !shouldHideTab(tab)) ?? 'playground')\n\n\tconst previewTabs = tabs.map((tab) => {\n\t\tconst hidden = shouldHideTab(tab)\n\t\treturn {\n\t\t\tid: tab,\n\t\t\tlabel: tab,\n\t\t\thidden,\n\t\t\tto: `?${getPreviewSearchParams(searchParams, tab, 'playground')}`,\n\t\t}\n\t})\n\n\t// Create MDX components with extra-specific InlineFile\n\tconst mdxComponents = useMemo(() => {\n\t\tconst InlineFile = createInlineFileComponent(() => ({\n\t\t\tname: data.extra.name,\n\t\t\tfullPath: data.extra.fullPath,\n\t\t}))\n\t\treturn {\n\t\t\t// we'll render the title ourselves thank you\n\t\t\th1: () => null,\n\t\t\tInlineFile,\n\t\t}\n\t}, [data.extra.name, data.extra.fullPath])\n\n\tuseRevalidationWS({\n\t\twatchPaths: [data.extraReadme.file],\n\t})\n\n\treturn (\n\t\t<div className=\"flex max-w-full grow flex-col\">\n\t\t\t<main\n\t\t\t\tref={containerRef}\n\t\t\t\tclassName=\"flex grow flex-col sm:h-full sm:min-h-[800px] md:min-h-[unset] lg:flex-row\"\n\t\t\t>\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"relative flex min-w-0 flex-none basis-full flex-col sm:col-span-1 sm:row-span-1 sm:h-full lg:basis-(--split-pct)\"\n\t\t\t\t\tstyle={{ ['--split-pct' as any]: `${splitPercent}%` }}\n\t\t\t\t\tref={leftPaneRef}\n\t\t\t\t>\n\t\t\t\t\t<h1 className=\"@container h-14 border-b pr-5 pl-10 text-sm leading-tight font-medium\">\n\t\t\t\t\t\t<div className=\"flex h-14 items-center justify-between gap-x-2 py-2 whitespace-nowrap\">\n\t\t\t\t\t\t\t<div className=\"flex items-center justify-start gap-x-2 uppercase\">\n\t\t\t\t\t\t\t\t<Link to=\"/extra\" className=\"hover:underline\">\n\t\t\t\t\t\t\t\t\t<span>Extras</span>\n\t\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t\t<span>/</span>\n\t\t\t\t\t\t\t\t<Link to=\".\" className=\"hover:underline\">\n\t\t\t\t\t\t\t\t\t<span>{data.extra.title}</span>\n\t\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t{shouldShowSetPlayground ? (\n\t\t\t\t\t\t\t\t<SetAppToPlayground\n\t\t\t\t\t\t\t\t\tappName={data.extra.name}\n\t\t\t\t\t\t\t\t\tisOutdated={data.playground?.isUpToDate === false}\n\t\t\t\t\t\t\t\t\thideTextOnNarrow\n\t\t\t\t\t\t\t\t\tshowOnboardingIndicator={showPlaygroundIndicator}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</h1>\n\t\t\t\t\t<article\n\t\t\t\t\t\tid={data.articleId}\n\t\t\t\t\t\tkey={data.articleId}\n\t\t\t\t\t\tclassName=\"shadow-on-scrollbox scrollbar-thin scrollbar-thumb-scrollbar flex h-full w-full max-w-none flex-1 scroll-pt-6 flex-col justify-between space-y-6 overflow-y-auto p-2 sm:p-10 sm:pt-8\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{data.extra.instructionsCode ? (\n\t\t\t\t\t\t\t<EpicVideoInfoProvider\n\t\t\t\t\t\t\t\tepicVideoInfosPromise={data.epicVideoInfosPromise}\n\t\t\t\t\t\t\t>\n\t\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\t<Mdx\n\t\t\t\t\t\t\t\t\t\tcode={data.extra.instructionsCode}\n\t\t\t\t\t\t\t\t\t\tcomponents={mdxComponents}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</EpicVideoInfoProvider>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<div className=\"flex h-full items-center justify-center text-lg\">\n\t\t\t\t\t\t\t\t<p>No instructions yet...</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t<div className=\"mt-auto flex justify-between\">\n\t\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\t\tto={previousExtraLink.to}\n\t\t\t\t\t\t\t\taria-label={previousExtraLink['aria-label']}\n\t\t\t\t\t\t\t\tprefetch=\"intent\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span aria-hidden>←</span>\n\t\t\t\t\t\t\t\t<span className=\"hidden xl:inline\"> Previous</span>\n\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\t\tto={nextExtraLink.to}\n\t\t\t\t\t\t\t\taria-label={nextExtraLink['aria-label']}\n\t\t\t\t\t\t\t\tprefetch=\"intent\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span className=\"hidden xl:inline\">Next </span>\n\t\t\t\t\t\t\t\t<span aria-hidden>→</span>\n\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</article>\n\t\t\t\t\t<ElementScrollRestoration\n\t\t\t\t\t\telementQuery={`#${data.articleId}`}\n\t\t\t\t\t\tkey={`scroll-${data.articleId}`}\n\t\t\t\t\t/>\n\t\t\t\t\t<div className=\"@container flex h-16 justify-between border-t border-b-4 lg:border-b-0\">\n\t\t\t\t\t\t<div />\n\t\t\t\t\t\t<EditFileOnGitHub\n\t\t\t\t\t\t\tappName={data.extra.name}\n\t\t\t\t\t\t\trelativePath={data.extraReadme.relativePath}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<NavChevrons prev={previousExtraLink} next={nextExtraLink} />\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div\n\t\t\t\t\trole=\"separator\"\n\t\t\t\t\taria-orientation=\"vertical\"\n\t\t\t\t\ttitle=\"Drag to resize\"\n\t\t\t\t\tclassName=\"bg-border hover:bg-muted hidden w-1 cursor-col-resize lg:block\"\n\t\t\t\t\tonMouseDown={(event) =>\n\t\t\t\t\t\tstartSplitDrag({\n\t\t\t\t\t\t\tcontainer: containerRef.current,\n\t\t\t\t\t\t\tinitialClientX: event.clientX,\n\t\t\t\t\t\t\tsetSplitPercent,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\tonDoubleClick={() => {\n\t\t\t\t\t\tsetSplitPercent(setSplitPercentCookie(50))\n\t\t\t\t\t}}\n\t\t\t\t\tonTouchStart={(event) => {\n\t\t\t\t\t\tconst firstTouch = event.touches?.[0]\n\t\t\t\t\t\tif (!firstTouch) return\n\t\t\t\t\t\tstartSplitDrag({\n\t\t\t\t\t\t\tcontainer: containerRef.current,\n\t\t\t\t\t\t\tinitialClientX: firstTouch.clientX,\n\t\t\t\t\t\t\tsetSplitPercent,\n\t\t\t\t\t\t})\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t\t<Tabs.Root\n\t\t\t\t\tclassName=\"relative flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\"\n\t\t\t\t\tvalue={activeTab}\n\t\t\t\t>\n\t\t\t\t\t<PreviewTabsList tabs={previewTabs} />\n\t\t\t\t\t<div className=\"relative z-10 flex min-h-0 flex-1 flex-col overflow-hidden\">\n\t\t\t\t\t\t<Tabs.Content\n\t\t\t\t\t\t\tvalue=\"playground\"\n\t\t\t\t\t\t\tclassName=\"radix-state-inactive:hidden flex min-h-0 w-full grow basis-0 items-stretch justify-center self-start\"\n\t\t\t\t\t\t\tforceMount\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Playground\n\t\t\t\t\t\t\t\tappInfo={data.playground}\n\t\t\t\t\t\t\t\tproblemAppName={data.extra.name}\n\t\t\t\t\t\t\t\tallApps={data.allApps ?? []}\n\t\t\t\t\t\t\t\tisUpToDate={data.playground?.isUpToDate ?? false}\n\t\t\t\t\t\t\t\tinBrowserBrowserRef={inBrowserBrowserRef}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</Tabs.Content>\n\t\t\t\t\t\t<Tabs.Content\n\t\t\t\t\t\t\tvalue=\"extra\"\n\t\t\t\t\t\t\tclassName=\"radix-state-inactive:hidden flex min-h-0 w-full grow basis-0 items-stretch justify-center self-start\"\n\t\t\t\t\t\t\tforceMount\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Preview\n\t\t\t\t\t\t\t\tappInfo={data.extra}\n\t\t\t\t\t\t\t\tinBrowserBrowserRef={inBrowserBrowserRef}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</Tabs.Content>\n\t\t\t\t\t\t<Tabs.Content\n\t\t\t\t\t\t\tvalue=\"diff\"\n\t\t\t\t\t\t\tclassName=\"radix-state-inactive:hidden flex h-full min-h-0 w-full grow basis-0 items-stretch justify-center self-start\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Diff\n\t\t\t\t\t\t\t\tdiff={data.diff}\n\t\t\t\t\t\t\t\tallApps={data.allApps}\n\t\t\t\t\t\t\t\tuserHasAccessPromise={userHasAccessPromise}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</Tabs.Content>\n\t\t\t\t\t\t<Tabs.Content\n\t\t\t\t\t\t\tvalue=\"chat\"\n\t\t\t\t\t\t\tclassName=\"radix-state-inactive:hidden flex h-full min-h-0 w-full grow basis-0 items-stretch justify-center self-start\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<DiscordChat discordPostsPromise={data.discordPostsPromise} />\n\t\t\t\t\t\t</Tabs.Content>\n\t\t\t\t\t</div>\n\t\t\t\t</Tabs.Root>\n\t\t\t</main>\n\t\t</div>\n\t)\n}\n\nexport function ErrorBoundary() {\n\treturn <GeneralErrorBoundary />\n}\n"],"names":["meta","args","loaderData","data","rootData","getRootMatchLoaderData","matches","title","getSeoMetaTags","extra","workshopTitle","description","ogTitle","ogDescription","instructor","requestInfo","$extra","_UNSAFE_withComponentProps","useLoaderData","useRootLoaderData","inBrowserBrowserRef","useRef","containerRef","leftPaneRef","splitPercent","setSplitPercent","useState","searchParams","useSearchParams","workshopConfig","useWorkshopConfig","userHasAccessPromise","useMemo","Promise","resolve","userHasAccess","showPlaygroundIndicator","playground","appName","name","shouldShowSetPlayground","isUpToDate","tabs","preview","get","previousExtraLink","previousExtra","to","dirName","nextExtraLink","nextExtra","isValidPreview","value","Boolean","includes","shouldHideTab","tab","ENV","EPICSHOP_DEPLOYED","devType","dev","type","stackBlitzUrl","product","discordChannelId","activeTab","find","previewTabs","map","hidden","id","label","getPreviewSearchParams","mdxComponents","InlineFile","createInlineFileComponent","fullPath","h1","useRevalidationWS","watchPaths","extraReadme","file","jsx","className","children","jsxs","ref","style","Link","SetAppToPlayground","isOutdated","hideTextOnNarrow","showOnboardingIndicator","articleId","instructionsCode","EpicVideoInfoProvider","epicVideoInfosPromise","Mdx","code","components","prefetch","ElementScrollRestoration","elementQuery","EditFileOnGitHub","relativePath","NavChevrons","prev","next","role","onMouseDown","event","startSplitDrag","container","current","initialClientX","clientX","onDoubleClick","setSplitPercentCookie","onTouchStart","firstTouch","touches","Tabs","PreviewTabsList","forceMount","Playground","appInfo","problemAppName","allApps","Preview","Diff","diff","DiscordChat","discordPostsPromise","ErrorBoundary","_UNSAFE_withErrorBoundaryProps","GeneralErrorBoundary"],"mappings":"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"}