@epic-web/workshop-app 6.50.3 → 6.50.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"preview-CuS2jg4z.js","sources":["../../../app/routes/start.tsx","../../../app/components/in-browser-browser.tsx","../../../app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/__shared/preview.tsx"],"sourcesContent":["import { invariant, invariantResponse } from '@epic-web/invariant'\nimport { getAppByName } from '@epic-web/workshop-utils/apps.server'\nimport {\n\tcloseProcess,\n\trunAppDev,\n\tstopPort,\n\twaitOnApp,\n} from '@epic-web/workshop-utils/process-manager.server'\nimport { data, type ActionFunctionArgs, useFetcher } from 'react-router'\nimport { Button } from '#app/components/button.tsx'\nimport { Loading } from '#app/components/loading.tsx'\nimport { showProgressBarField } from '#app/components/progress-bar.tsx'\nimport { ensureUndeployed, useAltDown } from '#app/utils/misc.tsx'\nimport { dataWithPE, usePERedirectInput } from '#app/utils/pe.tsx'\nimport { createToastHeaders } from '#app/utils/toast.server'\n\nexport async function action({ request }: ActionFunctionArgs) {\n\tensureUndeployed()\n\tconst formData = await request.formData()\n\tconst intent = formData.get('intent')\n\tinvariantResponse(typeof intent === 'string', 'intent is required')\n\n\tif (intent === 'start' || intent === 'stop' || intent === 'restart') {\n\t\tconst name = formData.get('name')\n\t\tinvariantResponse(typeof name === 'string', 'name is required')\n\t\tconst app = await getAppByName(name)\n\t\tif (!app) {\n\t\t\tthrow new Response('Not found', { status: 404 })\n\t\t}\n\t\tif (app.dev.type !== 'script') {\n\t\t\tthrow new Response(`App \"${name}\" does not have a server`, {\n\t\t\t\tstatus: 400,\n\t\t\t})\n\t\t}\n\n\t\tasync function startApp() {\n\t\t\tinvariant(app, 'app must be defined')\n\t\t\tconst result = await runAppDev(app)\n\t\t\tif (result.running) {\n\t\t\t\tconst appRunningResult = await waitOnApp(app)\n\t\t\t\tif (appRunningResult?.status === 'success') {\n\t\t\t\t\t// wait another 200ms just in case the build output for assets isn't finished\n\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 200))\n\t\t\t\t\treturn dataWithPE(request, formData, {\n\t\t\t\t\t\tstatus: 'app-started',\n\t\t\t\t\t} as const)\n\t\t\t\t} else if (app.dev.type === 'script') {\n\t\t\t\t\tconst errorMessage = appRunningResult\n\t\t\t\t\t\t? appRunningResult.error\n\t\t\t\t\t\t: 'Unknown error'\n\t\t\t\t\treturn data(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstatus: 'app-not-started',\n\t\t\t\t\t\t\terror: errorMessage,\n\t\t\t\t\t\t\tport: app.dev.portNumber,\n\t\t\t\t\t\t} as const,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstatus: 500,\n\t\t\t\t\t\t\tstatusText: 'App did not start',\n\t\t\t\t\t\t\theaders: await createToastHeaders({\n\t\t\t\t\t\t\t\tdescription: errorMessage,\n\t\t\t\t\t\t\t\ttitle: 'App did not start',\n\t\t\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t},\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t} else if (result.portNumber) {\n\t\t\t\treturn dataWithPE(request, formData, {\n\t\t\t\t\tstatus: 'app-not-started',\n\t\t\t\t\terror: result.status,\n\t\t\t\t\tport: result.portNumber,\n\t\t\t\t} as const)\n\t\t\t} else {\n\t\t\t\tthrow new Response(\n\t\t\t\t\t'Tried starting a server for an app that does not have one',\n\t\t\t\t\t{ status: 400 },\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tasync function stopApp() {\n\t\t\tinvariant(app, 'app must be defined')\n\t\t\tawait closeProcess(app.name)\n\t\t\treturn dataWithPE(request, formData, { status: 'app-stopped' } as const)\n\t\t}\n\n\t\tswitch (intent) {\n\t\t\tcase 'start': {\n\t\t\t\treturn startApp()\n\t\t\t}\n\t\t\tcase 'stop': {\n\t\t\t\treturn stopApp()\n\t\t\t}\n\t\t\tcase 'restart': {\n\t\t\t\tawait stopApp()\n\t\t\t\treturn startApp()\n\t\t\t}\n\t\t}\n\t}\n\n\tif (intent === 'stop-port') {\n\t\tconst port = formData.get('port')\n\t\tinvariantResponse(typeof port === 'string', 'port is required')\n\t\tawait stopPort(port)\n\t\treturn dataWithPE(request, formData, { status: 'port-stopped' } as const)\n\t}\n\tthrow new Error(`Unknown intent: ${intent}`)\n}\n\nexport function AppStopper({ name }: { name: string }) {\n\tconst fetcher = useFetcher<typeof action>()\n\tconst peRedirectInput = usePERedirectInput()\n\tconst inFlightIntent = fetcher.formData?.get('intent')\n\tconst inFlightState =\n\t\tinFlightIntent === 'stop'\n\t\t\t? 'Stopping App'\n\t\t\t: inFlightIntent === 'restart'\n\t\t\t\t? 'Restarting App'\n\t\t\t\t: null\n\tconst altDown = useAltDown()\n\treturn (\n\t\t<fetcher.Form method=\"POST\" action=\"/start\">\n\t\t\t{peRedirectInput}\n\t\t\t{showProgressBarField}\n\t\t\t<input type=\"hidden\" name=\"name\" value={name} />\n\t\t\t<button\n\t\t\t\ttype=\"submit\"\n\t\t\t\tname=\"intent\"\n\t\t\t\tvalue={altDown ? 'restart' : 'stop'}\n\t\t\t\tclassName=\"h-full border-r px-3 py-4 font-mono text-xs leading-none uppercase\"\n\t\t\t>\n\t\t\t\t{inFlightState ? inFlightState : altDown ? 'Restart App' : 'Stop App'}\n\t\t\t</button>\n\t\t</fetcher.Form>\n\t)\n}\n\nexport function PortStopper({ port }: { port: number | string }) {\n\tconst fetcher = useFetcher<typeof action>()\n\tconst peRedirectInput = usePERedirectInput()\n\treturn (\n\t\t<fetcher.Form method=\"POST\" action=\"/start\">\n\t\t\t{peRedirectInput}\n\t\t\t{showProgressBarField}\n\t\t\t<input type=\"hidden\" name=\"port\" value={port} />\n\t\t\t<Button varient=\"mono\" type=\"submit\" name=\"intent\" value=\"stop-port\">\n\t\t\t\t{fetcher.state === 'idle' ? 'Stop Port' : 'Stopping Port'}\n\t\t\t</Button>\n\t\t</fetcher.Form>\n\t)\n}\n\nexport function AppStarter({ name }: { name: string }) {\n\tconst fetcher = useFetcher<typeof action>()\n\tconst peRedirectInput = usePERedirectInput()\n\tif (fetcher.data?.status === 'app-not-started') {\n\t\tif (fetcher.data.error === 'port-unavailable') {\n\t\t\treturn (\n\t\t\t\t<div>\n\t\t\t\t\tThe port is unavailable. Would you like to stop whatever is running on\n\t\t\t\t\tthat port and try again?\n\t\t\t\t\t<PortStopper port={fetcher.data.port} />\n\t\t\t\t</div>\n\t\t\t)\n\t\t} else {\n\t\t\treturn <div>An unknown error has happened.</div>\n\t\t}\n\t}\n\treturn (\n\t\t<fetcher.Form method=\"POST\" action=\"/start\">\n\t\t\t{peRedirectInput}\n\t\t\t{showProgressBarField}\n\t\t\t<input type=\"hidden\" name=\"name\" value={name} />\n\t\t\t{fetcher.state === 'idle' ? (\n\t\t\t\t<Button type=\"submit\" name=\"intent\" value=\"start\" varient=\"mono\">\n\t\t\t\t\tStart App\n\t\t\t\t</Button>\n\t\t\t) : (\n\t\t\t\t<div>\n\t\t\t\t\t<Loading>Starting App</Loading>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</fetcher.Form>\n\t)\n}\n","import { clsx } from 'clsx'\nimport * as React from 'react'\nimport { useImperativeHandle, useRef, useState } from 'react'\nimport { Icon } from '#app/components/icons.tsx'\nimport { AppStarter, AppStopper, PortStopper } from '#app/routes/start.tsx'\nimport { useTheme } from '#app/routes/theme/index.tsx'\nimport { getBaseUrl } from '#app/utils/misc.tsx'\nimport { useRequestInfo } from '#app/utils/root-loader.ts'\nimport { LinkButton } from './button.tsx'\nimport {\n\tTooltip,\n\tTooltipContent,\n\tTooltipProvider,\n\tTooltipTrigger,\n} from './ui/tooltip.tsx'\n\nexport type InBrowserBrowserRef = {\n\thandleExtrnalNavigation: (pathname?: string) => void\n}\n\ntype Props = {\n\tid: string\n\tname: string\n\tport: number\n\tportIsAvailable: boolean | null\n\tisRunning: boolean\n\tbaseUrl: string\n\tinitialRoute: string\n\tref?: React.Ref<InBrowserBrowserRef>\n}\n\nexport function InBrowserBrowser({\n\tname,\n\tport,\n\tportIsAvailable,\n\tisRunning,\n\tbaseUrl,\n\tid,\n\tinitialRoute,\n\tref,\n}: Props) {\n\tconst requestInfo = useRequestInfo()\n\tconst [showUnmanaged, setShowUnmanaged] = useState(false)\n\tif (isRunning || showUnmanaged) {\n\t\treturn (\n\t\t\t<InBrowserBrowserForRealz\n\t\t\t\tbaseUrl={baseUrl}\n\t\t\t\tid={id}\n\t\t\t\tname={name}\n\t\t\t\tref={ref}\n\t\t\t\tinitialRoute={initialRoute}\n\t\t\t/>\n\t\t)\n\t} else if (portIsAvailable === false) {\n\t\treturn (\n\t\t\t<div className=\"flex flex-col items-center justify-center\">\n\t\t\t\t<p className=\"max-w-xs pb-5 text-center\" role=\"status\">\n\t\t\t\t\t{`The port for this app is unavailable. It could be that you're running it `}\n\t\t\t\t\t<a\n\t\t\t\t\t\thref={getBaseUrl({ domain: requestInfo.domain, port })}\n\t\t\t\t\t\tclassName=\"underline\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noreferrer\"\n\t\t\t\t\t>\n\t\t\t\t\t\telsewhere\n\t\t\t\t\t</a>\n\t\t\t\t\t{'. '}\n\t\t\t\t\t<LinkButton onClick={() => setShowUnmanaged(true)}>\n\t\t\t\t\t\tShow here anyway\n\t\t\t\t\t</LinkButton>\n\t\t\t\t</p>\n\t\t\t\t<PortStopper port={port} />\n\t\t\t</div>\n\t\t)\n\t} else {\n\t\treturn (\n\t\t\t<div className=\"flex h-full flex-col items-center justify-center\">\n\t\t\t\t<AppStarter name={name} />\n\t\t\t</div>\n\t\t)\n\t}\n}\ntype RealBrowserProps = {\n\tbaseUrl: string\n\tid: string\n\tname: string\n\tinitialRoute: string\n\tref?: React.Ref<InBrowserBrowserRef>\n}\n\nfunction InBrowserBrowserForRealz({\n\tbaseUrl,\n\tid,\n\tname,\n\tinitialRoute,\n\tref,\n}: RealBrowserProps) {\n\tconst theme = useTheme()\n\tconst [iframeKeyNumber, setIframeKeyNumber] = useState(0)\n\tconst iframeKey = id + iframeKeyNumber\n\tconst iframeRef = useRef<HTMLIFrameElement>(null)\n\n\tconst appUrl = new URL(initialRoute, baseUrl)\n\tconst [iframeSrcUrl, setIframeSrcUrl] = useState(appUrl)\n\n\tconst currentId = useRef(id)\n\tif (currentId.current !== id) {\n\t\tcurrentId.current = id\n\t\tsetIframeSrcUrl(appUrl)\n\t}\n\n\tfunction handleExtrnalNavigation(pathname?: string) {\n\t\tif (pathname) {\n\t\t\tconst newUrl = new URL(pathname, baseUrl)\n\t\t\tsetIframeSrcUrl(newUrl)\n\t\t\tsetIframeKeyNumber((prev) => prev + 1)\n\t\t}\n\t}\n\n\tuseImperativeHandle(ref, () => ({ handleExtrnalNavigation }))\n\n\treturn (\n\t\t<TooltipProvider>\n\t\t\t<div className=\"flex h-full grow flex-col\">\n\t\t\t\t<div className=\"flex items-center justify-between border-b pl-1.5\">\n\t\t\t\t\t<div className=\"mr-2 flex items-center justify-center gap-2 px-1\">\n\t\t\t\t\t\t<Tooltip>\n\t\t\t\t\t\t\t<TooltipTrigger asChild>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tclassName=\"flex aspect-square h-full w-full items-center justify-center p-1 transition disabled:opacity-40\"\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\tsetIframeKeyNumber((prev) => prev + 1)\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Icon name=\"Refresh\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</TooltipTrigger>\n\t\t\t\t\t\t\t<TooltipContent>Refresh</TooltipContent>\n\t\t\t\t\t\t</Tooltip>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"bg-background text-foreground flex flex-1 items-center border-x p-3 leading-none\">\n\t\t\t\t\t\t<a href={iframeSrcUrl.toString()} target=\"_blank\" rel=\"noreferrer\">\n\t\t\t\t\t\t\t{iframeSrcUrl.toString()}\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</div>\n\t\t\t\t\t<AppStopper name={name} />\n\t\t\t\t\t<Tooltip>\n\t\t\t\t\t\t<TooltipTrigger asChild>\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\thref={iframeSrcUrl.toString()}\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noreferrer\"\n\t\t\t\t\t\t\t\tclassName={clsx(\n\t\t\t\t\t\t\t\t\t'flex aspect-square items-center justify-center px-3.5',\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Icon name=\"ExternalLink\" />\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</TooltipTrigger>\n\t\t\t\t\t\t<TooltipContent>Open in new tab</TooltipContent>\n\t\t\t\t\t</Tooltip>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"flex h-full w-full grow dark:bg-white\">\n\t\t\t\t\t<iframe\n\t\t\t\t\t\ttitle={name}\n\t\t\t\t\t\tkey={iframeKey}\n\t\t\t\t\t\tref={iframeRef}\n\t\t\t\t\t\tsrc={iframeSrcUrl.toString()}\n\t\t\t\t\t\tclassName=\"h-full w-full grow bg-white\"\n\t\t\t\t\t\tstyle={{ colorScheme: theme }}\n\t\t\t\t\t\tallow=\"clipboard-write\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</TooltipProvider>\n\t)\n}\n","import { type BaseExerciseStepApp } from '@epic-web/workshop-utils/apps.server'\nimport { useState } from 'react'\nimport { useSearchParams } from 'react-router'\nimport { Icon } from '#app/components/icons'\nimport {\n\tInBrowserBrowser,\n\ttype InBrowserBrowserRef,\n} from '#app/components/in-browser-browser.tsx'\nimport { Loading } from '#app/components/loading.tsx'\nimport { useTheme } from '#app/routes/theme/index.tsx'\nimport { cn, getBaseUrl } from '#app/utils/misc.tsx'\nimport { useRequestInfo } from '#app/utils/root-loader.ts'\n\nexport function Preview({\n\tid,\n\tappInfo,\n\tinBrowserBrowserRef,\n}: {\n\tid?: string\n\tappInfo: {\n\t\tisRunning: boolean\n\t\tappName?: string\n\t\tname: string\n\t\ttitle: string\n\t\tportIsAvailable: boolean | null\n\t\ttype: string\n\t\tfullPath: string\n\t\tdev: BaseExerciseStepApp['dev']\n\t\ttest: BaseExerciseStepApp['test']\n\t\tstackBlitzUrl: BaseExerciseStepApp['stackBlitzUrl']\n\t} | null\n\tinBrowserBrowserRef: React.RefObject<InBrowserBrowserRef | null>\n}) {\n\tconst requestInfo = useRequestInfo()\n\tconst [searchParams] = useSearchParams()\n\tconst theme = useTheme()\n\tif (!appInfo) return <p>No app here. Sorry.</p>\n\tconst { isRunning, dev, name, portIsAvailable, title } = appInfo\n\n\tif (ENV.EPICSHOP_DEPLOYED && appInfo.stackBlitzUrl) {\n\t\tconst url = new URL(appInfo.stackBlitzUrl)\n\t\turl.searchParams.set('embed', '1')\n\t\turl.searchParams.set('theme', theme)\n\n\t\treturn (\n\t\t\t<StackBlitzEmbed\n\t\t\t\ttitle={title}\n\t\t\t\turl={url.toString()}\n\t\t\t\tloadingContent={\n\t\t\t\t\t<Loading>\n\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\tLoading{' '}\n\t\t\t\t\t\t\t<a className=\"underline\" href={appInfo.stackBlitzUrl}>\n\t\t\t\t\t\t\t\t\"{title}\"\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</Loading>\n\t\t\t\t}\n\t\t\t/>\n\t\t)\n\t}\n\n\tif (dev.type === 'script') {\n\t\tconst baseUrl = getBaseUrl({\n\t\t\tdomain: requestInfo.domain,\n\t\t\tport: dev.portNumber,\n\t\t})\n\t\treturn (\n\t\t\t<InBrowserBrowser\n\t\t\t\tref={inBrowserBrowserRef}\n\t\t\t\tisRunning={isRunning}\n\t\t\t\tid={id ?? name}\n\t\t\t\tname={name}\n\t\t\t\tportIsAvailable={portIsAvailable}\n\t\t\t\tport={dev.portNumber}\n\t\t\t\tbaseUrl={baseUrl}\n\t\t\t\tinitialRoute={searchParams.get('pathname') ?? dev.initialRoute}\n\t\t\t/>\n\t\t)\n\t} else if (dev.type === 'browser') {\n\t\treturn (\n\t\t\t<div className=\"scrollbar-thin scrollbar-thumb-scrollbar relative h-full grow overflow-y-auto\">\n\t\t\t\t<a\n\t\t\t\t\thref={dev.pathname}\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noreferrer\"\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t'absolute right-5 bottom-5 flex items-center justify-center rounded-full bg-gray-100 p-2.5 transition hover:bg-gray-200 dark:bg-gray-800 hover:dark:bg-gray-600',\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\t<Icon name=\"ExternalLink\" aria-hidden=\"true\" />\n\t\t\t\t\t<span className=\"sr-only\">Open in New Window</span>\n\t\t\t\t</a>\n\t\t\t\t<iframe\n\t\t\t\t\ttitle={title}\n\t\t\t\t\tsrc={dev.pathname}\n\t\t\t\t\tclassName=\"yo yo h-full w-full grow bg-white\"\n\t\t\t\t\tstyle={{ colorScheme: theme }}\n\t\t\t\t\tallow=\"clipboard-write\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t)\n\t} else {\n\t\treturn (\n\t\t\t<div className=\"flex h-full items-center justify-center text-lg\">\n\t\t\t\t<p>\n\t\t\t\t\tPreview for dev type of <code>{dev.type}</code> not supported.\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t)\n\t}\n}\n\nexport function StackBlitzEmbed({\n\turl,\n\ttitle,\n\tloadingContent,\n}: {\n\turl: string\n\ttitle?: string\n\tloadingContent: React.ReactNode\n}) {\n\tconst theme = useTheme()\n\tconst [iframeLoaded, setIframeLoaded] = useState(false)\n\n\treturn (\n\t\t<div className=\"h-full w-full grow\">\n\t\t\t{iframeLoaded ? null : (\n\t\t\t\t<div className=\"absolute inset-0 z-10 flex items-center justify-center\">\n\t\t\t\t\t{loadingContent}\n\t\t\t\t</div>\n\t\t\t)}\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\tsrc={url}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t'h-full w-full grow transition-opacity duration-300',\n\t\t\t\t\tiframeLoaded ? 'opacity-100' : 'opacity-0',\n\t\t\t\t)}\n\t\t\t\ttitle={title}\n\t\t\t\tsandbox=\"allow-forms allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox\"\n\t\t\t\tallow=\"clipboard-write\"\n\t\t\t\tstyle={{ colorScheme: theme }}\n\t\t\t/>\n\t\t</div>\n\t)\n}\n"],"names":["AppStopper","name","fetcher","useFetcher","peRedirectInput","usePERedirectInput","inFlightIntent","formData","get","inFlightState","altDown","useAltDown","Form","method","action","children","showProgressBarField","type","value","jsx","className","PortStopper","port","Button","varient","state","AppStarter","data","status","error","Loading","InBrowserBrowser","portIsAvailable","isRunning","baseUrl","id","initialRoute","ref","requestInfo","useRequestInfo","showUnmanaged","setShowUnmanaged","useState","InBrowserBrowserForRealz","jsxs","getBaseUrl","LinkButton","theme","useTheme","iframeKeyNumber","setIframeKeyNumber","iframeKey","iframeRef","useRef","appUrl","iframeSrcUrl","setIframeSrcUrl","currentId","handleExtrnalNavigation","pathname","newUrl","prev","useImperativeHandle","TooltipProvider","Tooltip","TooltipTrigger","Icon","TooltipContent","clsx","Preview","appInfo","inBrowserBrowserRef","searchParams","useSearchParams","dev","title","url","StackBlitzEmbed","cn","loadingContent","iframeLoaded","setIframeLoaded"],"mappings":"ohBA8GO,SAASA,EAAW,CAAEC,KAAAA,CAAK,EAAqB,CACtD,MAAMC,EAAUC,EAAA,EACVC,EAAkBC,EAAA,EAClBC,EAAiBJ,EAAQK,UAAUC,IAAI,QAAQ,EAC/CC,EACLH,IAAmB,OAChB,eACAA,IAAmB,UAClB,iBACA,KACCI,EAAUC,EAAA,EAChB,cACET,EAAQU,KAAR,CAAaC,OAAO,OAAOC,OAAO,SACjCC,SAAA,CAAAX,EACAY,QACA,QAAA,CAAMC,KAAK,SAAShB,KAAK,OAAOiB,MAAOjB,CAAA,CAAM,EAC9CkB,EAAAA,IAAC,SAAA,CACAF,KAAK,SACLhB,KAAK,SACLiB,MAAOR,EAAU,UAAY,OAC7BU,UAAU,qEAETL,SAAAN,IAAgCC,EAAU,cAAgB,WAAA,CAC5D,CAAA,CAAA,CACD,CAEF,CAEO,SAASW,EAAY,CAAEC,KAAAA,CAAK,EAA8B,CAChE,MAAMpB,EAAUC,EAAA,EACVC,EAAkBC,EAAA,EACxB,cACEH,EAAQU,KAAR,CAAaC,OAAO,OAAOC,OAAO,SACjCC,SAAA,CAAAX,EACAY,QACA,QAAA,CAAMC,KAAK,SAAShB,KAAK,OAAOiB,MAAOI,CAAA,CAAM,EAC9CH,EAAAA,IAACI,EAAA,CAAOC,QAAQ,OAAOP,KAAK,SAAShB,KAAK,SAASiB,MAAM,YACvDH,SAAAb,EAAQuB,QAAU,OAAS,YAAc,eAAA,CAC3C,CAAA,CAAA,CACD,CAEF,CAEO,SAASC,EAAW,CAAEzB,KAAAA,CAAK,EAAqB,CACtD,MAAMC,EAAUC,EAAA,EACVC,EAAkBC,EAAA,EACxB,OAAIH,EAAQyB,MAAMC,SAAW,kBACxB1B,EAAQyB,KAAKE,QAAU,0BAExB,MAAA,CAAId,SAAA,CAAA,kGAGJI,EAAAA,IAACE,EAAA,CAAYC,KAAMpB,EAAQyB,KAAKL,IAAA,CAAM,CAAA,CAAA,CACvC,EAGMH,EAAAA,IAAC,OAAIJ,SAAA,gCAAA,CAA8B,SAI1Cb,EAAQU,KAAR,CAAaC,OAAO,OAAOC,OAAO,SACjCC,SAAA,CAAAX,EACAY,QACA,QAAA,CAAMC,KAAK,SAAShB,KAAK,OAAOiB,MAAOjB,CAAA,CAAM,EAC7CC,EAAQuB,QAAU,OAClBN,EAAAA,IAACI,GAAON,KAAK,SAAShB,KAAK,SAASiB,MAAM,QAAQM,QAAQ,OAAOT,qBAEjE,EAEAI,EAAAA,IAAC,OACAJ,SAAAI,EAAAA,IAACW,EAAA,CAAQf,wBAAY,CAAA,CACtB,CAAA,CAAA,CAEF,CAEF,CC1JO,SAASgB,EAAiB,CAChC,KAAA9B,EACA,KAAAqB,EACA,gBAAAU,EACA,UAAAC,EACA,QAAAC,EACA,GAAAC,EACA,aAAAC,EACA,IAAAC,CACD,EAAU,CACT,MAAMC,EAAcC,EAAA,EACd,CAACC,EAAeC,CAAgB,EAAIC,EAAAA,SAAS,EAAK,EACxD,OAAIT,GAAaO,EAEfrB,EAAAA,IAACwB,EAAA,CACA,QAAAT,EACA,GAAAC,EACA,KAAAlC,EACA,IAAAoC,EACA,aAAAD,CAAA,CAAA,EAGQJ,IAAoB,GAE7BY,EAAAA,KAAC,MAAA,CAAI,UAAU,4CACd,SAAA,CAAAA,EAAAA,KAAC,IAAA,CAAE,UAAU,4BAA4B,KAAK,SAC5C,SAAA,CAAA,4EACDzB,EAAAA,IAAC,IAAA,CACA,KAAM0B,EAAW,CAAE,OAAQP,EAAY,OAAQ,KAAAhB,EAAM,EACrD,UAAU,YACV,OAAO,SACP,IAAI,aACJ,SAAA,WAAA,CAAA,EAGA,WACAwB,EAAA,CAAW,QAAS,IAAML,EAAiB,EAAI,EAAG,SAAA,kBAAA,CAEnD,CAAA,EACD,EACAtB,MAACE,GAAY,KAAAC,CAAA,CAAY,CAAA,EAC1B,QAIC,MAAA,CAAI,UAAU,mDACd,SAAAH,EAAAA,IAACO,EAAA,CAAW,KAAAzB,EAAY,CAAA,CACzB,CAGH,CASA,SAAS0C,EAAyB,CACjC,QAAAT,EACA,GAAAC,EACA,KAAAlC,EACA,aAAAmC,EACA,IAAAC,CACD,EAAqB,CACpB,MAAMU,EAAQC,EAAA,EACR,CAACC,EAAiBC,CAAkB,EAAIR,EAAAA,SAAS,CAAC,EAClDS,EAAYhB,EAAKc,EACjBG,EAAYC,EAAAA,OAA0B,IAAI,EAE1CC,EAAS,IAAI,IAAIlB,EAAcF,CAAO,EACtC,CAACqB,EAAcC,CAAe,EAAId,EAAAA,SAASY,CAAM,EAEjDG,EAAYJ,EAAAA,OAAOlB,CAAE,EACvBsB,EAAU,UAAYtB,IACzBsB,EAAU,QAAUtB,EACpBqB,EAAgBF,CAAM,GAGvB,SAASI,EAAwBC,EAAmB,CACnD,GAAIA,EAAU,CACb,MAAMC,EAAS,IAAI,IAAID,EAAUzB,CAAO,EACxCsB,EAAgBI,CAAM,EACtBV,EAAoBW,GAASA,EAAO,CAAC,CACtC,CACD,CAEAC,OAAAA,EAAAA,oBAAoBzB,EAAK,KAAO,CAAE,wBAAAqB,CAAA,EAA0B,EAG3DvC,EAAAA,IAAC4C,EAAA,CACA,SAAAnB,EAAAA,KAAC,MAAA,CAAI,UAAU,4BACd,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,oDACd,SAAA,CAAAzB,MAAC,MAAA,CAAI,UAAU,mDACd,SAAAyB,EAAAA,KAACoB,EAAA,CACA,SAAA,CAAA7C,EAAAA,IAAC8C,EAAA,CAAe,QAAO,GACtB,SAAA9C,EAAAA,IAAC,SAAA,CACA,KAAK,SACL,UAAU,kGACV,QAAS,IAAM,CACd+B,EAAoBW,GAASA,EAAO,CAAC,CACtC,EAEA,SAAA1C,EAAAA,IAAC+C,EAAA,CAAK,KAAK,UAAU,cAAY,MAAA,CAAO,CAAA,CAAA,EAE1C,EACA/C,EAAAA,IAACgD,GAAe,SAAA,SAAA,CAAO,CAAA,CAAA,CACxB,CAAA,CACD,QACC,MAAA,CAAI,UAAU,mFACd,SAAAhD,MAAC,KAAE,KAAMoC,EAAa,SAAA,EAAY,OAAO,SAAS,IAAI,aACpD,SAAAA,EAAa,WACf,EACD,EACApC,MAACnB,GAAW,KAAAC,EAAY,SACvB+D,EAAA,CACA,SAAA,CAAA7C,EAAAA,IAAC8C,EAAA,CAAe,QAAO,GACtB,SAAA9C,EAAAA,IAAC,IAAA,CACA,KAAMoC,EAAa,SAAA,EACnB,OAAO,SACP,IAAI,aACJ,UAAWa,EACV,uDAAA,EAGD,SAAAjD,EAAAA,IAAC+C,EAAA,CAAK,KAAK,cAAA,CAAe,CAAA,CAAA,EAE5B,EACA/C,EAAAA,IAACgD,GAAe,SAAA,iBAAA,CAAe,CAAA,CAAA,CAChC,CAAA,EACD,EACAhD,EAAAA,IAAC,MAAA,CAAI,UAAU,wCACd,SAAAA,EAAAA,IAAC,SAAA,CACA,MAAOlB,EAEP,IAAKmD,EACL,IAAKG,EAAa,SAAA,EAClB,UAAU,8BACV,MAAO,CAAE,YAAaR,CAAA,EACtB,MAAM,iBAAA,EALDI,CAAA,CAMN,CACD,CAAA,CAAA,CACD,CAAA,CACD,CAEF,CCpKO,SAASkB,GAAQ,CACvB,GAAAlC,EACA,QAAAmC,EACA,oBAAAC,CACD,EAeG,CACF,MAAMjC,EAAcC,EAAA,EACd,CAACiC,CAAY,EAAIC,EAAA,EACjB1B,EAAQC,EAAA,EACd,GAAI,CAACsB,EAAS,OAAOnD,EAAAA,IAAC,KAAE,SAAA,sBAAmB,EAC3C,KAAM,CAAE,UAAAc,EAAW,IAAAyC,EAAK,KAAAzE,EAAM,gBAAA+B,EAAiB,MAAA2C,GAAUL,EAEzD,GAAI,IAAI,mBAAqBA,EAAQ,cAAe,CACnD,MAAMM,EAAM,IAAI,IAAIN,EAAQ,aAAa,EACzC,OAAAM,EAAI,aAAa,IAAI,QAAS,GAAG,EACjCA,EAAI,aAAa,IAAI,QAAS7B,CAAK,EAGlC5B,EAAAA,IAAC0D,EAAA,CACA,MAAAF,EACA,IAAKC,EAAI,SAAA,EACT,eACCzD,EAAAA,IAACW,EAAA,CACA,SAAAc,OAAC,OAAA,CAAK,SAAA,CAAA,UACG,WACP,IAAA,CAAE,UAAU,YAAY,KAAM0B,EAAQ,cAAe,SAAA,CAAA,IACnDK,EAAM,GAAA,CAAA,CACT,CAAA,CAAA,CACD,CAAA,CACD,CAAA,CAAA,CAIJ,CAEA,GAAID,EAAI,OAAS,SAAU,CAC1B,MAAMxC,EAAUW,EAAW,CAC1B,OAAQP,EAAY,OACpB,KAAMoC,EAAI,UAAA,CACV,EACD,OACCvD,EAAAA,IAACY,EAAA,CACA,IAAKwC,EACL,UAAAtC,EACA,GAAIE,GAAMlC,EACV,KAAAA,EACA,gBAAA+B,EACA,KAAM0C,EAAI,WACV,QAAAxC,EACA,aAAcsC,EAAa,IAAI,UAAU,GAAKE,EAAI,YAAA,CAAA,CAGrD,KAAA,QAAWA,EAAI,OAAS,UAEtB9B,EAAAA,KAAC,MAAA,CAAI,UAAU,gFACd,SAAA,CAAAA,EAAAA,KAAC,IAAA,CACA,KAAM8B,EAAI,SACV,OAAO,SACP,IAAI,aACJ,UAAWI,EACV,gKAAA,EAGD,SAAA,CAAA3D,EAAAA,IAAC+C,EAAA,CAAK,KAAK,eAAe,cAAY,OAAO,EAC7C/C,EAAAA,IAAC,OAAA,CAAK,UAAU,UAAU,SAAA,oBAAA,CAAkB,CAAA,CAAA,CAAA,EAE7CA,EAAAA,IAAC,SAAA,CACA,MAAAwD,EACA,IAAKD,EAAI,SACT,UAAU,oCACV,MAAO,CAAE,YAAa3B,CAAA,EACtB,MAAM,iBAAA,CAAA,CACP,EACD,EAIA5B,EAAAA,IAAC,MAAA,CAAI,UAAU,kDACd,gBAAC,IAAA,CAAE,SAAA,CAAA,2BACsBA,EAAAA,IAAC,OAAA,CAAM,SAAAuD,EAAI,IAAA,CAAK,EAAO,iBAAA,CAAA,CAChD,CAAA,CACD,CAGH,CAEO,SAASG,EAAgB,CAC/B,IAAAD,EACA,MAAAD,EACA,eAAAI,CACD,EAIG,CACF,MAAMhC,EAAQC,EAAA,EACR,CAACgC,EAAcC,CAAe,EAAIvC,EAAAA,SAAS,EAAK,EAEtD,OACCE,EAAAA,KAAC,MAAA,CAAI,UAAU,qBACb,SAAA,CAAAoC,EAAe,KACf7D,EAAAA,IAAC,MAAA,CAAI,UAAU,yDACb,SAAA4D,EACF,EAED5D,EAAAA,IAAC,SAAA,CACA,OAAQ,IAAM8D,EAAgB,EAAI,EAElC,QAAS,IAAMA,EAAgB,EAAI,EACnC,IAAKL,EACL,UAAWE,EACV,qDACAE,EAAe,cAAgB,WAAA,EAEhC,MAAAL,EACA,QAAQ,0FACR,MAAM,kBACN,MAAO,CAAE,YAAa5B,CAAA,CAAM,CAAA,CAC7B,EACD,CAEF"}
1
+ {"version":3,"file":"preview-CqV1y1NY.js","sources":["../../../app/routes/start.tsx","../../../app/components/in-browser-browser.tsx","../../../app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/__shared/preview.tsx"],"sourcesContent":["import { invariant, invariantResponse } from '@epic-web/invariant'\nimport { getAppByName } from '@epic-web/workshop-utils/apps.server'\nimport {\n\tcloseProcess,\n\trunAppDev,\n\tstopPort,\n\twaitOnApp,\n} from '@epic-web/workshop-utils/process-manager.server'\nimport { data, type ActionFunctionArgs, useFetcher } from 'react-router'\nimport { Button } from '#app/components/button.tsx'\nimport { Loading } from '#app/components/loading.tsx'\nimport { showProgressBarField } from '#app/components/progress-bar.tsx'\nimport { ensureUndeployed, useAltDown } from '#app/utils/misc.tsx'\nimport { dataWithPE, usePERedirectInput } from '#app/utils/pe.tsx'\nimport { createToastHeaders } from '#app/utils/toast.server'\n\nexport async function action({ request }: ActionFunctionArgs) {\n\tensureUndeployed()\n\tconst formData = await request.formData()\n\tconst intent = formData.get('intent')\n\tinvariantResponse(typeof intent === 'string', 'intent is required')\n\n\tif (intent === 'start' || intent === 'stop' || intent === 'restart') {\n\t\tconst name = formData.get('name')\n\t\tinvariantResponse(typeof name === 'string', 'name is required')\n\t\tconst app = await getAppByName(name)\n\t\tif (!app) {\n\t\t\tthrow new Response('Not found', { status: 404 })\n\t\t}\n\t\tif (app.dev.type !== 'script') {\n\t\t\tthrow new Response(`App \"${name}\" does not have a server`, {\n\t\t\t\tstatus: 400,\n\t\t\t})\n\t\t}\n\n\t\tasync function startApp() {\n\t\t\tinvariant(app, 'app must be defined')\n\t\t\tconst result = await runAppDev(app)\n\t\t\tif (result.running) {\n\t\t\t\tconst appRunningResult = await waitOnApp(app)\n\t\t\t\tif (appRunningResult?.status === 'success') {\n\t\t\t\t\t// wait another 200ms just in case the build output for assets isn't finished\n\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 200))\n\t\t\t\t\treturn dataWithPE(request, formData, {\n\t\t\t\t\t\tstatus: 'app-started',\n\t\t\t\t\t} as const)\n\t\t\t\t} else if (app.dev.type === 'script') {\n\t\t\t\t\tconst errorMessage = appRunningResult\n\t\t\t\t\t\t? appRunningResult.error\n\t\t\t\t\t\t: 'Unknown error'\n\t\t\t\t\treturn data(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstatus: 'app-not-started',\n\t\t\t\t\t\t\terror: errorMessage,\n\t\t\t\t\t\t\tport: app.dev.portNumber,\n\t\t\t\t\t\t} as const,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstatus: 500,\n\t\t\t\t\t\t\tstatusText: 'App did not start',\n\t\t\t\t\t\t\theaders: await createToastHeaders({\n\t\t\t\t\t\t\t\tdescription: errorMessage,\n\t\t\t\t\t\t\t\ttitle: 'App did not start',\n\t\t\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t},\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t} else if (result.portNumber) {\n\t\t\t\treturn dataWithPE(request, formData, {\n\t\t\t\t\tstatus: 'app-not-started',\n\t\t\t\t\terror: result.status,\n\t\t\t\t\tport: result.portNumber,\n\t\t\t\t} as const)\n\t\t\t} else {\n\t\t\t\tthrow new Response(\n\t\t\t\t\t'Tried starting a server for an app that does not have one',\n\t\t\t\t\t{ status: 400 },\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tasync function stopApp() {\n\t\t\tinvariant(app, 'app must be defined')\n\t\t\tawait closeProcess(app.name)\n\t\t\treturn dataWithPE(request, formData, { status: 'app-stopped' } as const)\n\t\t}\n\n\t\tswitch (intent) {\n\t\t\tcase 'start': {\n\t\t\t\treturn startApp()\n\t\t\t}\n\t\t\tcase 'stop': {\n\t\t\t\treturn stopApp()\n\t\t\t}\n\t\t\tcase 'restart': {\n\t\t\t\tawait stopApp()\n\t\t\t\treturn startApp()\n\t\t\t}\n\t\t}\n\t}\n\n\tif (intent === 'stop-port') {\n\t\tconst port = formData.get('port')\n\t\tinvariantResponse(typeof port === 'string', 'port is required')\n\t\tawait stopPort(port)\n\t\treturn dataWithPE(request, formData, { status: 'port-stopped' } as const)\n\t}\n\tthrow new Error(`Unknown intent: ${intent}`)\n}\n\nexport function AppStopper({ name }: { name: string }) {\n\tconst fetcher = useFetcher<typeof action>()\n\tconst peRedirectInput = usePERedirectInput()\n\tconst inFlightIntent = fetcher.formData?.get('intent')\n\tconst inFlightState =\n\t\tinFlightIntent === 'stop'\n\t\t\t? 'Stopping App'\n\t\t\t: inFlightIntent === 'restart'\n\t\t\t\t? 'Restarting App'\n\t\t\t\t: null\n\tconst altDown = useAltDown()\n\treturn (\n\t\t<fetcher.Form method=\"POST\" action=\"/start\">\n\t\t\t{peRedirectInput}\n\t\t\t{showProgressBarField}\n\t\t\t<input type=\"hidden\" name=\"name\" value={name} />\n\t\t\t<button\n\t\t\t\ttype=\"submit\"\n\t\t\t\tname=\"intent\"\n\t\t\t\tvalue={altDown ? 'restart' : 'stop'}\n\t\t\t\tclassName=\"h-full border-r px-3 py-4 font-mono text-xs leading-none uppercase\"\n\t\t\t>\n\t\t\t\t{inFlightState ? inFlightState : altDown ? 'Restart App' : 'Stop App'}\n\t\t\t</button>\n\t\t</fetcher.Form>\n\t)\n}\n\nexport function PortStopper({ port }: { port: number | string }) {\n\tconst fetcher = useFetcher<typeof action>()\n\tconst peRedirectInput = usePERedirectInput()\n\treturn (\n\t\t<fetcher.Form method=\"POST\" action=\"/start\">\n\t\t\t{peRedirectInput}\n\t\t\t{showProgressBarField}\n\t\t\t<input type=\"hidden\" name=\"port\" value={port} />\n\t\t\t<Button varient=\"mono\" type=\"submit\" name=\"intent\" value=\"stop-port\">\n\t\t\t\t{fetcher.state === 'idle' ? 'Stop Port' : 'Stopping Port'}\n\t\t\t</Button>\n\t\t</fetcher.Form>\n\t)\n}\n\nexport function AppStarter({ name }: { name: string }) {\n\tconst fetcher = useFetcher<typeof action>()\n\tconst peRedirectInput = usePERedirectInput()\n\tif (fetcher.data?.status === 'app-not-started') {\n\t\tif (fetcher.data.error === 'port-unavailable') {\n\t\t\treturn (\n\t\t\t\t<div>\n\t\t\t\t\tThe port is unavailable. Would you like to stop whatever is running on\n\t\t\t\t\tthat port and try again?\n\t\t\t\t\t<PortStopper port={fetcher.data.port} />\n\t\t\t\t</div>\n\t\t\t)\n\t\t} else {\n\t\t\treturn <div>An unknown error has happened.</div>\n\t\t}\n\t}\n\treturn (\n\t\t<fetcher.Form method=\"POST\" action=\"/start\">\n\t\t\t{peRedirectInput}\n\t\t\t{showProgressBarField}\n\t\t\t<input type=\"hidden\" name=\"name\" value={name} />\n\t\t\t{fetcher.state === 'idle' ? (\n\t\t\t\t<Button type=\"submit\" name=\"intent\" value=\"start\" varient=\"mono\">\n\t\t\t\t\tStart App\n\t\t\t\t</Button>\n\t\t\t) : (\n\t\t\t\t<div>\n\t\t\t\t\t<Loading>Starting App</Loading>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</fetcher.Form>\n\t)\n}\n","import { clsx } from 'clsx'\nimport * as React from 'react'\nimport { useImperativeHandle, useRef, useState } from 'react'\nimport { Icon } from '#app/components/icons.tsx'\nimport { AppStarter, AppStopper, PortStopper } from '#app/routes/start.tsx'\nimport { useTheme } from '#app/routes/theme/index.tsx'\nimport { getBaseUrl } from '#app/utils/misc.tsx'\nimport { useRequestInfo } from '#app/utils/root-loader.ts'\nimport { LinkButton } from './button.tsx'\nimport {\n\tTooltip,\n\tTooltipContent,\n\tTooltipProvider,\n\tTooltipTrigger,\n} from './ui/tooltip.tsx'\n\nexport type InBrowserBrowserRef = {\n\thandleExtrnalNavigation: (pathname?: string) => void\n}\n\ntype Props = {\n\tid: string\n\tname: string\n\tport: number\n\tportIsAvailable: boolean | null\n\tisRunning: boolean\n\tbaseUrl: string\n\tinitialRoute: string\n\tref?: React.Ref<InBrowserBrowserRef>\n}\n\nexport function InBrowserBrowser({\n\tname,\n\tport,\n\tportIsAvailable,\n\tisRunning,\n\tbaseUrl,\n\tid,\n\tinitialRoute,\n\tref,\n}: Props) {\n\tconst requestInfo = useRequestInfo()\n\tconst [showUnmanaged, setShowUnmanaged] = useState(false)\n\tif (isRunning || showUnmanaged) {\n\t\treturn (\n\t\t\t<InBrowserBrowserForRealz\n\t\t\t\tbaseUrl={baseUrl}\n\t\t\t\tid={id}\n\t\t\t\tname={name}\n\t\t\t\tref={ref}\n\t\t\t\tinitialRoute={initialRoute}\n\t\t\t/>\n\t\t)\n\t} else if (portIsAvailable === false) {\n\t\treturn (\n\t\t\t<div className=\"flex flex-col items-center justify-center\">\n\t\t\t\t<p className=\"max-w-xs pb-5 text-center\" role=\"status\">\n\t\t\t\t\t{`The port for this app is unavailable. It could be that you're running it `}\n\t\t\t\t\t<a\n\t\t\t\t\t\thref={getBaseUrl({ domain: requestInfo.domain, port })}\n\t\t\t\t\t\tclassName=\"underline\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noreferrer\"\n\t\t\t\t\t>\n\t\t\t\t\t\telsewhere\n\t\t\t\t\t</a>\n\t\t\t\t\t{'. '}\n\t\t\t\t\t<LinkButton onClick={() => setShowUnmanaged(true)}>\n\t\t\t\t\t\tShow here anyway\n\t\t\t\t\t</LinkButton>\n\t\t\t\t</p>\n\t\t\t\t<PortStopper port={port} />\n\t\t\t</div>\n\t\t)\n\t} else {\n\t\treturn (\n\t\t\t<div className=\"flex h-full flex-col items-center justify-center\">\n\t\t\t\t<AppStarter name={name} />\n\t\t\t</div>\n\t\t)\n\t}\n}\ntype RealBrowserProps = {\n\tbaseUrl: string\n\tid: string\n\tname: string\n\tinitialRoute: string\n\tref?: React.Ref<InBrowserBrowserRef>\n}\n\nfunction InBrowserBrowserForRealz({\n\tbaseUrl,\n\tid,\n\tname,\n\tinitialRoute,\n\tref,\n}: RealBrowserProps) {\n\tconst theme = useTheme()\n\tconst [iframeKeyNumber, setIframeKeyNumber] = useState(0)\n\tconst iframeKey = id + iframeKeyNumber\n\tconst iframeRef = useRef<HTMLIFrameElement>(null)\n\n\tconst appUrl = new URL(initialRoute, baseUrl)\n\tconst [iframeSrcUrl, setIframeSrcUrl] = useState(appUrl)\n\n\tconst currentId = useRef(id)\n\tif (currentId.current !== id) {\n\t\tcurrentId.current = id\n\t\tsetIframeSrcUrl(appUrl)\n\t}\n\n\tfunction handleExtrnalNavigation(pathname?: string) {\n\t\tif (pathname) {\n\t\t\tconst newUrl = new URL(pathname, baseUrl)\n\t\t\tsetIframeSrcUrl(newUrl)\n\t\t\tsetIframeKeyNumber((prev) => prev + 1)\n\t\t}\n\t}\n\n\tuseImperativeHandle(ref, () => ({ handleExtrnalNavigation }))\n\n\treturn (\n\t\t<TooltipProvider>\n\t\t\t<div className=\"flex h-full grow flex-col\">\n\t\t\t\t<div className=\"flex items-center justify-between border-b pl-1.5\">\n\t\t\t\t\t<div className=\"mr-2 flex items-center justify-center gap-2 px-1\">\n\t\t\t\t\t\t<Tooltip>\n\t\t\t\t\t\t\t<TooltipTrigger asChild>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tclassName=\"flex aspect-square h-full w-full items-center justify-center p-1 transition disabled:opacity-40\"\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\tsetIframeKeyNumber((prev) => prev + 1)\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Icon name=\"Refresh\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</TooltipTrigger>\n\t\t\t\t\t\t\t<TooltipContent>Refresh</TooltipContent>\n\t\t\t\t\t\t</Tooltip>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"bg-background text-foreground flex flex-1 items-center border-x p-3 leading-none\">\n\t\t\t\t\t\t<a href={iframeSrcUrl.toString()} target=\"_blank\" rel=\"noreferrer\">\n\t\t\t\t\t\t\t{iframeSrcUrl.toString()}\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</div>\n\t\t\t\t\t<AppStopper name={name} />\n\t\t\t\t\t<Tooltip>\n\t\t\t\t\t\t<TooltipTrigger asChild>\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\thref={iframeSrcUrl.toString()}\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noreferrer\"\n\t\t\t\t\t\t\t\tclassName={clsx(\n\t\t\t\t\t\t\t\t\t'flex aspect-square items-center justify-center px-3.5',\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Icon name=\"ExternalLink\" />\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</TooltipTrigger>\n\t\t\t\t\t\t<TooltipContent>Open in new tab</TooltipContent>\n\t\t\t\t\t</Tooltip>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"flex h-full w-full grow dark:bg-white\">\n\t\t\t\t\t<iframe\n\t\t\t\t\t\ttitle={name}\n\t\t\t\t\t\tkey={iframeKey}\n\t\t\t\t\t\tref={iframeRef}\n\t\t\t\t\t\tsrc={iframeSrcUrl.toString()}\n\t\t\t\t\t\tclassName=\"h-full w-full grow bg-white\"\n\t\t\t\t\t\tstyle={{ colorScheme: theme }}\n\t\t\t\t\t\tallow=\"clipboard-write\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</TooltipProvider>\n\t)\n}\n","import { type BaseExerciseStepApp } from '@epic-web/workshop-utils/apps.server'\nimport { useState } from 'react'\nimport { useSearchParams } from 'react-router'\nimport { Icon } from '#app/components/icons'\nimport {\n\tInBrowserBrowser,\n\ttype InBrowserBrowserRef,\n} from '#app/components/in-browser-browser.tsx'\nimport { Loading } from '#app/components/loading.tsx'\nimport { useTheme } from '#app/routes/theme/index.tsx'\nimport { cn, getBaseUrl } from '#app/utils/misc.tsx'\nimport { useRequestInfo } from '#app/utils/root-loader.ts'\n\nexport function Preview({\n\tid,\n\tappInfo,\n\tinBrowserBrowserRef,\n}: {\n\tid?: string\n\tappInfo: {\n\t\tisRunning: boolean\n\t\tappName?: string\n\t\tname: string\n\t\ttitle: string\n\t\tportIsAvailable: boolean | null\n\t\ttype: string\n\t\tfullPath: string\n\t\tdev: BaseExerciseStepApp['dev']\n\t\ttest: BaseExerciseStepApp['test']\n\t\tstackBlitzUrl: BaseExerciseStepApp['stackBlitzUrl']\n\t} | null\n\tinBrowserBrowserRef: React.RefObject<InBrowserBrowserRef | null>\n}) {\n\tconst requestInfo = useRequestInfo()\n\tconst [searchParams] = useSearchParams()\n\tconst theme = useTheme()\n\tif (!appInfo) return <p>No app here. Sorry.</p>\n\tconst { isRunning, dev, name, portIsAvailable, title } = appInfo\n\n\tif (ENV.EPICSHOP_DEPLOYED && appInfo.stackBlitzUrl) {\n\t\tconst url = new URL(appInfo.stackBlitzUrl)\n\t\turl.searchParams.set('embed', '1')\n\t\turl.searchParams.set('theme', theme)\n\n\t\treturn (\n\t\t\t<StackBlitzEmbed\n\t\t\t\ttitle={title}\n\t\t\t\turl={url.toString()}\n\t\t\t\tloadingContent={\n\t\t\t\t\t<Loading>\n\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\tLoading{' '}\n\t\t\t\t\t\t\t<a className=\"underline\" href={appInfo.stackBlitzUrl}>\n\t\t\t\t\t\t\t\t\"{title}\"\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</Loading>\n\t\t\t\t}\n\t\t\t/>\n\t\t)\n\t}\n\n\tif (dev.type === 'script') {\n\t\tconst baseUrl = getBaseUrl({\n\t\t\tdomain: requestInfo.domain,\n\t\t\tport: dev.portNumber,\n\t\t})\n\t\treturn (\n\t\t\t<InBrowserBrowser\n\t\t\t\tref={inBrowserBrowserRef}\n\t\t\t\tisRunning={isRunning}\n\t\t\t\tid={id ?? name}\n\t\t\t\tname={name}\n\t\t\t\tportIsAvailable={portIsAvailable}\n\t\t\t\tport={dev.portNumber}\n\t\t\t\tbaseUrl={baseUrl}\n\t\t\t\tinitialRoute={searchParams.get('pathname') ?? dev.initialRoute}\n\t\t\t/>\n\t\t)\n\t} else if (dev.type === 'browser' || dev.type === 'export') {\n\t\treturn (\n\t\t\t<div className=\"scrollbar-thin scrollbar-thumb-scrollbar relative h-full grow overflow-y-auto\">\n\t\t\t\t<a\n\t\t\t\t\thref={dev.pathname}\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noreferrer\"\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t'absolute right-5 bottom-5 flex items-center justify-center rounded-full bg-gray-100 p-2.5 transition hover:bg-gray-200 dark:bg-gray-800 hover:dark:bg-gray-600',\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\t<Icon name=\"ExternalLink\" aria-hidden=\"true\" />\n\t\t\t\t\t<span className=\"sr-only\">Open in New Window</span>\n\t\t\t\t</a>\n\t\t\t\t<iframe\n\t\t\t\t\ttitle={title}\n\t\t\t\t\tsrc={dev.pathname}\n\t\t\t\t\tclassName=\"yo yo h-full w-full grow bg-white\"\n\t\t\t\t\tstyle={{ colorScheme: theme }}\n\t\t\t\t\tallow=\"clipboard-write\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t)\n\t} else {\n\t\treturn (\n\t\t\t<div className=\"flex h-full items-center justify-center text-lg\">\n\t\t\t\t<p>\n\t\t\t\t\tPreview for dev type of <code>{dev.type}</code> not supported.\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t)\n\t}\n}\n\nexport function StackBlitzEmbed({\n\turl,\n\ttitle,\n\tloadingContent,\n}: {\n\turl: string\n\ttitle?: string\n\tloadingContent: React.ReactNode\n}) {\n\tconst theme = useTheme()\n\tconst [iframeLoaded, setIframeLoaded] = useState(false)\n\n\treturn (\n\t\t<div className=\"h-full w-full grow\">\n\t\t\t{iframeLoaded ? null : (\n\t\t\t\t<div className=\"absolute inset-0 z-10 flex items-center justify-center\">\n\t\t\t\t\t{loadingContent}\n\t\t\t\t</div>\n\t\t\t)}\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\tsrc={url}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t'h-full w-full grow transition-opacity duration-300',\n\t\t\t\t\tiframeLoaded ? 'opacity-100' : 'opacity-0',\n\t\t\t\t)}\n\t\t\t\ttitle={title}\n\t\t\t\tsandbox=\"allow-forms allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox\"\n\t\t\t\tallow=\"clipboard-write\"\n\t\t\t\tstyle={{ colorScheme: theme }}\n\t\t\t/>\n\t\t</div>\n\t)\n}\n"],"names":["AppStopper","name","fetcher","useFetcher","peRedirectInput","usePERedirectInput","inFlightIntent","formData","get","inFlightState","altDown","useAltDown","Form","method","action","children","showProgressBarField","type","value","jsx","className","PortStopper","port","Button","varient","state","AppStarter","data","status","error","Loading","InBrowserBrowser","portIsAvailable","isRunning","baseUrl","id","initialRoute","ref","requestInfo","useRequestInfo","showUnmanaged","setShowUnmanaged","useState","InBrowserBrowserForRealz","jsxs","getBaseUrl","LinkButton","theme","useTheme","iframeKeyNumber","setIframeKeyNumber","iframeKey","iframeRef","useRef","appUrl","iframeSrcUrl","setIframeSrcUrl","currentId","handleExtrnalNavigation","pathname","newUrl","prev","useImperativeHandle","TooltipProvider","Tooltip","TooltipTrigger","Icon","TooltipContent","clsx","Preview","appInfo","inBrowserBrowserRef","searchParams","useSearchParams","dev","title","url","StackBlitzEmbed","cn","loadingContent","iframeLoaded","setIframeLoaded"],"mappings":"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"}
@@ -0,0 +1,558 @@
1
+ /* Export App Styles */
2
+
3
+ #epicshop-export-app-root {
4
+ display: flex;
5
+ flex-direction: column;
6
+ gap: 1.5rem;
7
+ padding: 1rem;
8
+ font-family:
9
+ ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
10
+ 'Courier New', monospace;
11
+ font-size: 0.875rem;
12
+ line-height: 1.5;
13
+ min-height: 100vh;
14
+ box-sizing: border-box;
15
+ }
16
+
17
+ #epicshop-console-output,
18
+ #epicshop-exports {
19
+ border: 1px solid #e5e7eb;
20
+ border-radius: 0.5rem;
21
+ overflow: hidden;
22
+ background: #ffffff;
23
+ }
24
+
25
+ @media (prefers-color-scheme: dark) {
26
+ #epicshop-console-output,
27
+ #epicshop-exports {
28
+ border-color: #374151;
29
+ background: #1f2937;
30
+ }
31
+ }
32
+
33
+ #epicshop-console-output h2,
34
+ #epicshop-exports h2 {
35
+ margin: 0;
36
+ padding: 0.75rem 1rem;
37
+ font-size: 0.875rem;
38
+ font-weight: 600;
39
+ background: #f3f4f6;
40
+ border-bottom: 1px solid #e5e7eb;
41
+ color: #374151;
42
+ }
43
+
44
+ @media (prefers-color-scheme: dark) {
45
+ #epicshop-console-output h2,
46
+ #epicshop-exports h2 {
47
+ background: #374151;
48
+ border-bottom-color: #4b5563;
49
+ color: #e5e7eb;
50
+ }
51
+ }
52
+
53
+ #epicshop-console-entries {
54
+ max-height: 300px;
55
+ overflow-y: auto;
56
+ padding: 0.5rem;
57
+ }
58
+
59
+ #epicshop-exports-entries {
60
+ padding: 0.75rem 1rem;
61
+ }
62
+
63
+ /* Console Entry Styles */
64
+ .console-entry {
65
+ display: flex;
66
+ align-items: flex-start;
67
+ gap: 0.5rem;
68
+ padding: 0.375rem 0.5rem;
69
+ border-radius: 0.25rem;
70
+ margin-bottom: 0.25rem;
71
+ }
72
+
73
+ .console-entry:last-child {
74
+ margin-bottom: 0;
75
+ }
76
+
77
+ .console-log {
78
+ background: transparent;
79
+ }
80
+
81
+ .console-info {
82
+ background: #eff6ff;
83
+ }
84
+
85
+ .console-warn {
86
+ background: #fffbeb;
87
+ }
88
+
89
+ .console-error {
90
+ background: #fef2f2;
91
+ }
92
+
93
+ .console-debug {
94
+ background: #f5f3ff;
95
+ }
96
+
97
+ @media (prefers-color-scheme: dark) {
98
+ .console-info {
99
+ background: rgba(59, 130, 246, 0.1);
100
+ }
101
+
102
+ .console-warn {
103
+ background: rgba(245, 158, 11, 0.1);
104
+ }
105
+
106
+ .console-error {
107
+ background: rgba(239, 68, 68, 0.1);
108
+ }
109
+
110
+ .console-debug {
111
+ background: rgba(139, 92, 246, 0.1);
112
+ }
113
+ }
114
+
115
+ .console-type {
116
+ font-size: 0.625rem;
117
+ font-weight: 700;
118
+ padding: 0.125rem 0.375rem;
119
+ border-radius: 0.25rem;
120
+ flex-shrink: 0;
121
+ text-transform: uppercase;
122
+ letter-spacing: 0.05em;
123
+ }
124
+
125
+ .console-log .console-type {
126
+ background: #e5e7eb;
127
+ color: #374151;
128
+ }
129
+
130
+ .console-info .console-type {
131
+ background: #dbeafe;
132
+ color: #1d4ed8;
133
+ }
134
+
135
+ .console-warn .console-type {
136
+ background: #fef3c7;
137
+ color: #b45309;
138
+ }
139
+
140
+ .console-error .console-type {
141
+ background: #fee2e2;
142
+ color: #dc2626;
143
+ }
144
+
145
+ .console-debug .console-type {
146
+ background: #ede9fe;
147
+ color: #7c3aed;
148
+ }
149
+
150
+ @media (prefers-color-scheme: dark) {
151
+ .console-log .console-type {
152
+ background: #4b5563;
153
+ color: #e5e7eb;
154
+ }
155
+
156
+ .console-info .console-type {
157
+ background: rgba(59, 130, 246, 0.3);
158
+ color: #93c5fd;
159
+ }
160
+
161
+ .console-warn .console-type {
162
+ background: rgba(245, 158, 11, 0.3);
163
+ color: #fcd34d;
164
+ }
165
+
166
+ .console-error .console-type {
167
+ background: rgba(239, 68, 68, 0.3);
168
+ color: #fca5a5;
169
+ }
170
+
171
+ .console-debug .console-type {
172
+ background: rgba(139, 92, 246, 0.3);
173
+ color: #c4b5fd;
174
+ }
175
+ }
176
+
177
+ .console-content {
178
+ flex: 1;
179
+ word-break: break-word;
180
+ color: #1f2937;
181
+ }
182
+
183
+ @media (prefers-color-scheme: dark) {
184
+ .console-content {
185
+ color: #e5e7eb;
186
+ }
187
+ }
188
+
189
+ /* Export Entry Styles */
190
+ .export-entry {
191
+ display: flex;
192
+ align-items: flex-start;
193
+ gap: 0.25rem;
194
+ padding: 0.5rem 0;
195
+ border-bottom: 1px solid #f3f4f6;
196
+ }
197
+
198
+ .export-entry:last-child {
199
+ border-bottom: none;
200
+ }
201
+
202
+ @media (prefers-color-scheme: dark) {
203
+ .export-entry {
204
+ border-bottom-color: #374151;
205
+ }
206
+ }
207
+
208
+ .export-name {
209
+ font-weight: 600;
210
+ color: #7c3aed;
211
+ flex-shrink: 0;
212
+ }
213
+
214
+ @media (prefers-color-scheme: dark) {
215
+ .export-name {
216
+ color: #a78bfa;
217
+ }
218
+ }
219
+
220
+ .export-colon {
221
+ color: #6b7280;
222
+ }
223
+
224
+ .export-value {
225
+ flex: 1;
226
+ word-break: break-word;
227
+ }
228
+
229
+ .export-empty {
230
+ color: #9ca3af;
231
+ font-style: italic;
232
+ padding: 0.5rem 0;
233
+ }
234
+
235
+ /* Value Type Styles */
236
+ .value-string {
237
+ color: #059669;
238
+ }
239
+
240
+ .value-number {
241
+ color: #2563eb;
242
+ }
243
+
244
+ .value-boolean {
245
+ color: #dc2626;
246
+ }
247
+
248
+ .value-null,
249
+ .value-undefined {
250
+ color: #6b7280;
251
+ font-style: italic;
252
+ }
253
+
254
+ .value-function {
255
+ color: #7c3aed;
256
+ }
257
+
258
+ .value-object,
259
+ .value-array {
260
+ color: #1f2937;
261
+ }
262
+
263
+ .value-key {
264
+ color: #0891b2;
265
+ }
266
+
267
+ .value-error {
268
+ color: #dc2626;
269
+ white-space: pre-wrap;
270
+ }
271
+
272
+ .value-date {
273
+ color: #ea580c;
274
+ }
275
+
276
+ .value-regexp {
277
+ color: #be185d;
278
+ }
279
+
280
+ .value-symbol {
281
+ color: #4f46e5;
282
+ }
283
+
284
+ .value-bigint {
285
+ color: #0d9488;
286
+ }
287
+
288
+ .value-circular {
289
+ color: #9ca3af;
290
+ font-style: italic;
291
+ }
292
+
293
+ @media (prefers-color-scheme: dark) {
294
+ .value-string {
295
+ color: #34d399;
296
+ }
297
+
298
+ .value-number {
299
+ color: #60a5fa;
300
+ }
301
+
302
+ .value-boolean {
303
+ color: #f87171;
304
+ }
305
+
306
+ .value-null,
307
+ .value-undefined {
308
+ color: #9ca3af;
309
+ }
310
+
311
+ .value-function {
312
+ color: #a78bfa;
313
+ }
314
+
315
+ .value-object,
316
+ .value-array {
317
+ color: #e5e7eb;
318
+ }
319
+
320
+ .value-key {
321
+ color: #22d3ee;
322
+ }
323
+
324
+ .value-error {
325
+ color: #f87171;
326
+ }
327
+
328
+ .value-date {
329
+ color: #fb923c;
330
+ }
331
+
332
+ .value-regexp {
333
+ color: #f472b6;
334
+ }
335
+
336
+ .value-symbol {
337
+ color: #818cf8;
338
+ }
339
+
340
+ .value-bigint {
341
+ color: #2dd4bf;
342
+ }
343
+ }
344
+
345
+ /* Promise Styles */
346
+ .value-promise {
347
+ display: inline-flex;
348
+ align-items: baseline;
349
+ gap: 0.5rem;
350
+ flex-wrap: wrap;
351
+ }
352
+
353
+ .promise-badge {
354
+ font-size: 0.625rem;
355
+ font-weight: 700;
356
+ padding: 0.125rem 0.375rem;
357
+ border-radius: 0.25rem;
358
+ background: #ddd6fe;
359
+ color: #5b21b6;
360
+ text-transform: uppercase;
361
+ letter-spacing: 0.05em;
362
+ }
363
+
364
+ @media (prefers-color-scheme: dark) {
365
+ .promise-badge {
366
+ background: rgba(139, 92, 246, 0.3);
367
+ color: #c4b5fd;
368
+ }
369
+ }
370
+
371
+ .promise-status {
372
+ font-weight: 500;
373
+ }
374
+
375
+ .promise-status.pending {
376
+ color: #d97706;
377
+ }
378
+
379
+ .promise-status.resolved {
380
+ color: #059669;
381
+ }
382
+
383
+ .promise-status.rejected {
384
+ color: #dc2626;
385
+ }
386
+
387
+ @media (prefers-color-scheme: dark) {
388
+ .promise-status.pending {
389
+ color: #fbbf24;
390
+ }
391
+
392
+ .promise-status.resolved {
393
+ color: #34d399;
394
+ }
395
+
396
+ .promise-status.rejected {
397
+ color: #f87171;
398
+ }
399
+ }
400
+
401
+ .export-promise {
402
+ background: #fef3c7;
403
+ }
404
+
405
+ .export-promise-resolved {
406
+ background: #d1fae5;
407
+ }
408
+
409
+ .export-promise-rejected {
410
+ background: #fee2e2;
411
+ }
412
+
413
+ @media (prefers-color-scheme: dark) {
414
+ .export-promise {
415
+ background: rgba(245, 158, 11, 0.1);
416
+ }
417
+
418
+ .export-promise-resolved {
419
+ background: rgba(16, 185, 129, 0.1);
420
+ }
421
+
422
+ .export-promise-rejected {
423
+ background: rgba(239, 68, 68, 0.1);
424
+ }
425
+ }
426
+
427
+ /* Map and Set Styles */
428
+ .value-map,
429
+ .value-set {
430
+ color: #0891b2;
431
+ }
432
+
433
+ @media (prefers-color-scheme: dark) {
434
+ .value-map,
435
+ .value-set {
436
+ color: #22d3ee;
437
+ }
438
+ }
439
+
440
+ /* Error Container Styles */
441
+ .error-container {
442
+ display: flex;
443
+ flex-direction: column;
444
+ gap: 0.5rem;
445
+ padding: 0.75rem;
446
+ background: #fef2f2;
447
+ border: 1px solid #fecaca;
448
+ border-radius: 0.375rem;
449
+ margin: 0.25rem 0;
450
+ }
451
+
452
+ @media (prefers-color-scheme: dark) {
453
+ .error-container {
454
+ background: rgba(239, 68, 68, 0.1);
455
+ border-color: rgba(239, 68, 68, 0.3);
456
+ }
457
+ }
458
+
459
+ .error-type {
460
+ font-size: 0.625rem;
461
+ font-weight: 700;
462
+ padding: 0.125rem 0.375rem;
463
+ border-radius: 0.25rem;
464
+ background: #fee2e2;
465
+ color: #dc2626;
466
+ text-transform: uppercase;
467
+ letter-spacing: 0.05em;
468
+ width: fit-content;
469
+ }
470
+
471
+ @media (prefers-color-scheme: dark) {
472
+ .error-type {
473
+ background: rgba(239, 68, 68, 0.3);
474
+ color: #fca5a5;
475
+ }
476
+ }
477
+
478
+ .error-message {
479
+ color: #dc2626;
480
+ font-weight: 500;
481
+ white-space: pre-wrap;
482
+ word-break: break-word;
483
+ }
484
+
485
+ @media (prefers-color-scheme: dark) {
486
+ .error-message {
487
+ color: #f87171;
488
+ }
489
+ }
490
+
491
+ .error-location {
492
+ font-size: 0.75rem;
493
+ color: #9ca3af;
494
+ }
495
+
496
+ .error-stack-details {
497
+ margin-top: 0.25rem;
498
+ }
499
+
500
+ .error-stack-details summary {
501
+ font-size: 0.75rem;
502
+ color: #6b7280;
503
+ cursor: pointer;
504
+ user-select: none;
505
+ }
506
+
507
+ .error-stack-details summary:hover {
508
+ color: #374151;
509
+ }
510
+
511
+ @media (prefers-color-scheme: dark) {
512
+ .error-stack-details summary {
513
+ color: #9ca3af;
514
+ }
515
+
516
+ .error-stack-details summary:hover {
517
+ color: #d1d5db;
518
+ }
519
+ }
520
+
521
+ .error-stack {
522
+ margin: 0.5rem 0 0 0;
523
+ padding: 0.5rem;
524
+ font-size: 0.75rem;
525
+ background: #fef2f2;
526
+ border: 1px solid #fecaca;
527
+ border-radius: 0.25rem;
528
+ overflow-x: auto;
529
+ white-space: pre-wrap;
530
+ word-break: break-word;
531
+ color: #991b1b;
532
+ }
533
+
534
+ @media (prefers-color-scheme: dark) {
535
+ .error-stack {
536
+ background: rgba(239, 68, 68, 0.05);
537
+ border-color: rgba(239, 68, 68, 0.2);
538
+ color: #fca5a5;
539
+ }
540
+ }
541
+
542
+ /* Module Error Styles */
543
+ .module-error {
544
+ padding: 1rem;
545
+ }
546
+
547
+ .module-error h3 {
548
+ margin: 0 0 0.75rem 0;
549
+ font-size: 1rem;
550
+ font-weight: 600;
551
+ color: #dc2626;
552
+ }
553
+
554
+ @media (prefers-color-scheme: dark) {
555
+ .module-error h3 {
556
+ color: #f87171;
557
+ }
558
+ }