@epic-web/workshop-app 6.88.1 → 6.89.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/client/assets/{_exerciseNumber-BkoIHU4s.js → _exerciseNumber-BZvzAp9f.js} +2 -2
- package/build/client/assets/{_exerciseNumber-BkoIHU4s.js.map → _exerciseNumber-BZvzAp9f.js.map} +1 -1
- package/build/client/assets/{_exerciseNumber_.finished-BRspQYAo.js → _exerciseNumber_.finished-D5eMZ_ok.js} +2 -2
- package/build/client/assets/{_exerciseNumber_.finished-BRspQYAo.js.map → _exerciseNumber_.finished-D5eMZ_ok.js.map} +1 -1
- package/build/client/assets/{_extra-qH2jDBa4.js → _extra-Bd0-gThk.js} +2 -2
- package/build/client/assets/{_extra-qH2jDBa4.js.map → _extra-Bd0-gThk.js.map} +1 -1
- package/build/client/assets/{_layout-Bg1RFS9D.js → _layout-CbP-ZGKA.js} +2 -2
- package/build/client/assets/{_layout-Bg1RFS9D.js.map → _layout-CbP-ZGKA.js.map} +1 -1
- package/build/client/assets/{app-BH96aYJx.js → app-BTlBQWy_.js} +2 -2
- package/build/client/assets/{app-BH96aYJx.js.map → app-BTlBQWy_.js.map} +1 -1
- package/build/client/assets/{cache-5tXauWqp.js → cache-Cjjb4Th1.js} +2 -2
- package/build/client/assets/{cache-5tXauWqp.js.map → cache-Cjjb4Th1.js.map} +1 -1
- package/build/client/assets/{db-CPB2ZdAy.js → db-n83JPsm9.js} +2 -2
- package/build/client/assets/{db-CPB2ZdAy.js.map → db-n83JPsm9.js.map} +1 -1
- package/build/client/assets/{diff-BncP0Faq.js → diff-C4LoXlDb.js} +3 -3
- package/build/client/assets/diff-C4LoXlDb.js.map +1 -0
- package/build/client/assets/{diff-IVQnB3Pk.js → diff-Fut2J8f5.js} +2 -2
- package/build/client/assets/{diff-IVQnB3Pk.js.map → diff-Fut2J8f5.js.map} +1 -1
- package/build/client/assets/{finished-CYdLPdvJ.js → finished-CsRM8483.js} +2 -2
- package/build/client/assets/{finished-CYdLPdvJ.js.map → finished-CsRM8483.js.map} +1 -1
- package/build/client/assets/{index-n4CWCwSA.js → index-BnHkUMN_.js} +2 -2
- package/build/client/assets/{index-n4CWCwSA.js.map → index-BnHkUMN_.js.map} +1 -1
- package/build/client/assets/{index-CKhNevM7.js → index-Bo0DKzj5.js} +2 -2
- package/build/client/assets/{index-CKhNevM7.js.map → index-Bo0DKzj5.js.map} +1 -1
- package/build/client/assets/{index-Cb8U85TK.js → index-m9K7ACZe.js} +2 -2
- package/build/client/assets/{index-Cb8U85TK.js.map → index-m9K7ACZe.js.map} +1 -1
- package/build/client/assets/launch-editor-C-8ah8qM.js +2 -0
- package/build/client/assets/launch-editor-C-8ah8qM.js.map +1 -0
- package/build/client/assets/{manifest-d6d8ad00.js → manifest-72875289.js} +1 -1
- package/build/client/assets/{mdx-Bvb9XNyL.js → mdx-C3Jl4O5D.js} +2 -2
- package/build/client/assets/{mdx-Bvb9XNyL.js.map → mdx-C3Jl4O5D.js.map} +1 -1
- package/build/client/assets/{playground-CEUo0DCy.js → playground-BV2y1ifv.js} +2 -2
- package/build/client/assets/{playground-CEUo0DCy.js.map → playground-BV2y1ifv.js.map} +1 -1
- package/build/client/assets/{preview-BS9wGiJ3.js → preview-Dav9KrHN.js} +2 -2
- package/build/client/assets/{preview-BS9wGiJ3.js.map → preview-Dav9KrHN.js.map} +1 -1
- package/build/server/index.js +36 -4
- package/build/server/index.js.map +1 -1
- package/package.json +3 -3
- package/build/client/assets/diff-BncP0Faq.js.map +0 -1
- package/build/client/assets/launch-editor-C8OwTN5L.js +0 -2
- package/build/client/assets/launch-editor-C8OwTN5L.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playground-CEUo0DCy.js","sources":["../../../../../node_modules/@radix-ui/react-roving-focus/dist/index.mjs","../../../../../node_modules/@radix-ui/react-tabs/dist/index.mjs","../../../app/components/discord-chat.tsx","../../../app/components/preview-tabs.tsx","../../../app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/__shared/playground.tsx"],"sourcesContent":["\"use client\";\n\n// src/roving-focus-group.tsx\nimport * as React from \"react\";\nimport { composeEventHandlers } from \"@radix-ui/primitive\";\nimport { createCollection } from \"@radix-ui/react-collection\";\nimport { useComposedRefs } from \"@radix-ui/react-compose-refs\";\nimport { createContextScope } from \"@radix-ui/react-context\";\nimport { useId } from \"@radix-ui/react-id\";\nimport { Primitive } from \"@radix-ui/react-primitive\";\nimport { useCallbackRef } from \"@radix-ui/react-use-callback-ref\";\nimport { useControllableState } from \"@radix-ui/react-use-controllable-state\";\nimport { useDirection } from \"@radix-ui/react-direction\";\nimport { jsx } from \"react/jsx-runtime\";\nvar ENTRY_FOCUS = \"rovingFocusGroup.onEntryFocus\";\nvar EVENT_OPTIONS = { bubbles: false, cancelable: true };\nvar GROUP_NAME = \"RovingFocusGroup\";\nvar [Collection, useCollection, createCollectionScope] = createCollection(GROUP_NAME);\nvar [createRovingFocusGroupContext, createRovingFocusGroupScope] = createContextScope(\n GROUP_NAME,\n [createCollectionScope]\n);\nvar [RovingFocusProvider, useRovingFocusContext] = createRovingFocusGroupContext(GROUP_NAME);\nvar RovingFocusGroup = React.forwardRef(\n (props, forwardedRef) => {\n return /* @__PURE__ */ jsx(Collection.Provider, { scope: props.__scopeRovingFocusGroup, children: /* @__PURE__ */ jsx(Collection.Slot, { scope: props.__scopeRovingFocusGroup, children: /* @__PURE__ */ jsx(RovingFocusGroupImpl, { ...props, ref: forwardedRef }) }) });\n }\n);\nRovingFocusGroup.displayName = GROUP_NAME;\nvar RovingFocusGroupImpl = React.forwardRef((props, forwardedRef) => {\n const {\n __scopeRovingFocusGroup,\n orientation,\n loop = false,\n dir,\n currentTabStopId: currentTabStopIdProp,\n defaultCurrentTabStopId,\n onCurrentTabStopIdChange,\n onEntryFocus,\n preventScrollOnEntryFocus = false,\n ...groupProps\n } = props;\n const ref = React.useRef(null);\n const composedRefs = useComposedRefs(forwardedRef, ref);\n const direction = useDirection(dir);\n const [currentTabStopId, setCurrentTabStopId] = useControllableState({\n prop: currentTabStopIdProp,\n defaultProp: defaultCurrentTabStopId ?? null,\n onChange: onCurrentTabStopIdChange,\n caller: GROUP_NAME\n });\n const [isTabbingBackOut, setIsTabbingBackOut] = React.useState(false);\n const handleEntryFocus = useCallbackRef(onEntryFocus);\n const getItems = useCollection(__scopeRovingFocusGroup);\n const isClickFocusRef = React.useRef(false);\n const [focusableItemsCount, setFocusableItemsCount] = React.useState(0);\n React.useEffect(() => {\n const node = ref.current;\n if (node) {\n node.addEventListener(ENTRY_FOCUS, handleEntryFocus);\n return () => node.removeEventListener(ENTRY_FOCUS, handleEntryFocus);\n }\n }, [handleEntryFocus]);\n return /* @__PURE__ */ jsx(\n RovingFocusProvider,\n {\n scope: __scopeRovingFocusGroup,\n orientation,\n dir: direction,\n loop,\n currentTabStopId,\n onItemFocus: React.useCallback(\n (tabStopId) => setCurrentTabStopId(tabStopId),\n [setCurrentTabStopId]\n ),\n onItemShiftTab: React.useCallback(() => setIsTabbingBackOut(true), []),\n onFocusableItemAdd: React.useCallback(\n () => setFocusableItemsCount((prevCount) => prevCount + 1),\n []\n ),\n onFocusableItemRemove: React.useCallback(\n () => setFocusableItemsCount((prevCount) => prevCount - 1),\n []\n ),\n children: /* @__PURE__ */ jsx(\n Primitive.div,\n {\n tabIndex: isTabbingBackOut || focusableItemsCount === 0 ? -1 : 0,\n \"data-orientation\": orientation,\n ...groupProps,\n ref: composedRefs,\n style: { outline: \"none\", ...props.style },\n onMouseDown: composeEventHandlers(props.onMouseDown, () => {\n isClickFocusRef.current = true;\n }),\n onFocus: composeEventHandlers(props.onFocus, (event) => {\n const isKeyboardFocus = !isClickFocusRef.current;\n if (event.target === event.currentTarget && isKeyboardFocus && !isTabbingBackOut) {\n const entryFocusEvent = new CustomEvent(ENTRY_FOCUS, EVENT_OPTIONS);\n event.currentTarget.dispatchEvent(entryFocusEvent);\n if (!entryFocusEvent.defaultPrevented) {\n const items = getItems().filter((item) => item.focusable);\n const activeItem = items.find((item) => item.active);\n const currentItem = items.find((item) => item.id === currentTabStopId);\n const candidateItems = [activeItem, currentItem, ...items].filter(\n Boolean\n );\n const candidateNodes = candidateItems.map((item) => item.ref.current);\n focusFirst(candidateNodes, preventScrollOnEntryFocus);\n }\n }\n isClickFocusRef.current = false;\n }),\n onBlur: composeEventHandlers(props.onBlur, () => setIsTabbingBackOut(false))\n }\n )\n }\n );\n});\nvar ITEM_NAME = \"RovingFocusGroupItem\";\nvar RovingFocusGroupItem = React.forwardRef(\n (props, forwardedRef) => {\n const {\n __scopeRovingFocusGroup,\n focusable = true,\n active = false,\n tabStopId,\n children,\n ...itemProps\n } = props;\n const autoId = useId();\n const id = tabStopId || autoId;\n const context = useRovingFocusContext(ITEM_NAME, __scopeRovingFocusGroup);\n const isCurrentTabStop = context.currentTabStopId === id;\n const getItems = useCollection(__scopeRovingFocusGroup);\n const { onFocusableItemAdd, onFocusableItemRemove, currentTabStopId } = context;\n React.useEffect(() => {\n if (focusable) {\n onFocusableItemAdd();\n return () => onFocusableItemRemove();\n }\n }, [focusable, onFocusableItemAdd, onFocusableItemRemove]);\n return /* @__PURE__ */ jsx(\n Collection.ItemSlot,\n {\n scope: __scopeRovingFocusGroup,\n id,\n focusable,\n active,\n children: /* @__PURE__ */ jsx(\n Primitive.span,\n {\n tabIndex: isCurrentTabStop ? 0 : -1,\n \"data-orientation\": context.orientation,\n ...itemProps,\n ref: forwardedRef,\n onMouseDown: composeEventHandlers(props.onMouseDown, (event) => {\n if (!focusable) event.preventDefault();\n else context.onItemFocus(id);\n }),\n onFocus: composeEventHandlers(props.onFocus, () => context.onItemFocus(id)),\n onKeyDown: composeEventHandlers(props.onKeyDown, (event) => {\n if (event.key === \"Tab\" && event.shiftKey) {\n context.onItemShiftTab();\n return;\n }\n if (event.target !== event.currentTarget) return;\n const focusIntent = getFocusIntent(event, context.orientation, context.dir);\n if (focusIntent !== void 0) {\n if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;\n event.preventDefault();\n const items = getItems().filter((item) => item.focusable);\n let candidateNodes = items.map((item) => item.ref.current);\n if (focusIntent === \"last\") candidateNodes.reverse();\n else if (focusIntent === \"prev\" || focusIntent === \"next\") {\n if (focusIntent === \"prev\") candidateNodes.reverse();\n const currentIndex = candidateNodes.indexOf(event.currentTarget);\n candidateNodes = context.loop ? wrapArray(candidateNodes, currentIndex + 1) : candidateNodes.slice(currentIndex + 1);\n }\n setTimeout(() => focusFirst(candidateNodes));\n }\n }),\n children: typeof children === \"function\" ? children({ isCurrentTabStop, hasTabStop: currentTabStopId != null }) : children\n }\n )\n }\n );\n }\n);\nRovingFocusGroupItem.displayName = ITEM_NAME;\nvar MAP_KEY_TO_FOCUS_INTENT = {\n ArrowLeft: \"prev\",\n ArrowUp: \"prev\",\n ArrowRight: \"next\",\n ArrowDown: \"next\",\n PageUp: \"first\",\n Home: \"first\",\n PageDown: \"last\",\n End: \"last\"\n};\nfunction getDirectionAwareKey(key, dir) {\n if (dir !== \"rtl\") return key;\n return key === \"ArrowLeft\" ? \"ArrowRight\" : key === \"ArrowRight\" ? \"ArrowLeft\" : key;\n}\nfunction getFocusIntent(event, orientation, dir) {\n const key = getDirectionAwareKey(event.key, dir);\n if (orientation === \"vertical\" && [\"ArrowLeft\", \"ArrowRight\"].includes(key)) return void 0;\n if (orientation === \"horizontal\" && [\"ArrowUp\", \"ArrowDown\"].includes(key)) return void 0;\n return MAP_KEY_TO_FOCUS_INTENT[key];\n}\nfunction focusFirst(candidates, preventScroll = false) {\n const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;\n for (const candidate of candidates) {\n if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;\n candidate.focus({ preventScroll });\n if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;\n }\n}\nfunction wrapArray(array, startIndex) {\n return array.map((_, index) => array[(startIndex + index) % array.length]);\n}\nvar Root = RovingFocusGroup;\nvar Item = RovingFocusGroupItem;\nexport {\n Item,\n Root,\n RovingFocusGroup,\n RovingFocusGroupItem,\n createRovingFocusGroupScope\n};\n//# sourceMappingURL=index.mjs.map\n","\"use client\";\n\n// src/tabs.tsx\nimport * as React from \"react\";\nimport { composeEventHandlers } from \"@radix-ui/primitive\";\nimport { createContextScope } from \"@radix-ui/react-context\";\nimport { createRovingFocusGroupScope } from \"@radix-ui/react-roving-focus\";\nimport { Presence } from \"@radix-ui/react-presence\";\nimport { Primitive } from \"@radix-ui/react-primitive\";\nimport * as RovingFocusGroup from \"@radix-ui/react-roving-focus\";\nimport { useDirection } from \"@radix-ui/react-direction\";\nimport { useControllableState } from \"@radix-ui/react-use-controllable-state\";\nimport { useId } from \"@radix-ui/react-id\";\nimport { jsx } from \"react/jsx-runtime\";\nvar TABS_NAME = \"Tabs\";\nvar [createTabsContext, createTabsScope] = createContextScope(TABS_NAME, [\n createRovingFocusGroupScope\n]);\nvar useRovingFocusGroupScope = createRovingFocusGroupScope();\nvar [TabsProvider, useTabsContext] = createTabsContext(TABS_NAME);\nvar Tabs = React.forwardRef(\n (props, forwardedRef) => {\n const {\n __scopeTabs,\n value: valueProp,\n onValueChange,\n defaultValue,\n orientation = \"horizontal\",\n dir,\n activationMode = \"automatic\",\n ...tabsProps\n } = props;\n const direction = useDirection(dir);\n const [value, setValue] = useControllableState({\n prop: valueProp,\n onChange: onValueChange,\n defaultProp: defaultValue ?? \"\",\n caller: TABS_NAME\n });\n return /* @__PURE__ */ jsx(\n TabsProvider,\n {\n scope: __scopeTabs,\n baseId: useId(),\n value,\n onValueChange: setValue,\n orientation,\n dir: direction,\n activationMode,\n children: /* @__PURE__ */ jsx(\n Primitive.div,\n {\n dir: direction,\n \"data-orientation\": orientation,\n ...tabsProps,\n ref: forwardedRef\n }\n )\n }\n );\n }\n);\nTabs.displayName = TABS_NAME;\nvar TAB_LIST_NAME = \"TabsList\";\nvar TabsList = React.forwardRef(\n (props, forwardedRef) => {\n const { __scopeTabs, loop = true, ...listProps } = props;\n const context = useTabsContext(TAB_LIST_NAME, __scopeTabs);\n const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeTabs);\n return /* @__PURE__ */ jsx(\n RovingFocusGroup.Root,\n {\n asChild: true,\n ...rovingFocusGroupScope,\n orientation: context.orientation,\n dir: context.dir,\n loop,\n children: /* @__PURE__ */ jsx(\n Primitive.div,\n {\n role: \"tablist\",\n \"aria-orientation\": context.orientation,\n ...listProps,\n ref: forwardedRef\n }\n )\n }\n );\n }\n);\nTabsList.displayName = TAB_LIST_NAME;\nvar TRIGGER_NAME = \"TabsTrigger\";\nvar TabsTrigger = React.forwardRef(\n (props, forwardedRef) => {\n const { __scopeTabs, value, disabled = false, ...triggerProps } = props;\n const context = useTabsContext(TRIGGER_NAME, __scopeTabs);\n const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeTabs);\n const triggerId = makeTriggerId(context.baseId, value);\n const contentId = makeContentId(context.baseId, value);\n const isSelected = value === context.value;\n return /* @__PURE__ */ jsx(\n RovingFocusGroup.Item,\n {\n asChild: true,\n ...rovingFocusGroupScope,\n focusable: !disabled,\n active: isSelected,\n children: /* @__PURE__ */ jsx(\n Primitive.button,\n {\n type: \"button\",\n role: \"tab\",\n \"aria-selected\": isSelected,\n \"aria-controls\": contentId,\n \"data-state\": isSelected ? \"active\" : \"inactive\",\n \"data-disabled\": disabled ? \"\" : void 0,\n disabled,\n id: triggerId,\n ...triggerProps,\n ref: forwardedRef,\n onMouseDown: composeEventHandlers(props.onMouseDown, (event) => {\n if (!disabled && event.button === 0 && event.ctrlKey === false) {\n context.onValueChange(value);\n } else {\n event.preventDefault();\n }\n }),\n onKeyDown: composeEventHandlers(props.onKeyDown, (event) => {\n if ([\" \", \"Enter\"].includes(event.key)) context.onValueChange(value);\n }),\n onFocus: composeEventHandlers(props.onFocus, () => {\n const isAutomaticActivation = context.activationMode !== \"manual\";\n if (!isSelected && !disabled && isAutomaticActivation) {\n context.onValueChange(value);\n }\n })\n }\n )\n }\n );\n }\n);\nTabsTrigger.displayName = TRIGGER_NAME;\nvar CONTENT_NAME = \"TabsContent\";\nvar TabsContent = React.forwardRef(\n (props, forwardedRef) => {\n const { __scopeTabs, value, forceMount, children, ...contentProps } = props;\n const context = useTabsContext(CONTENT_NAME, __scopeTabs);\n const triggerId = makeTriggerId(context.baseId, value);\n const contentId = makeContentId(context.baseId, value);\n const isSelected = value === context.value;\n const isMountAnimationPreventedRef = React.useRef(isSelected);\n React.useEffect(() => {\n const rAF = requestAnimationFrame(() => isMountAnimationPreventedRef.current = false);\n return () => cancelAnimationFrame(rAF);\n }, []);\n return /* @__PURE__ */ jsx(Presence, { present: forceMount || isSelected, children: ({ present }) => /* @__PURE__ */ jsx(\n Primitive.div,\n {\n \"data-state\": isSelected ? \"active\" : \"inactive\",\n \"data-orientation\": context.orientation,\n role: \"tabpanel\",\n \"aria-labelledby\": triggerId,\n hidden: !present,\n id: contentId,\n tabIndex: 0,\n ...contentProps,\n ref: forwardedRef,\n style: {\n ...props.style,\n animationDuration: isMountAnimationPreventedRef.current ? \"0s\" : void 0\n },\n children: present && children\n }\n ) });\n }\n);\nTabsContent.displayName = CONTENT_NAME;\nfunction makeTriggerId(baseId, value) {\n return `${baseId}-trigger-${value}`;\n}\nfunction makeContentId(baseId, value) {\n return `${baseId}-content-${value}`;\n}\nvar Root2 = Tabs;\nvar List = TabsList;\nvar Trigger = TabsTrigger;\nvar Content = TabsContent;\nexport {\n Content,\n List,\n Root2 as Root,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Trigger,\n createTabsScope\n};\n//# sourceMappingURL=index.mjs.map\n","import * as React from 'react'\nimport { Await, Link } from 'react-router'\nimport { Icon } from '#app/components/icons.tsx'\nimport { Loading } from '#app/components/loading.tsx'\nimport { DiscordCTA, useDiscordCTALink } from '#app/routes/_app+/discord.tsx'\nimport { useAltDown } from '#app/utils/misc.tsx'\nimport { useIsOnline } from '#app/utils/online.ts'\n\ntype EmojiData = {\n\temojiName?: string\n\temojiUrl?: string\n}\n\ntype DiscordTag = { name: string } & EmojiData\ntype DiscordReaction = { count: number } & EmojiData\n\nexport type DiscordThread = {\n\tid: string\n\ttags: DiscordTag[]\n\tname: string\n\tlink: string\n\tauthorDisplayName: string\n\tauthorHexAccentColor?: string | null\n\tauthorAvatarUrl: string | null\n\tmessagePreview: string\n\tmessageCount: number\n\tlastUpdated: string\n\tlastUpdatedDisplay: string\n\tpreviewImageUrl: string | null\n\treactions: DiscordReaction[]\n}\n\nexport function DiscordChat({\n\tdiscordPostsPromise,\n}: {\n\tdiscordPostsPromise: Promise<DiscordThread[]>\n}) {\n\treturn (\n\t\t<div className=\"flex h-full w-full flex-col gap-4 pt-4\">\n\t\t\t<div className=\"text-center\">\n\t\t\t\t<DiscordCTA />\n\t\t\t</div>\n\t\t\t<div className=\"bg-accent scrollbar-thin scrollbar-thumb-scrollbar flex-1 overflow-y-scroll pb-4\">\n\t\t\t\t<DiscordPosts discordPostsPromise={discordPostsPromise} />\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nfunction DiscordPosts({\n\tdiscordPostsPromise,\n}: {\n\tdiscordPostsPromise: Promise<DiscordThread[]>\n}) {\n\tconst ctaLink = useDiscordCTALink()\n\tconst altDown = useAltDown()\n\tconst isOnline = useIsOnline()\n\tif (!isOnline) {\n\t\treturn (\n\t\t\t<div className=\"flex h-full flex-col items-center justify-between\">\n\t\t\t\t<div className=\"text-foreground-destructive flex h-full w-full flex-col items-center justify-center\">\n\t\t\t\t\t<Icon name=\"WifiNoConnection\" size=\"xl\">\n\t\t\t\t\t\tUnable to load discord messages when offline\n\t\t\t\t\t</Icon>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t)\n\t}\n\treturn (\n\t\t<div className=\"flex h-full flex-col items-center justify-between\">\n\t\t\t<React.Suspense\n\t\t\t\tfallback={\n\t\t\t\t\t<div className=\"flex h-full w-full flex-col items-center justify-center\">\n\t\t\t\t\t\t<Loading>Loading Discord Posts</Loading>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t>\n\t\t\t\t<Await\n\t\t\t\t\tresolve={discordPostsPromise}\n\t\t\t\t\terrorElement={\n\t\t\t\t\t\t<div className=\"text-foreground-destructive\">\n\t\t\t\t\t\t\tThere was a problem loading the discord posts\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t>\n\t\t\t\t\t{(posts) => (\n\t\t\t\t\t\t<ul className=\"flex w-full flex-col gap-4 p-3 xl:p-12\">\n\t\t\t\t\t\t\t{posts.map((post) => (\n\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\tkey={post.id}\n\t\t\t\t\t\t\t\t\tclassName=\"bg-background rounded-xl border transition-all duration-200 focus-within:-translate-y-1 focus-within:shadow-lg hover:-translate-y-1 hover:shadow-lg\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<DiscordPost thread={post} />\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t)}\n\t\t\t\t</Await>\n\t\t\t</React.Suspense>\n\t\t\t<div>\n\t\t\t\t<Link\n\t\t\t\t\tto={\n\t\t\t\t\t\taltDown && !ctaLink.includes('oauth')\n\t\t\t\t\t\t\t? ctaLink.replace(/^https/, 'discord')\n\t\t\t\t\t\t\t: ctaLink\n\t\t\t\t\t}\n\t\t\t\t\ttarget={ctaLink.includes('oauth') ? undefined : '_blank'}\n\t\t\t\t\trel=\"noreferrer noopener\"\n\t\t\t\t\tonClick={\n\t\t\t\t\t\taltDown\n\t\t\t\t\t\t\t? (event) => {\n\t\t\t\t\t\t\t\t\tevent.preventDefault()\n\t\t\t\t\t\t\t\t\twindow.open(\n\t\t\t\t\t\t\t\t\t\tevent.currentTarget.href,\n\t\t\t\t\t\t\t\t\t\t'_blank',\n\t\t\t\t\t\t\t\t\t\t'noreferrer noopener',\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t}\n\t\t\t\t\tclassName=\"flex items-center gap-2 p-2 text-xl hover:underline\"\n\t\t\t\t>\n\t\t\t\t\tCreate Post <Icon name=\"ExternalLink\" />\n\t\t\t\t</Link>\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nfunction DiscordPost({ thread }: { thread: DiscordThread }) {\n\tconst reactionsWithCounts = thread.reactions.filter(\n\t\t(reaction) => reaction.count,\n\t)\n\n\treturn (\n\t\t<div>\n\t\t\t<div className=\"flex flex-col gap-2 p-4\">\n\t\t\t\t<div className=\"flex min-w-0 gap-4\">\n\t\t\t\t\t<div className=\"flex min-w-0 flex-col gap-1\">\n\t\t\t\t\t\t{thread.tags.length ? (\n\t\t\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t\t\t{thread.tags.map((tag) => (\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tkey={`${tag.name}-${tag.emojiName ?? tag.emojiUrl ?? 'tag'}`}\n\t\t\t\t\t\t\t\t\t\tclassName=\"bg-accent flex items-center justify-center gap-1 rounded-full px-2 py-1 text-sm\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<span className=\"h-3 w-3 leading-3\">\n\t\t\t\t\t\t\t\t\t\t\t<Emoji name={tag.emojiName} url={tag.emojiUrl} />\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t<span>{tag.name}</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t<strong className=\"text-xl font-bold\">{thread.name}</strong>\n\t\t\t\t\t\t<div className=\"flex min-w-0 flex-col gap-1\">\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-1\">\n\t\t\t\t\t\t\t\t{thread.authorAvatarUrl ? (\n\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\tsrc={thread.authorAvatarUrl}\n\t\t\t\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\t\t\t\tclassName=\"h-6 w-6 rounded-full\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\tclassName=\"font-bold\"\n\t\t\t\t\t\t\t\t\t\tstyle={\n\t\t\t\t\t\t\t\t\t\t\tthread.authorHexAccentColor\n\t\t\t\t\t\t\t\t\t\t\t\t? { color: thread.authorHexAccentColor }\n\t\t\t\t\t\t\t\t\t\t\t\t: {}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{thread.authorDisplayName}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t:{' '}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<span className=\"text-muted-foreground line-clamp-4 min-w-0\">\n\t\t\t\t\t\t\t\t{thread.messagePreview}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t{thread.previewImageUrl ? (\n\t\t\t\t\t\t<img\n\t\t\t\t\t\t\tsrc={thread.previewImageUrl}\n\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\tclassName=\"h-28 w-28 rounded-lg object-cover\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\n\t\t\t\t<div className=\"flex justify-between\">\n\t\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t{reactionsWithCounts.length ? (\n\t\t\t\t\t\t\t\t<ul className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t{reactionsWithCounts.map((reaction, index) => (\n\t\t\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\t\t\tkey={`${reaction.emojiName ?? reaction.emojiUrl ?? 'reaction'}-${index}`}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"border-info/60 bg-info/10 text-info flex items-center gap-1 rounded-md border px-[5px] py-[0.5px] text-sm\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"h-3 w-3 leading-3\">\n\t\t\t\t\t\t\t\t\t\t\t\t<Emoji\n\t\t\t\t\t\t\t\t\t\t\t\t\tname={reaction.emojiName}\n\t\t\t\t\t\t\t\t\t\t\t\t\turl={reaction.emojiUrl}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t<span>{reaction.count}</span>\n\t\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"flex items-center gap-1\">\n\t\t\t\t\t\t\t<span className=\"inline-flex items-center gap-1\">\n\t\t\t\t\t\t\t\t<Icon name=\"Chat\" /> {thread.messageCount}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t{` · ${thread.lastUpdatedDisplay}`}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<span className=\"flex items-center gap-4\">\n\t\t\t\t\t\t<a href={thread.link.replace(/^https/, 'discord')}>\n\t\t\t\t\t\t\t<Icon name=\"Discord\" />\n\t\t\t\t\t\t</a>\n\t\t\t\t\t\t<a href={thread.link} target=\"_blank\" rel=\"noreferrer noopener\">\n\t\t\t\t\t\t\t<Icon name=\"ExternalLink\" />\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nfunction Emoji({ name, url }: { name?: string; url?: string }) {\n\treturn url ? (\n\t\t<img src={url} alt={name} className=\"h-full w-full\" />\n\t) : name ? (\n\t\tname\n\t) : null\n}\n","import * as Tabs from '@radix-ui/react-tabs'\nimport * as React from 'react'\nimport { Link } from 'react-router'\nimport { StatusIndicator } from '#app/components/status-indicator.tsx'\nimport { cn } from '#app/utils/misc.tsx'\n\nexport type PreviewTab = {\n\tid: string\n\tlabel: string\n\tto: string\n\thidden?: boolean\n\tstatus?: 'running' | 'passed' | 'failed' | null\n\tonClick?: React.MouseEventHandler<HTMLAnchorElement>\n}\n\nexport function getPreviewSearchParams(\n\tsearchParams: URLSearchParams,\n\tpreviewValue: string,\n\tdefaultValue: string,\n) {\n\tconst next = new URLSearchParams(searchParams)\n\t// Keep URLs clean by omitting the preview param for the default tab.\n\tif (previewValue === defaultValue) {\n\t\tnext.delete('preview')\n\t} else {\n\t\tnext.set('preview', previewValue)\n\t}\n\treturn next\n}\n\nexport function PreviewTabsList({ tabs }: { tabs: PreviewTab[] }) {\n\treturn (\n\t\t<Tabs.List className=\"scrollbar-thin scrollbar-thumb-scrollbar h-14 min-h-14 overflow-x-auto border-b whitespace-nowrap\">\n\t\t\t{tabs.map((tab) => (\n\t\t\t\t<Tabs.Trigger key={tab.id} value={tab.id} hidden={tab.hidden} asChild>\n\t\t\t\t\t<Link\n\t\t\t\t\t\tid={`${tab.id}-tab`}\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t'clip-path-button radix-state-active:z-10 radix-state-active:bg-foreground radix-state-active:text-background radix-state-active:hover:bg-foreground/80 radix-state-active:hover:text-background/80 radix-state-inactive:hover:bg-foreground/20 radix-state-inactive:hover:text-foreground/80 focus:bg-foreground/80 focus:text-background/80 relative h-full px-6 py-4 font-mono text-sm uppercase outline-none',\n\t\t\t\t\t\t\ttab.hidden ? 'hidden' : 'inline-block',\n\t\t\t\t\t\t)}\n\t\t\t\t\t\tpreventScrollReset\n\t\t\t\t\t\tprefetch=\"intent\"\n\t\t\t\t\t\tonClick={tab.onClick}\n\t\t\t\t\t\tto={tab.to}\n\t\t\t\t\t>\n\t\t\t\t\t\t<span className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t{tab.status ? <StatusIndicator status={tab.status} /> : null}\n\t\t\t\t\t\t\t<span>{tab.label}</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</Link>\n\t\t\t\t</Tabs.Trigger>\n\t\t\t))}\n\t\t</Tabs.List>\n\t)\n}\n","import { toast as showToast } from 'sonner'\nimport { Icon } from '#app/components/icons.tsx'\nimport { type InBrowserBrowserRef } from '#app/components/in-browser-browser'\nimport { SimpleTooltip } from '#app/components/ui/tooltip'\nimport { SetAppToPlayground } from '#app/routes/set-playground'\nimport { PlaygroundWindow } from './playground-window'\nimport { Preview } from './preview'\n\nexport function Playground({\n\tappInfo: playgroundAppInfo,\n\tinBrowserBrowserRef,\n\tproblemAppName,\n\tallApps,\n\tisUpToDate,\n}: {\n\tappInfo: Parameters<typeof Preview>['0']['appInfo'] | null\n\tinBrowserBrowserRef: React.RefObject<InBrowserBrowserRef | null>\n\tproblemAppName?: string\n\tallApps: Array<{ name: string; displayName: string }>\n\tisUpToDate: boolean\n}) {\n\treturn (\n\t\t<PlaygroundWindow\n\t\t\tplaygroundAppName={playgroundAppInfo?.appName}\n\t\t\tproblemAppName={problemAppName}\n\t\t\tallApps={allApps}\n\t\t\tisUpToDate={isUpToDate}\n\t\t>\n\t\t\t{playgroundAppInfo?.dev?.type === 'none' ? (\n\t\t\t\t<div className=\"flex h-full flex-col items-center justify-center gap-4\">\n\t\t\t\t\t<div className=\"text-secondary-foreground text-2xl\">\n\t\t\t\t\t\tNon-UI exercise\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"text-secondary-foreground max-w-md text-center text-balance\">\n\t\t\t\t\t\tThis exercise has no application or other UI associated with it.{' '}\n\t\t\t\t\t\t<br />\n\t\t\t\t\t\tNavigate to{' '}\n\t\t\t\t\t\t<SimpleTooltip content={playgroundAppInfo.fullPath}>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclassName=\"inline-flex cursor-pointer items-center gap-1.5 underline\"\n\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\tvoid navigator.clipboard.writeText(playgroundAppInfo.fullPath)\n\t\t\t\t\t\t\t\t\tshowToast.success('Copied playground path to clipboard')\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tthe playground directory\n\t\t\t\t\t\t\t\t<Icon name=\"Copy\" size=\"sm\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</SimpleTooltip>{' '}\n\t\t\t\t\t\tin your editor and follow the exercise instructions to complete it.\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t) : playgroundAppInfo ? (\n\t\t\t\t<Preview\n\t\t\t\t\tid={playgroundAppInfo.appName}\n\t\t\t\t\tappInfo={playgroundAppInfo}\n\t\t\t\t\tinBrowserBrowserRef={inBrowserBrowserRef}\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<div className=\"flex flex-col justify-center gap-2\">\n\t\t\t\t\t<p>Please set the playground first</p>\n\t\t\t\t\t{problemAppName ? (\n\t\t\t\t\t\t<SetAppToPlayground appName={problemAppName} />\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</PlaygroundWindow>\n\t)\n}\n"],"names":["ENTRY_FOCUS","EVENT_OPTIONS","GROUP_NAME","Collection","useCollection","createCollectionScope","createCollection","createRovingFocusGroupContext","createRovingFocusGroupScope","createContextScope","RovingFocusProvider","useRovingFocusContext","RovingFocusGroup","React.forwardRef","props","forwardedRef","jsx","RovingFocusGroupImpl","__scopeRovingFocusGroup","orientation","loop","dir","currentTabStopIdProp","defaultCurrentTabStopId","onCurrentTabStopIdChange","onEntryFocus","preventScrollOnEntryFocus","groupProps","ref","React.useRef","composedRefs","useComposedRefs","direction","useDirection","currentTabStopId","setCurrentTabStopId","useControllableState","isTabbingBackOut","setIsTabbingBackOut","React.useState","handleEntryFocus","useCallbackRef","getItems","isClickFocusRef","focusableItemsCount","setFocusableItemsCount","React.useEffect","node","React.useCallback","tabStopId","prevCount","Primitive","composeEventHandlers","event","isKeyboardFocus","entryFocusEvent","items","item","activeItem","currentItem","candidateNodes","focusFirst","ITEM_NAME","RovingFocusGroupItem","focusable","active","children","itemProps","autoId","useId","id","context","isCurrentTabStop","onFocusableItemAdd","onFocusableItemRemove","focusIntent","getFocusIntent","currentIndex","wrapArray","MAP_KEY_TO_FOCUS_INTENT","getDirectionAwareKey","key","candidates","preventScroll","PREVIOUSLY_FOCUSED_ELEMENT","candidate","array","startIndex","_","index","Root","Item","TABS_NAME","createTabsContext","useRovingFocusGroupScope","TabsProvider","useTabsContext","Tabs","__scopeTabs","valueProp","onValueChange","defaultValue","activationMode","tabsProps","value","setValue","TAB_LIST_NAME","TabsList","listProps","rovingFocusGroupScope","RovingFocusGroup.Root","TRIGGER_NAME","TabsTrigger","disabled","triggerProps","triggerId","makeTriggerId","contentId","makeContentId","isSelected","RovingFocusGroup.Item","isAutomaticActivation","CONTENT_NAME","TabsContent","forceMount","contentProps","isMountAnimationPreventedRef","rAF","Presence","present","baseId","Root2","List","Trigger","Content","DiscordChat","discordPostsPromise","jsxs","DiscordCTA","DiscordPosts","ctaLink","useDiscordCTALink","altDown","useAltDown","useIsOnline","React.Suspense","Loading","Await","posts","post","DiscordPost","Link","Icon","thread","reactionsWithCounts","reaction","tag","Emoji","name","url","getPreviewSearchParams","searchParams","previewValue","next","PreviewTabsList","tabs","Tabs.List","tab","Tabs.Trigger","cn","StatusIndicator","Playground","playgroundAppInfo","inBrowserBrowserRef","problemAppName","allApps","isUpToDate","PlaygroundWindow","SimpleTooltip","showToast","Preview","SetAppToPlayground"],"mappings":"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","x_google_ignoreList":[0,1]}
|
|
1
|
+
{"version":3,"file":"playground-BV2y1ifv.js","sources":["../../../../../node_modules/@radix-ui/react-roving-focus/dist/index.mjs","../../../../../node_modules/@radix-ui/react-tabs/dist/index.mjs","../../../app/components/discord-chat.tsx","../../../app/components/preview-tabs.tsx","../../../app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/__shared/playground.tsx"],"sourcesContent":["\"use client\";\n\n// src/roving-focus-group.tsx\nimport * as React from \"react\";\nimport { composeEventHandlers } from \"@radix-ui/primitive\";\nimport { createCollection } from \"@radix-ui/react-collection\";\nimport { useComposedRefs } from \"@radix-ui/react-compose-refs\";\nimport { createContextScope } from \"@radix-ui/react-context\";\nimport { useId } from \"@radix-ui/react-id\";\nimport { Primitive } from \"@radix-ui/react-primitive\";\nimport { useCallbackRef } from \"@radix-ui/react-use-callback-ref\";\nimport { useControllableState } from \"@radix-ui/react-use-controllable-state\";\nimport { useDirection } from \"@radix-ui/react-direction\";\nimport { jsx } from \"react/jsx-runtime\";\nvar ENTRY_FOCUS = \"rovingFocusGroup.onEntryFocus\";\nvar EVENT_OPTIONS = { bubbles: false, cancelable: true };\nvar GROUP_NAME = \"RovingFocusGroup\";\nvar [Collection, useCollection, createCollectionScope] = createCollection(GROUP_NAME);\nvar [createRovingFocusGroupContext, createRovingFocusGroupScope] = createContextScope(\n GROUP_NAME,\n [createCollectionScope]\n);\nvar [RovingFocusProvider, useRovingFocusContext] = createRovingFocusGroupContext(GROUP_NAME);\nvar RovingFocusGroup = React.forwardRef(\n (props, forwardedRef) => {\n return /* @__PURE__ */ jsx(Collection.Provider, { scope: props.__scopeRovingFocusGroup, children: /* @__PURE__ */ jsx(Collection.Slot, { scope: props.__scopeRovingFocusGroup, children: /* @__PURE__ */ jsx(RovingFocusGroupImpl, { ...props, ref: forwardedRef }) }) });\n }\n);\nRovingFocusGroup.displayName = GROUP_NAME;\nvar RovingFocusGroupImpl = React.forwardRef((props, forwardedRef) => {\n const {\n __scopeRovingFocusGroup,\n orientation,\n loop = false,\n dir,\n currentTabStopId: currentTabStopIdProp,\n defaultCurrentTabStopId,\n onCurrentTabStopIdChange,\n onEntryFocus,\n preventScrollOnEntryFocus = false,\n ...groupProps\n } = props;\n const ref = React.useRef(null);\n const composedRefs = useComposedRefs(forwardedRef, ref);\n const direction = useDirection(dir);\n const [currentTabStopId, setCurrentTabStopId] = useControllableState({\n prop: currentTabStopIdProp,\n defaultProp: defaultCurrentTabStopId ?? null,\n onChange: onCurrentTabStopIdChange,\n caller: GROUP_NAME\n });\n const [isTabbingBackOut, setIsTabbingBackOut] = React.useState(false);\n const handleEntryFocus = useCallbackRef(onEntryFocus);\n const getItems = useCollection(__scopeRovingFocusGroup);\n const isClickFocusRef = React.useRef(false);\n const [focusableItemsCount, setFocusableItemsCount] = React.useState(0);\n React.useEffect(() => {\n const node = ref.current;\n if (node) {\n node.addEventListener(ENTRY_FOCUS, handleEntryFocus);\n return () => node.removeEventListener(ENTRY_FOCUS, handleEntryFocus);\n }\n }, [handleEntryFocus]);\n return /* @__PURE__ */ jsx(\n RovingFocusProvider,\n {\n scope: __scopeRovingFocusGroup,\n orientation,\n dir: direction,\n loop,\n currentTabStopId,\n onItemFocus: React.useCallback(\n (tabStopId) => setCurrentTabStopId(tabStopId),\n [setCurrentTabStopId]\n ),\n onItemShiftTab: React.useCallback(() => setIsTabbingBackOut(true), []),\n onFocusableItemAdd: React.useCallback(\n () => setFocusableItemsCount((prevCount) => prevCount + 1),\n []\n ),\n onFocusableItemRemove: React.useCallback(\n () => setFocusableItemsCount((prevCount) => prevCount - 1),\n []\n ),\n children: /* @__PURE__ */ jsx(\n Primitive.div,\n {\n tabIndex: isTabbingBackOut || focusableItemsCount === 0 ? -1 : 0,\n \"data-orientation\": orientation,\n ...groupProps,\n ref: composedRefs,\n style: { outline: \"none\", ...props.style },\n onMouseDown: composeEventHandlers(props.onMouseDown, () => {\n isClickFocusRef.current = true;\n }),\n onFocus: composeEventHandlers(props.onFocus, (event) => {\n const isKeyboardFocus = !isClickFocusRef.current;\n if (event.target === event.currentTarget && isKeyboardFocus && !isTabbingBackOut) {\n const entryFocusEvent = new CustomEvent(ENTRY_FOCUS, EVENT_OPTIONS);\n event.currentTarget.dispatchEvent(entryFocusEvent);\n if (!entryFocusEvent.defaultPrevented) {\n const items = getItems().filter((item) => item.focusable);\n const activeItem = items.find((item) => item.active);\n const currentItem = items.find((item) => item.id === currentTabStopId);\n const candidateItems = [activeItem, currentItem, ...items].filter(\n Boolean\n );\n const candidateNodes = candidateItems.map((item) => item.ref.current);\n focusFirst(candidateNodes, preventScrollOnEntryFocus);\n }\n }\n isClickFocusRef.current = false;\n }),\n onBlur: composeEventHandlers(props.onBlur, () => setIsTabbingBackOut(false))\n }\n )\n }\n );\n});\nvar ITEM_NAME = \"RovingFocusGroupItem\";\nvar RovingFocusGroupItem = React.forwardRef(\n (props, forwardedRef) => {\n const {\n __scopeRovingFocusGroup,\n focusable = true,\n active = false,\n tabStopId,\n children,\n ...itemProps\n } = props;\n const autoId = useId();\n const id = tabStopId || autoId;\n const context = useRovingFocusContext(ITEM_NAME, __scopeRovingFocusGroup);\n const isCurrentTabStop = context.currentTabStopId === id;\n const getItems = useCollection(__scopeRovingFocusGroup);\n const { onFocusableItemAdd, onFocusableItemRemove, currentTabStopId } = context;\n React.useEffect(() => {\n if (focusable) {\n onFocusableItemAdd();\n return () => onFocusableItemRemove();\n }\n }, [focusable, onFocusableItemAdd, onFocusableItemRemove]);\n return /* @__PURE__ */ jsx(\n Collection.ItemSlot,\n {\n scope: __scopeRovingFocusGroup,\n id,\n focusable,\n active,\n children: /* @__PURE__ */ jsx(\n Primitive.span,\n {\n tabIndex: isCurrentTabStop ? 0 : -1,\n \"data-orientation\": context.orientation,\n ...itemProps,\n ref: forwardedRef,\n onMouseDown: composeEventHandlers(props.onMouseDown, (event) => {\n if (!focusable) event.preventDefault();\n else context.onItemFocus(id);\n }),\n onFocus: composeEventHandlers(props.onFocus, () => context.onItemFocus(id)),\n onKeyDown: composeEventHandlers(props.onKeyDown, (event) => {\n if (event.key === \"Tab\" && event.shiftKey) {\n context.onItemShiftTab();\n return;\n }\n if (event.target !== event.currentTarget) return;\n const focusIntent = getFocusIntent(event, context.orientation, context.dir);\n if (focusIntent !== void 0) {\n if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;\n event.preventDefault();\n const items = getItems().filter((item) => item.focusable);\n let candidateNodes = items.map((item) => item.ref.current);\n if (focusIntent === \"last\") candidateNodes.reverse();\n else if (focusIntent === \"prev\" || focusIntent === \"next\") {\n if (focusIntent === \"prev\") candidateNodes.reverse();\n const currentIndex = candidateNodes.indexOf(event.currentTarget);\n candidateNodes = context.loop ? wrapArray(candidateNodes, currentIndex + 1) : candidateNodes.slice(currentIndex + 1);\n }\n setTimeout(() => focusFirst(candidateNodes));\n }\n }),\n children: typeof children === \"function\" ? children({ isCurrentTabStop, hasTabStop: currentTabStopId != null }) : children\n }\n )\n }\n );\n }\n);\nRovingFocusGroupItem.displayName = ITEM_NAME;\nvar MAP_KEY_TO_FOCUS_INTENT = {\n ArrowLeft: \"prev\",\n ArrowUp: \"prev\",\n ArrowRight: \"next\",\n ArrowDown: \"next\",\n PageUp: \"first\",\n Home: \"first\",\n PageDown: \"last\",\n End: \"last\"\n};\nfunction getDirectionAwareKey(key, dir) {\n if (dir !== \"rtl\") return key;\n return key === \"ArrowLeft\" ? \"ArrowRight\" : key === \"ArrowRight\" ? \"ArrowLeft\" : key;\n}\nfunction getFocusIntent(event, orientation, dir) {\n const key = getDirectionAwareKey(event.key, dir);\n if (orientation === \"vertical\" && [\"ArrowLeft\", \"ArrowRight\"].includes(key)) return void 0;\n if (orientation === \"horizontal\" && [\"ArrowUp\", \"ArrowDown\"].includes(key)) return void 0;\n return MAP_KEY_TO_FOCUS_INTENT[key];\n}\nfunction focusFirst(candidates, preventScroll = false) {\n const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;\n for (const candidate of candidates) {\n if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;\n candidate.focus({ preventScroll });\n if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;\n }\n}\nfunction wrapArray(array, startIndex) {\n return array.map((_, index) => array[(startIndex + index) % array.length]);\n}\nvar Root = RovingFocusGroup;\nvar Item = RovingFocusGroupItem;\nexport {\n Item,\n Root,\n RovingFocusGroup,\n RovingFocusGroupItem,\n createRovingFocusGroupScope\n};\n//# sourceMappingURL=index.mjs.map\n","\"use client\";\n\n// src/tabs.tsx\nimport * as React from \"react\";\nimport { composeEventHandlers } from \"@radix-ui/primitive\";\nimport { createContextScope } from \"@radix-ui/react-context\";\nimport { createRovingFocusGroupScope } from \"@radix-ui/react-roving-focus\";\nimport { Presence } from \"@radix-ui/react-presence\";\nimport { Primitive } from \"@radix-ui/react-primitive\";\nimport * as RovingFocusGroup from \"@radix-ui/react-roving-focus\";\nimport { useDirection } from \"@radix-ui/react-direction\";\nimport { useControllableState } from \"@radix-ui/react-use-controllable-state\";\nimport { useId } from \"@radix-ui/react-id\";\nimport { jsx } from \"react/jsx-runtime\";\nvar TABS_NAME = \"Tabs\";\nvar [createTabsContext, createTabsScope] = createContextScope(TABS_NAME, [\n createRovingFocusGroupScope\n]);\nvar useRovingFocusGroupScope = createRovingFocusGroupScope();\nvar [TabsProvider, useTabsContext] = createTabsContext(TABS_NAME);\nvar Tabs = React.forwardRef(\n (props, forwardedRef) => {\n const {\n __scopeTabs,\n value: valueProp,\n onValueChange,\n defaultValue,\n orientation = \"horizontal\",\n dir,\n activationMode = \"automatic\",\n ...tabsProps\n } = props;\n const direction = useDirection(dir);\n const [value, setValue] = useControllableState({\n prop: valueProp,\n onChange: onValueChange,\n defaultProp: defaultValue ?? \"\",\n caller: TABS_NAME\n });\n return /* @__PURE__ */ jsx(\n TabsProvider,\n {\n scope: __scopeTabs,\n baseId: useId(),\n value,\n onValueChange: setValue,\n orientation,\n dir: direction,\n activationMode,\n children: /* @__PURE__ */ jsx(\n Primitive.div,\n {\n dir: direction,\n \"data-orientation\": orientation,\n ...tabsProps,\n ref: forwardedRef\n }\n )\n }\n );\n }\n);\nTabs.displayName = TABS_NAME;\nvar TAB_LIST_NAME = \"TabsList\";\nvar TabsList = React.forwardRef(\n (props, forwardedRef) => {\n const { __scopeTabs, loop = true, ...listProps } = props;\n const context = useTabsContext(TAB_LIST_NAME, __scopeTabs);\n const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeTabs);\n return /* @__PURE__ */ jsx(\n RovingFocusGroup.Root,\n {\n asChild: true,\n ...rovingFocusGroupScope,\n orientation: context.orientation,\n dir: context.dir,\n loop,\n children: /* @__PURE__ */ jsx(\n Primitive.div,\n {\n role: \"tablist\",\n \"aria-orientation\": context.orientation,\n ...listProps,\n ref: forwardedRef\n }\n )\n }\n );\n }\n);\nTabsList.displayName = TAB_LIST_NAME;\nvar TRIGGER_NAME = \"TabsTrigger\";\nvar TabsTrigger = React.forwardRef(\n (props, forwardedRef) => {\n const { __scopeTabs, value, disabled = false, ...triggerProps } = props;\n const context = useTabsContext(TRIGGER_NAME, __scopeTabs);\n const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeTabs);\n const triggerId = makeTriggerId(context.baseId, value);\n const contentId = makeContentId(context.baseId, value);\n const isSelected = value === context.value;\n return /* @__PURE__ */ jsx(\n RovingFocusGroup.Item,\n {\n asChild: true,\n ...rovingFocusGroupScope,\n focusable: !disabled,\n active: isSelected,\n children: /* @__PURE__ */ jsx(\n Primitive.button,\n {\n type: \"button\",\n role: \"tab\",\n \"aria-selected\": isSelected,\n \"aria-controls\": contentId,\n \"data-state\": isSelected ? \"active\" : \"inactive\",\n \"data-disabled\": disabled ? \"\" : void 0,\n disabled,\n id: triggerId,\n ...triggerProps,\n ref: forwardedRef,\n onMouseDown: composeEventHandlers(props.onMouseDown, (event) => {\n if (!disabled && event.button === 0 && event.ctrlKey === false) {\n context.onValueChange(value);\n } else {\n event.preventDefault();\n }\n }),\n onKeyDown: composeEventHandlers(props.onKeyDown, (event) => {\n if ([\" \", \"Enter\"].includes(event.key)) context.onValueChange(value);\n }),\n onFocus: composeEventHandlers(props.onFocus, () => {\n const isAutomaticActivation = context.activationMode !== \"manual\";\n if (!isSelected && !disabled && isAutomaticActivation) {\n context.onValueChange(value);\n }\n })\n }\n )\n }\n );\n }\n);\nTabsTrigger.displayName = TRIGGER_NAME;\nvar CONTENT_NAME = \"TabsContent\";\nvar TabsContent = React.forwardRef(\n (props, forwardedRef) => {\n const { __scopeTabs, value, forceMount, children, ...contentProps } = props;\n const context = useTabsContext(CONTENT_NAME, __scopeTabs);\n const triggerId = makeTriggerId(context.baseId, value);\n const contentId = makeContentId(context.baseId, value);\n const isSelected = value === context.value;\n const isMountAnimationPreventedRef = React.useRef(isSelected);\n React.useEffect(() => {\n const rAF = requestAnimationFrame(() => isMountAnimationPreventedRef.current = false);\n return () => cancelAnimationFrame(rAF);\n }, []);\n return /* @__PURE__ */ jsx(Presence, { present: forceMount || isSelected, children: ({ present }) => /* @__PURE__ */ jsx(\n Primitive.div,\n {\n \"data-state\": isSelected ? \"active\" : \"inactive\",\n \"data-orientation\": context.orientation,\n role: \"tabpanel\",\n \"aria-labelledby\": triggerId,\n hidden: !present,\n id: contentId,\n tabIndex: 0,\n ...contentProps,\n ref: forwardedRef,\n style: {\n ...props.style,\n animationDuration: isMountAnimationPreventedRef.current ? \"0s\" : void 0\n },\n children: present && children\n }\n ) });\n }\n);\nTabsContent.displayName = CONTENT_NAME;\nfunction makeTriggerId(baseId, value) {\n return `${baseId}-trigger-${value}`;\n}\nfunction makeContentId(baseId, value) {\n return `${baseId}-content-${value}`;\n}\nvar Root2 = Tabs;\nvar List = TabsList;\nvar Trigger = TabsTrigger;\nvar Content = TabsContent;\nexport {\n Content,\n List,\n Root2 as Root,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Trigger,\n createTabsScope\n};\n//# sourceMappingURL=index.mjs.map\n","import * as React from 'react'\nimport { Await, Link } from 'react-router'\nimport { Icon } from '#app/components/icons.tsx'\nimport { Loading } from '#app/components/loading.tsx'\nimport { DiscordCTA, useDiscordCTALink } from '#app/routes/_app+/discord.tsx'\nimport { useAltDown } from '#app/utils/misc.tsx'\nimport { useIsOnline } from '#app/utils/online.ts'\n\ntype EmojiData = {\n\temojiName?: string\n\temojiUrl?: string\n}\n\ntype DiscordTag = { name: string } & EmojiData\ntype DiscordReaction = { count: number } & EmojiData\n\nexport type DiscordThread = {\n\tid: string\n\ttags: DiscordTag[]\n\tname: string\n\tlink: string\n\tauthorDisplayName: string\n\tauthorHexAccentColor?: string | null\n\tauthorAvatarUrl: string | null\n\tmessagePreview: string\n\tmessageCount: number\n\tlastUpdated: string\n\tlastUpdatedDisplay: string\n\tpreviewImageUrl: string | null\n\treactions: DiscordReaction[]\n}\n\nexport function DiscordChat({\n\tdiscordPostsPromise,\n}: {\n\tdiscordPostsPromise: Promise<DiscordThread[]>\n}) {\n\treturn (\n\t\t<div className=\"flex h-full w-full flex-col gap-4 pt-4\">\n\t\t\t<div className=\"text-center\">\n\t\t\t\t<DiscordCTA />\n\t\t\t</div>\n\t\t\t<div className=\"bg-accent scrollbar-thin scrollbar-thumb-scrollbar flex-1 overflow-y-scroll pb-4\">\n\t\t\t\t<DiscordPosts discordPostsPromise={discordPostsPromise} />\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nfunction DiscordPosts({\n\tdiscordPostsPromise,\n}: {\n\tdiscordPostsPromise: Promise<DiscordThread[]>\n}) {\n\tconst ctaLink = useDiscordCTALink()\n\tconst altDown = useAltDown()\n\tconst isOnline = useIsOnline()\n\tif (!isOnline) {\n\t\treturn (\n\t\t\t<div className=\"flex h-full flex-col items-center justify-between\">\n\t\t\t\t<div className=\"text-foreground-destructive flex h-full w-full flex-col items-center justify-center\">\n\t\t\t\t\t<Icon name=\"WifiNoConnection\" size=\"xl\">\n\t\t\t\t\t\tUnable to load discord messages when offline\n\t\t\t\t\t</Icon>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t)\n\t}\n\treturn (\n\t\t<div className=\"flex h-full flex-col items-center justify-between\">\n\t\t\t<React.Suspense\n\t\t\t\tfallback={\n\t\t\t\t\t<div className=\"flex h-full w-full flex-col items-center justify-center\">\n\t\t\t\t\t\t<Loading>Loading Discord Posts</Loading>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t>\n\t\t\t\t<Await\n\t\t\t\t\tresolve={discordPostsPromise}\n\t\t\t\t\terrorElement={\n\t\t\t\t\t\t<div className=\"text-foreground-destructive\">\n\t\t\t\t\t\t\tThere was a problem loading the discord posts\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t>\n\t\t\t\t\t{(posts) => (\n\t\t\t\t\t\t<ul className=\"flex w-full flex-col gap-4 p-3 xl:p-12\">\n\t\t\t\t\t\t\t{posts.map((post) => (\n\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\tkey={post.id}\n\t\t\t\t\t\t\t\t\tclassName=\"bg-background rounded-xl border transition-all duration-200 focus-within:-translate-y-1 focus-within:shadow-lg hover:-translate-y-1 hover:shadow-lg\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<DiscordPost thread={post} />\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t)}\n\t\t\t\t</Await>\n\t\t\t</React.Suspense>\n\t\t\t<div>\n\t\t\t\t<Link\n\t\t\t\t\tto={\n\t\t\t\t\t\taltDown && !ctaLink.includes('oauth')\n\t\t\t\t\t\t\t? ctaLink.replace(/^https/, 'discord')\n\t\t\t\t\t\t\t: ctaLink\n\t\t\t\t\t}\n\t\t\t\t\ttarget={ctaLink.includes('oauth') ? undefined : '_blank'}\n\t\t\t\t\trel=\"noreferrer noopener\"\n\t\t\t\t\tonClick={\n\t\t\t\t\t\taltDown\n\t\t\t\t\t\t\t? (event) => {\n\t\t\t\t\t\t\t\t\tevent.preventDefault()\n\t\t\t\t\t\t\t\t\twindow.open(\n\t\t\t\t\t\t\t\t\t\tevent.currentTarget.href,\n\t\t\t\t\t\t\t\t\t\t'_blank',\n\t\t\t\t\t\t\t\t\t\t'noreferrer noopener',\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t}\n\t\t\t\t\tclassName=\"flex items-center gap-2 p-2 text-xl hover:underline\"\n\t\t\t\t>\n\t\t\t\t\tCreate Post <Icon name=\"ExternalLink\" />\n\t\t\t\t</Link>\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nfunction DiscordPost({ thread }: { thread: DiscordThread }) {\n\tconst reactionsWithCounts = thread.reactions.filter(\n\t\t(reaction) => reaction.count,\n\t)\n\n\treturn (\n\t\t<div>\n\t\t\t<div className=\"flex flex-col gap-2 p-4\">\n\t\t\t\t<div className=\"flex min-w-0 gap-4\">\n\t\t\t\t\t<div className=\"flex min-w-0 flex-col gap-1\">\n\t\t\t\t\t\t{thread.tags.length ? (\n\t\t\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t\t\t{thread.tags.map((tag) => (\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tkey={`${tag.name}-${tag.emojiName ?? tag.emojiUrl ?? 'tag'}`}\n\t\t\t\t\t\t\t\t\t\tclassName=\"bg-accent flex items-center justify-center gap-1 rounded-full px-2 py-1 text-sm\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<span className=\"h-3 w-3 leading-3\">\n\t\t\t\t\t\t\t\t\t\t\t<Emoji name={tag.emojiName} url={tag.emojiUrl} />\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t<span>{tag.name}</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t<strong className=\"text-xl font-bold\">{thread.name}</strong>\n\t\t\t\t\t\t<div className=\"flex min-w-0 flex-col gap-1\">\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-1\">\n\t\t\t\t\t\t\t\t{thread.authorAvatarUrl ? (\n\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\tsrc={thread.authorAvatarUrl}\n\t\t\t\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\t\t\t\tclassName=\"h-6 w-6 rounded-full\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\tclassName=\"font-bold\"\n\t\t\t\t\t\t\t\t\t\tstyle={\n\t\t\t\t\t\t\t\t\t\t\tthread.authorHexAccentColor\n\t\t\t\t\t\t\t\t\t\t\t\t? { color: thread.authorHexAccentColor }\n\t\t\t\t\t\t\t\t\t\t\t\t: {}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{thread.authorDisplayName}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t:{' '}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<span className=\"text-muted-foreground line-clamp-4 min-w-0\">\n\t\t\t\t\t\t\t\t{thread.messagePreview}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t{thread.previewImageUrl ? (\n\t\t\t\t\t\t<img\n\t\t\t\t\t\t\tsrc={thread.previewImageUrl}\n\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\tclassName=\"h-28 w-28 rounded-lg object-cover\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\n\t\t\t\t<div className=\"flex justify-between\">\n\t\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t{reactionsWithCounts.length ? (\n\t\t\t\t\t\t\t\t<ul className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t{reactionsWithCounts.map((reaction, index) => (\n\t\t\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\t\t\tkey={`${reaction.emojiName ?? reaction.emojiUrl ?? 'reaction'}-${index}`}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"border-info/60 bg-info/10 text-info flex items-center gap-1 rounded-md border px-[5px] py-[0.5px] text-sm\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"h-3 w-3 leading-3\">\n\t\t\t\t\t\t\t\t\t\t\t\t<Emoji\n\t\t\t\t\t\t\t\t\t\t\t\t\tname={reaction.emojiName}\n\t\t\t\t\t\t\t\t\t\t\t\t\turl={reaction.emojiUrl}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t<span>{reaction.count}</span>\n\t\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"flex items-center gap-1\">\n\t\t\t\t\t\t\t<span className=\"inline-flex items-center gap-1\">\n\t\t\t\t\t\t\t\t<Icon name=\"Chat\" /> {thread.messageCount}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t{` · ${thread.lastUpdatedDisplay}`}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<span className=\"flex items-center gap-4\">\n\t\t\t\t\t\t<a href={thread.link.replace(/^https/, 'discord')}>\n\t\t\t\t\t\t\t<Icon name=\"Discord\" />\n\t\t\t\t\t\t</a>\n\t\t\t\t\t\t<a href={thread.link} target=\"_blank\" rel=\"noreferrer noopener\">\n\t\t\t\t\t\t\t<Icon name=\"ExternalLink\" />\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nfunction Emoji({ name, url }: { name?: string; url?: string }) {\n\treturn url ? (\n\t\t<img src={url} alt={name} className=\"h-full w-full\" />\n\t) : name ? (\n\t\tname\n\t) : null\n}\n","import * as Tabs from '@radix-ui/react-tabs'\nimport * as React from 'react'\nimport { Link } from 'react-router'\nimport { StatusIndicator } from '#app/components/status-indicator.tsx'\nimport { cn } from '#app/utils/misc.tsx'\n\nexport type PreviewTab = {\n\tid: string\n\tlabel: string\n\tto: string\n\thidden?: boolean\n\tstatus?: 'running' | 'passed' | 'failed' | null\n\tonClick?: React.MouseEventHandler<HTMLAnchorElement>\n}\n\nexport function getPreviewSearchParams(\n\tsearchParams: URLSearchParams,\n\tpreviewValue: string,\n\tdefaultValue: string,\n) {\n\tconst next = new URLSearchParams(searchParams)\n\t// Keep URLs clean by omitting the preview param for the default tab.\n\tif (previewValue === defaultValue) {\n\t\tnext.delete('preview')\n\t} else {\n\t\tnext.set('preview', previewValue)\n\t}\n\treturn next\n}\n\nexport function PreviewTabsList({ tabs }: { tabs: PreviewTab[] }) {\n\treturn (\n\t\t<Tabs.List className=\"scrollbar-thin scrollbar-thumb-scrollbar h-14 min-h-14 overflow-x-auto border-b whitespace-nowrap\">\n\t\t\t{tabs.map((tab) => (\n\t\t\t\t<Tabs.Trigger key={tab.id} value={tab.id} hidden={tab.hidden} asChild>\n\t\t\t\t\t<Link\n\t\t\t\t\t\tid={`${tab.id}-tab`}\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t'clip-path-button radix-state-active:z-10 radix-state-active:bg-foreground radix-state-active:text-background radix-state-active:hover:bg-foreground/80 radix-state-active:hover:text-background/80 radix-state-inactive:hover:bg-foreground/20 radix-state-inactive:hover:text-foreground/80 focus:bg-foreground/80 focus:text-background/80 relative h-full px-6 py-4 font-mono text-sm uppercase outline-none',\n\t\t\t\t\t\t\ttab.hidden ? 'hidden' : 'inline-block',\n\t\t\t\t\t\t)}\n\t\t\t\t\t\tpreventScrollReset\n\t\t\t\t\t\tprefetch=\"intent\"\n\t\t\t\t\t\tonClick={tab.onClick}\n\t\t\t\t\t\tto={tab.to}\n\t\t\t\t\t>\n\t\t\t\t\t\t<span className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t{tab.status ? <StatusIndicator status={tab.status} /> : null}\n\t\t\t\t\t\t\t<span>{tab.label}</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</Link>\n\t\t\t\t</Tabs.Trigger>\n\t\t\t))}\n\t\t</Tabs.List>\n\t)\n}\n","import { toast as showToast } from 'sonner'\nimport { Icon } from '#app/components/icons.tsx'\nimport { type InBrowserBrowserRef } from '#app/components/in-browser-browser'\nimport { SimpleTooltip } from '#app/components/ui/tooltip'\nimport { SetAppToPlayground } from '#app/routes/set-playground'\nimport { PlaygroundWindow } from './playground-window'\nimport { Preview } from './preview'\n\nexport function Playground({\n\tappInfo: playgroundAppInfo,\n\tinBrowserBrowserRef,\n\tproblemAppName,\n\tallApps,\n\tisUpToDate,\n}: {\n\tappInfo: Parameters<typeof Preview>['0']['appInfo'] | null\n\tinBrowserBrowserRef: React.RefObject<InBrowserBrowserRef | null>\n\tproblemAppName?: string\n\tallApps: Array<{ name: string; displayName: string }>\n\tisUpToDate: boolean\n}) {\n\treturn (\n\t\t<PlaygroundWindow\n\t\t\tplaygroundAppName={playgroundAppInfo?.appName}\n\t\t\tproblemAppName={problemAppName}\n\t\t\tallApps={allApps}\n\t\t\tisUpToDate={isUpToDate}\n\t\t>\n\t\t\t{playgroundAppInfo?.dev?.type === 'none' ? (\n\t\t\t\t<div className=\"flex h-full flex-col items-center justify-center gap-4\">\n\t\t\t\t\t<div className=\"text-secondary-foreground text-2xl\">\n\t\t\t\t\t\tNon-UI exercise\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"text-secondary-foreground max-w-md text-center text-balance\">\n\t\t\t\t\t\tThis exercise has no application or other UI associated with it.{' '}\n\t\t\t\t\t\t<br />\n\t\t\t\t\t\tNavigate to{' '}\n\t\t\t\t\t\t<SimpleTooltip content={playgroundAppInfo.fullPath}>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclassName=\"inline-flex cursor-pointer items-center gap-1.5 underline\"\n\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\tvoid navigator.clipboard.writeText(playgroundAppInfo.fullPath)\n\t\t\t\t\t\t\t\t\tshowToast.success('Copied playground path to clipboard')\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tthe playground directory\n\t\t\t\t\t\t\t\t<Icon name=\"Copy\" size=\"sm\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</SimpleTooltip>{' '}\n\t\t\t\t\t\tin your editor and follow the exercise instructions to complete it.\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t) : playgroundAppInfo ? (\n\t\t\t\t<Preview\n\t\t\t\t\tid={playgroundAppInfo.appName}\n\t\t\t\t\tappInfo={playgroundAppInfo}\n\t\t\t\t\tinBrowserBrowserRef={inBrowserBrowserRef}\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<div className=\"flex flex-col justify-center gap-2\">\n\t\t\t\t\t<p>Please set the playground first</p>\n\t\t\t\t\t{problemAppName ? (\n\t\t\t\t\t\t<SetAppToPlayground appName={problemAppName} />\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</PlaygroundWindow>\n\t)\n}\n"],"names":["ENTRY_FOCUS","EVENT_OPTIONS","GROUP_NAME","Collection","useCollection","createCollectionScope","createCollection","createRovingFocusGroupContext","createRovingFocusGroupScope","createContextScope","RovingFocusProvider","useRovingFocusContext","RovingFocusGroup","React.forwardRef","props","forwardedRef","jsx","RovingFocusGroupImpl","__scopeRovingFocusGroup","orientation","loop","dir","currentTabStopIdProp","defaultCurrentTabStopId","onCurrentTabStopIdChange","onEntryFocus","preventScrollOnEntryFocus","groupProps","ref","React.useRef","composedRefs","useComposedRefs","direction","useDirection","currentTabStopId","setCurrentTabStopId","useControllableState","isTabbingBackOut","setIsTabbingBackOut","React.useState","handleEntryFocus","useCallbackRef","getItems","isClickFocusRef","focusableItemsCount","setFocusableItemsCount","React.useEffect","node","React.useCallback","tabStopId","prevCount","Primitive","composeEventHandlers","event","isKeyboardFocus","entryFocusEvent","items","item","activeItem","currentItem","candidateNodes","focusFirst","ITEM_NAME","RovingFocusGroupItem","focusable","active","children","itemProps","autoId","useId","id","context","isCurrentTabStop","onFocusableItemAdd","onFocusableItemRemove","focusIntent","getFocusIntent","currentIndex","wrapArray","MAP_KEY_TO_FOCUS_INTENT","getDirectionAwareKey","key","candidates","preventScroll","PREVIOUSLY_FOCUSED_ELEMENT","candidate","array","startIndex","_","index","Root","Item","TABS_NAME","createTabsContext","useRovingFocusGroupScope","TabsProvider","useTabsContext","Tabs","__scopeTabs","valueProp","onValueChange","defaultValue","activationMode","tabsProps","value","setValue","TAB_LIST_NAME","TabsList","listProps","rovingFocusGroupScope","RovingFocusGroup.Root","TRIGGER_NAME","TabsTrigger","disabled","triggerProps","triggerId","makeTriggerId","contentId","makeContentId","isSelected","RovingFocusGroup.Item","isAutomaticActivation","CONTENT_NAME","TabsContent","forceMount","contentProps","isMountAnimationPreventedRef","rAF","Presence","present","baseId","Root2","List","Trigger","Content","DiscordChat","discordPostsPromise","jsxs","DiscordCTA","DiscordPosts","ctaLink","useDiscordCTALink","altDown","useAltDown","useIsOnline","React.Suspense","Loading","Await","posts","post","DiscordPost","Link","Icon","thread","reactionsWithCounts","reaction","tag","Emoji","name","url","getPreviewSearchParams","searchParams","previewValue","next","PreviewTabsList","tabs","Tabs.List","tab","Tabs.Trigger","cn","StatusIndicator","Playground","playgroundAppInfo","inBrowserBrowserRef","problemAppName","allApps","isUpToDate","PlaygroundWindow","SimpleTooltip","showToast","Preview","SetAppToPlayground"],"mappings":"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","x_google_ignoreList":[0,1]}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{j as e}from"./jsx-runtime-C5WNSv3b.js";import{r as c}from"./index-Bi6L2ga8.js";import{f as k,d as z}from"./chunk-JZWAC4HX-BuixXc9D.js";import{M as _}from"./mdx-Bvb9XNyL.js";import{L as q}from"./launch-editor-C8OwTN5L.js";import{I as v,c as w,u as K,n as U,a as Q}from"./misc-D9k_mI0z.js";import{P as R,a as E,b as L}from"./popover-Dh41LPal.js";import{B as A,L as W}from"./button-B_EAwzD_.js";import{L as T}from"./loading-BjV0PtX2.js";import{s as P}from"./progress-bar-BCbq1CuC.js";import{u as C}from"./pe-D585gnmh.js";import{u as F}from"./index-DSpM0ReJ.js";import{b as M}from"./root-loader-DAxaiFMN.js";import{n as H,T as B,j as I,k as D}from"./tooltip-H74tOFSr.js";function Y(s,t){return`/app/${encodeURIComponent(s)}/${t.split("/").map(encodeURIComponent).join("/")}`}function G(s){const t=new Map,a=new Map,o=new Map,n=new Map;function u(r,l){let i=o.get(r);if(i||(i=new Set,o.set(r,i)),i.has(l.path))return;i.add(l.path);let d=a.get(r);d||(d=[],a.set(r,d)),d.push(l)}const h=[...s].sort((r,l)=>r.path.localeCompare(l.path));for(const r of h){t.set(r.path,r),n.set("",n.get("")??r.path);const l=r.path.split("/").filter(Boolean);for(const[i,d]of l.entries()){const m=l.slice(0,i+1).join("/"),p=i===0?"":l.slice(0,i).join("/"),g=i<l.length-1;u(p,{name:d,path:m,isDirectory:g}),g&&!n.has(m)&&n.set(m,r.path)}}for(const r of a.values())r.sort((l,i)=>l.isDirectory&&!i.isDirectory?-1:!l.isDirectory&&i.isDirectory?1:l.name.localeCompare(i.name));return{filesByPath:t,childrenByDirectory:a,firstFileByDirectory:n}}function J(s){const t=[];if(!s)return t;const a=s.split("/").filter(Boolean);for(const[o,n]of a.entries())t.push({label:n,path:a.slice(0,o+1).join("/"),parentPath:o===0?"":a.slice(0,o).join("/"),isDirectory:o<a.length-1});return t}function V(s,t){return s.isDirectory?t.get(s.path)??null:s.path}function X({entries:s,selectedPath:t,firstFileByDirectory:a,onSelectPath:o}){return s.length===0?e.jsx("p",{className:"text-muted-foreground px-3 py-2 text-xs",children:"No files available."}):e.jsx("div",{className:"max-h-72 overflow-y-auto",children:s.map(n=>{const u=V(n,a),h=!n.isDirectory&&t===n.path;return e.jsxs("button",{type:"button",onClick:()=>{u&&o(u)},className:w("hover:bg-muted/70 flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-sm",h?"bg-muted":null),children:[e.jsx(v,{name:"Files",className:w("size-3.5 shrink-0",n.isDirectory?"text-muted-foreground":"text-foreground/80"),"aria-hidden":!0}),e.jsxs("span",{className:"truncate",children:[n.name,n.isDirectory?"/":""]})]},n.path)})})}function Z({appName:s,file:t}){const[a,o]=c.useState(null),[n,u]=c.useState(null),[h,r]=c.useState(!1),l=t?Y(s,t.path):null,i=t?.kind==="text"||t?.kind==="markdown";if(c.useEffect(()=>{if(!t||!i){o(null),u(null),r(!1);return}const d=new AbortController;o(null),u(null),r(!0);const m=new URLSearchParams({path:t.path,kind:t.kind,...t.language?{language:t.language}:{}});return fetch(`/app/${encodeURIComponent(s)}/file-preview?${m}`,{signal:d.signal}).then(async p=>{if(!p.ok)throw new Error(`Failed to render file (${p.status})`);const g=await p.json();o(g.code),r(!1)}).catch(p=>{d.signal.aborted||(u(p instanceof Error?p.message:String(p)),r(!1))}),()=>d.abort()},[s,t,i]),!t)return e.jsx("div",{className:"text-muted-foreground flex h-full items-center justify-center p-6 text-sm",children:"Choose a file from the file picker."});if(n)return e.jsx("div",{className:"text-foreground-destructive p-4 text-sm",children:n});if(i){if(h||!a)return e.jsx("div",{className:"text-muted-foreground flex h-full items-center justify-center p-6 text-sm",children:"Rendering file..."});const d=t.kind==="text"?"max-w-none [&_.group]:w-full [&_pre]:w-full":"prose dark:prose-invert max-w-none";return e.jsx("div",{className:"h-full overflow-auto p-4",children:e.jsx("div",{className:d,children:e.jsx(_,{code:a})})})}return t.kind==="image"&&l?e.jsx("div",{className:"flex h-full items-center justify-center p-4",children:e.jsx("img",{src:l,alt:t.path,className:"border-border max-h-full max-w-full rounded border object-contain"})}):t.kind==="video"&&l?e.jsx("div",{className:"flex h-full items-center justify-center p-4",children:e.jsx("video",{src:l,controls:!0,className:"bg-background border-border max-h-full max-w-full rounded border"})}):e.jsxs("div",{className:"flex h-full flex-col items-center justify-center gap-3 p-6 text-center",children:[e.jsxs("p",{className:"text-muted-foreground text-sm",children:["Preview is not available for this file type (",t.mimeType,")."]}),l?e.jsx("a",{href:l,target:"_blank",rel:"noreferrer",className:"text-sm underline",children:"Open file"}):null]})}function ee({appName:s}){const[t,a]=c.useState([]),[o,n]=c.useState(!0),[u,h]=c.useState(null),[r,l]=c.useState(null),[i,d]=c.useState(!1),[m,p]=c.useState(""),g=c.useRef(null);c.useEffect(()=>{const f=new AbortController;return a([]),n(!0),h(null),l(null),fetch(`/app/${encodeURIComponent(s)}/files`,{signal:f.signal}).then(async x=>{if(!x.ok)throw new Error(`Failed to load files (${x.status})`);const S=await x.json();a(S.files??[]),n(!1)}).catch(x=>{f.signal.aborted||(h(x instanceof Error?x.message:String(x)),n(!1))}),()=>f.abort()},[s]);const b=c.useMemo(()=>G(t),[t]),j=r?b.filesByPath.get(r)??null:null,y=c.useMemo(()=>J(r),[r]),N=c.useMemo(()=>{const f=m.trim().toLowerCase();return f?t.filter(x=>x.path.toLowerCase().includes(f)):t},[m,t]);return c.useEffect(()=>{r&&b.filesByPath.has(r)||l(t[0]?.path??null)},[t,b.filesByPath,r]),c.useEffect(()=>{const f=g.current;if(!f)return;const x=requestAnimationFrame(()=>{f.scrollLeft=f.scrollWidth});return()=>cancelAnimationFrame(x)},[r]),o?e.jsx("div",{className:"text-muted-foreground flex h-full items-center justify-center text-sm",children:"Loading files..."}):u?e.jsx("div",{className:"text-foreground-destructive flex h-full items-center justify-center px-3 text-sm",children:u}):t.length===0?e.jsx("div",{className:"text-muted-foreground flex h-full items-center justify-center text-sm",children:"No files to display."}):e.jsxs("div",{className:"bg-background flex h-full min-h-0 w-full flex-col",children:[e.jsxs("div",{className:"border-border flex h-10 shrink-0 items-center gap-2 border-b px-2",children:[e.jsxs(R,{open:i,onOpenChange:d,children:[e.jsx(E,{asChild:!0,children:e.jsx("button",{type:"button","aria-label":"Open file chooser",className:"hover:bg-muted text-foreground inline-flex h-8 w-8 items-center justify-center rounded border border-transparent",children:e.jsx(v,{name:"Files",className:"size-4"})})}),e.jsxs(L,{align:"start",className:"w-[min(420px,80vw)] p-0",children:[e.jsx("div",{className:"border-border border-b p-2",children:e.jsx("input",{type:"text",value:m,onChange:f=>p(f.currentTarget.value),placeholder:"Filter files...",className:"border-border bg-background w-full rounded border px-2 py-1 text-sm outline-none"})}),e.jsx("div",{className:"max-h-80 overflow-y-auto p-1",children:N.length?N.map(f=>e.jsx("button",{type:"button",onClick:()=>{l(f.path),d(!1)},className:w("hover:bg-muted/70 block w-full rounded px-2 py-1.5 text-left font-mono text-xs",r===f.path?"bg-muted":null),children:f.path},f.path)):e.jsx("p",{className:"text-muted-foreground px-2 py-2 text-xs",children:"No matching files."})})]})]}),e.jsx("div",{ref:g,className:"scrollbar-thin scrollbar-thumb-scrollbar min-w-0 flex-1 overflow-x-auto",children:e.jsxs("div",{className:"flex w-max min-w-full items-center gap-1 pr-2",children:[y.length>0?e.jsx("span",{className:"text-muted-foreground text-xs",children:"/"}):null,y.map((f,x)=>{const S=b.childrenByDirectory.get(f.parentPath),$=f.path===r;return e.jsxs(c.Fragment,{children:[e.jsxs(R,{children:[e.jsx(E,{asChild:!0,children:e.jsx("button",{type:"button",className:w("hover:bg-muted inline-flex h-7 items-center rounded px-2 font-mono text-xs",$?"bg-muted text-foreground":"text-muted-foreground"),children:f.label})}),e.jsx(L,{align:"start",className:"w-72 p-1",children:e.jsx(X,{entries:S??[],selectedPath:r,firstFileByDirectory:b.firstFileByDirectory,onSelectPath:l})})]}),x<y.length-1?e.jsx("span",{className:"text-muted-foreground text-xs",children:"/"}):null]},`${f.path}:${x}`)})]})}),j?e.jsx(q,{appName:s,appFile:`${j.path},1,1`,className:"text-muted-foreground hover:text-foreground text-xs",children:"Open in editor"}):null]}),e.jsx("div",{className:"min-h-0 flex-1",children:e.jsx(Z,{appName:s,file:j})})]})}function te({name:s}){const t=k(),a=C(),o=t.formData?.get("intent"),n=o==="stop"?"Stopping App":o==="restart"?"Restarting App":null,u=K();return e.jsxs(t.Form,{method:"POST",action:"/start",children:[a,P,e.jsx("input",{type:"hidden",name:"name",value:s}),e.jsx("button",{type:"submit",name:"intent",value:u?"restart":"stop",className:"h-full border-r px-3 py-4 font-mono text-xs leading-none uppercase",children:n||(u?"Restart App":"Stop App")})]})}function O({port:s}){const t=k(),a=C();return e.jsxs(t.Form,{method:"POST",action:"/start",children:[a,P,e.jsx("input",{type:"hidden",name:"port",value:s}),e.jsx(A,{varient:"mono",type:"submit",name:"intent",value:"stop-port",children:t.state==="idle"?"Stop Port":"Stopping Port"})]})}function re({name:s}){const t=k(),a=C();return t.data?.status==="app-not-started"?t.data.error==="port-unavailable"?e.jsxs("div",{children:["The port is unavailable. Would you like to stop whatever is running on that port and try again?",e.jsx(O,{port:t.data.port})]}):e.jsx("div",{children:"An unknown error has happened."}):e.jsxs(t.Form,{method:"POST",action:"/start",children:[a,P,e.jsx("input",{type:"hidden",name:"name",value:s}),t.state==="idle"?e.jsx(A,{type:"submit",name:"intent",value:"start",varient:"mono",children:"Start App"}):e.jsx("div",{children:e.jsx(T,{children:"Starting App"})})]})}function se({name:s,port:t,portIsAvailable:a,isRunning:o,baseUrl:n,id:u,initialRoute:h,ref:r}){const l=M(),[i,d]=c.useState(!1);return o||i?e.jsx(ne,{baseUrl:n,id:u,name:s,ref:r,initialRoute:h}):a===!1?e.jsxs("div",{className:"flex flex-col items-center justify-center",children:[e.jsxs("p",{className:"max-w-xs pb-5 text-center",role:"status",children:["The port for this app is unavailable. It could be that you're running it ",e.jsx("a",{href:U({domain:l.domain,port:t}),className:"underline",target:"_blank",rel:"noreferrer",children:"elsewhere"}),". ",e.jsx(W,{onClick:()=>d(!0),children:"Show here anyway"})]}),e.jsx(O,{port:t})]}):e.jsx("div",{className:"flex h-full flex-col items-center justify-center",children:e.jsx(re,{name:s})})}function ne({baseUrl:s,id:t,name:a,initialRoute:o,ref:n}){const u=F(),[h,r]=c.useState(0),l=t+h,i=c.useRef(null),d=new URL(o,s),[m,p]=c.useState(d),g=c.useRef(t);g.current!==t&&(g.current=t,p(d));function b(j){if(j){const y=new URL(j,s);p(y),r(N=>N+1)}}return c.useImperativeHandle(n,()=>({handleExtrnalNavigation:b})),e.jsx(H,{children:e.jsxs("div",{className:"flex h-full grow flex-col",children:[e.jsxs("div",{className:"flex items-center justify-between border-b pl-1.5",children:[e.jsx("div",{className:"mr-2 flex items-center justify-center gap-2 px-1",children:e.jsxs(B,{children:[e.jsx(I,{asChild:!0,children:e.jsx("button",{type:"button",className:"flex aspect-square h-full w-full items-center justify-center p-1 transition disabled:opacity-40",onClick:()=>{r(j=>j+1)},children:e.jsx(v,{name:"Refresh","aria-hidden":"true"})})}),e.jsx(D,{children:"Refresh"})]})}),e.jsx("div",{className:"bg-background text-foreground flex flex-1 items-center border-x p-3 leading-none",children:e.jsx("a",{href:m.toString(),target:"_blank",rel:"noreferrer",children:m.toString()})}),e.jsx(te,{name:a}),e.jsxs(B,{children:[e.jsx(I,{asChild:!0,children:e.jsx("a",{href:m.toString(),target:"_blank",rel:"noreferrer",className:Q("flex aspect-square items-center justify-center px-3.5"),children:e.jsx(v,{name:"ExternalLink"})})}),e.jsx(D,{children:"Open in new tab"})]})]}),e.jsx("div",{className:"bg-background flex h-full w-full grow",children:e.jsx("iframe",{title:a,ref:i,src:m.toString(),className:"bg-background h-full w-full grow",style:{colorScheme:u},allow:"clipboard-write"},l)})]})})}function we({id:s,appInfo:t,inBrowserBrowserRef:a}){const o=M(),[n]=z(),u=F();if(!t)return e.jsx("p",{children:"No app here. Sorry."});const{isRunning:h,dev:r,name:l,portIsAvailable:i,title:d}=t;if(!r)return e.jsx("div",{className:"flex h-full items-center justify-center text-lg",children:e.jsx("p",{children:"No preview available for this app."})});if(ENV.EPICSHOP_DEPLOYED&&t.stackBlitzUrl){const m=new URL(t.stackBlitzUrl);return m.searchParams.set("embed","1"),m.searchParams.set("theme",u),e.jsx(le,{title:d,url:m.toString(),loadingContent:e.jsx(T,{children:e.jsxs("span",{children:["Loading"," ",e.jsxs("a",{className:"underline",href:t.stackBlitzUrl,children:['"',d,'"']})]})})})}if(r.type==="script"){const m=U({domain:o.domain,port:r.portNumber});return e.jsx(se,{ref:a,isRunning:h,id:s??l,name:l,portIsAvailable:i,port:r.portNumber,baseUrl:m,initialRoute:n.get("pathname")??r.initialRoute})}else return r.type==="browser"||r.type==="export"?e.jsxs("div",{className:"scrollbar-thin scrollbar-thumb-scrollbar relative h-full grow overflow-y-auto",children:[e.jsxs("a",{href:r.pathname,target:"_blank",rel:"noreferrer",className:w("bg-muted text-foreground hover:bg-muted/80 absolute right-5 bottom-5 flex items-center justify-center rounded-full p-2.5 transition"),children:[e.jsx(v,{name:"ExternalLink","aria-hidden":"true"}),e.jsx("span",{className:"sr-only",children:"Open in New Window"})]}),e.jsx("iframe",{title:d,src:r.pathname,className:"bg-background h-full w-full grow",style:{colorScheme:u},allow:"clipboard-write"})]}):r.type==="file"?e.jsx(ee,{appName:t.appName??l}):e.jsx("div",{className:"flex h-full items-center justify-center text-lg",children:e.jsxs("p",{children:["Preview for dev type of ",e.jsx("code",{children:r.type})," not supported."]})})}function le({url:s,title:t,loadingContent:a}){const o=F(),[n,u]=c.useState(!1);return e.jsxs("div",{className:"h-full w-full grow",children:[n?null:e.jsx("div",{className:"absolute inset-0 z-10 flex items-center justify-center",children:a}),e.jsx("iframe",{onLoad:()=>u(!0),onError:()=>u(!0),src:s,className:w("h-full w-full grow transition-opacity duration-300",n?"opacity-100":"opacity-0"),title:t,sandbox:"allow-forms allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox",allow:"clipboard-write",style:{colorScheme:o}})]})}export{we as P};
|
|
2
|
-
//# sourceMappingURL=preview-
|
|
1
|
+
import{j as e}from"./jsx-runtime-C5WNSv3b.js";import{r as c}from"./index-Bi6L2ga8.js";import{f as k,d as z}from"./chunk-JZWAC4HX-BuixXc9D.js";import{M as _}from"./mdx-C3Jl4O5D.js";import{L as q}from"./launch-editor-C-8ah8qM.js";import{I as v,c as w,u as K,n as U,a as Q}from"./misc-D9k_mI0z.js";import{P as R,a as E,b as L}from"./popover-Dh41LPal.js";import{B as A,L as W}from"./button-B_EAwzD_.js";import{L as T}from"./loading-BjV0PtX2.js";import{s as P}from"./progress-bar-BCbq1CuC.js";import{u as C}from"./pe-D585gnmh.js";import{u as F}from"./index-DSpM0ReJ.js";import{b as M}from"./root-loader-DAxaiFMN.js";import{n as H,T as B,j as I,k as D}from"./tooltip-H74tOFSr.js";function Y(s,t){return`/app/${encodeURIComponent(s)}/${t.split("/").map(encodeURIComponent).join("/")}`}function G(s){const t=new Map,a=new Map,o=new Map,n=new Map;function u(r,l){let i=o.get(r);if(i||(i=new Set,o.set(r,i)),i.has(l.path))return;i.add(l.path);let d=a.get(r);d||(d=[],a.set(r,d)),d.push(l)}const h=[...s].sort((r,l)=>r.path.localeCompare(l.path));for(const r of h){t.set(r.path,r),n.set("",n.get("")??r.path);const l=r.path.split("/").filter(Boolean);for(const[i,d]of l.entries()){const m=l.slice(0,i+1).join("/"),p=i===0?"":l.slice(0,i).join("/"),g=i<l.length-1;u(p,{name:d,path:m,isDirectory:g}),g&&!n.has(m)&&n.set(m,r.path)}}for(const r of a.values())r.sort((l,i)=>l.isDirectory&&!i.isDirectory?-1:!l.isDirectory&&i.isDirectory?1:l.name.localeCompare(i.name));return{filesByPath:t,childrenByDirectory:a,firstFileByDirectory:n}}function J(s){const t=[];if(!s)return t;const a=s.split("/").filter(Boolean);for(const[o,n]of a.entries())t.push({label:n,path:a.slice(0,o+1).join("/"),parentPath:o===0?"":a.slice(0,o).join("/"),isDirectory:o<a.length-1});return t}function V(s,t){return s.isDirectory?t.get(s.path)??null:s.path}function X({entries:s,selectedPath:t,firstFileByDirectory:a,onSelectPath:o}){return s.length===0?e.jsx("p",{className:"text-muted-foreground px-3 py-2 text-xs",children:"No files available."}):e.jsx("div",{className:"max-h-72 overflow-y-auto",children:s.map(n=>{const u=V(n,a),h=!n.isDirectory&&t===n.path;return e.jsxs("button",{type:"button",onClick:()=>{u&&o(u)},className:w("hover:bg-muted/70 flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-sm",h?"bg-muted":null),children:[e.jsx(v,{name:"Files",className:w("size-3.5 shrink-0",n.isDirectory?"text-muted-foreground":"text-foreground/80"),"aria-hidden":!0}),e.jsxs("span",{className:"truncate",children:[n.name,n.isDirectory?"/":""]})]},n.path)})})}function Z({appName:s,file:t}){const[a,o]=c.useState(null),[n,u]=c.useState(null),[h,r]=c.useState(!1),l=t?Y(s,t.path):null,i=t?.kind==="text"||t?.kind==="markdown";if(c.useEffect(()=>{if(!t||!i){o(null),u(null),r(!1);return}const d=new AbortController;o(null),u(null),r(!0);const m=new URLSearchParams({path:t.path,kind:t.kind,...t.language?{language:t.language}:{}});return fetch(`/app/${encodeURIComponent(s)}/file-preview?${m}`,{signal:d.signal}).then(async p=>{if(!p.ok)throw new Error(`Failed to render file (${p.status})`);const g=await p.json();o(g.code),r(!1)}).catch(p=>{d.signal.aborted||(u(p instanceof Error?p.message:String(p)),r(!1))}),()=>d.abort()},[s,t,i]),!t)return e.jsx("div",{className:"text-muted-foreground flex h-full items-center justify-center p-6 text-sm",children:"Choose a file from the file picker."});if(n)return e.jsx("div",{className:"text-foreground-destructive p-4 text-sm",children:n});if(i){if(h||!a)return e.jsx("div",{className:"text-muted-foreground flex h-full items-center justify-center p-6 text-sm",children:"Rendering file..."});const d=t.kind==="text"?"max-w-none [&_.group]:w-full [&_pre]:w-full":"prose dark:prose-invert max-w-none";return e.jsx("div",{className:"h-full overflow-auto p-4",children:e.jsx("div",{className:d,children:e.jsx(_,{code:a})})})}return t.kind==="image"&&l?e.jsx("div",{className:"flex h-full items-center justify-center p-4",children:e.jsx("img",{src:l,alt:t.path,className:"border-border max-h-full max-w-full rounded border object-contain"})}):t.kind==="video"&&l?e.jsx("div",{className:"flex h-full items-center justify-center p-4",children:e.jsx("video",{src:l,controls:!0,className:"bg-background border-border max-h-full max-w-full rounded border"})}):e.jsxs("div",{className:"flex h-full flex-col items-center justify-center gap-3 p-6 text-center",children:[e.jsxs("p",{className:"text-muted-foreground text-sm",children:["Preview is not available for this file type (",t.mimeType,")."]}),l?e.jsx("a",{href:l,target:"_blank",rel:"noreferrer",className:"text-sm underline",children:"Open file"}):null]})}function ee({appName:s}){const[t,a]=c.useState([]),[o,n]=c.useState(!0),[u,h]=c.useState(null),[r,l]=c.useState(null),[i,d]=c.useState(!1),[m,p]=c.useState(""),g=c.useRef(null);c.useEffect(()=>{const f=new AbortController;return a([]),n(!0),h(null),l(null),fetch(`/app/${encodeURIComponent(s)}/files`,{signal:f.signal}).then(async x=>{if(!x.ok)throw new Error(`Failed to load files (${x.status})`);const S=await x.json();a(S.files??[]),n(!1)}).catch(x=>{f.signal.aborted||(h(x instanceof Error?x.message:String(x)),n(!1))}),()=>f.abort()},[s]);const b=c.useMemo(()=>G(t),[t]),j=r?b.filesByPath.get(r)??null:null,y=c.useMemo(()=>J(r),[r]),N=c.useMemo(()=>{const f=m.trim().toLowerCase();return f?t.filter(x=>x.path.toLowerCase().includes(f)):t},[m,t]);return c.useEffect(()=>{r&&b.filesByPath.has(r)||l(t[0]?.path??null)},[t,b.filesByPath,r]),c.useEffect(()=>{const f=g.current;if(!f)return;const x=requestAnimationFrame(()=>{f.scrollLeft=f.scrollWidth});return()=>cancelAnimationFrame(x)},[r]),o?e.jsx("div",{className:"text-muted-foreground flex h-full items-center justify-center text-sm",children:"Loading files..."}):u?e.jsx("div",{className:"text-foreground-destructive flex h-full items-center justify-center px-3 text-sm",children:u}):t.length===0?e.jsx("div",{className:"text-muted-foreground flex h-full items-center justify-center text-sm",children:"No files to display."}):e.jsxs("div",{className:"bg-background flex h-full min-h-0 w-full flex-col",children:[e.jsxs("div",{className:"border-border flex h-10 shrink-0 items-center gap-2 border-b px-2",children:[e.jsxs(R,{open:i,onOpenChange:d,children:[e.jsx(E,{asChild:!0,children:e.jsx("button",{type:"button","aria-label":"Open file chooser",className:"hover:bg-muted text-foreground inline-flex h-8 w-8 items-center justify-center rounded border border-transparent",children:e.jsx(v,{name:"Files",className:"size-4"})})}),e.jsxs(L,{align:"start",className:"w-[min(420px,80vw)] p-0",children:[e.jsx("div",{className:"border-border border-b p-2",children:e.jsx("input",{type:"text",value:m,onChange:f=>p(f.currentTarget.value),placeholder:"Filter files...",className:"border-border bg-background w-full rounded border px-2 py-1 text-sm outline-none"})}),e.jsx("div",{className:"max-h-80 overflow-y-auto p-1",children:N.length?N.map(f=>e.jsx("button",{type:"button",onClick:()=>{l(f.path),d(!1)},className:w("hover:bg-muted/70 block w-full rounded px-2 py-1.5 text-left font-mono text-xs",r===f.path?"bg-muted":null),children:f.path},f.path)):e.jsx("p",{className:"text-muted-foreground px-2 py-2 text-xs",children:"No matching files."})})]})]}),e.jsx("div",{ref:g,className:"scrollbar-thin scrollbar-thumb-scrollbar min-w-0 flex-1 overflow-x-auto",children:e.jsxs("div",{className:"flex w-max min-w-full items-center gap-1 pr-2",children:[y.length>0?e.jsx("span",{className:"text-muted-foreground text-xs",children:"/"}):null,y.map((f,x)=>{const S=b.childrenByDirectory.get(f.parentPath),$=f.path===r;return e.jsxs(c.Fragment,{children:[e.jsxs(R,{children:[e.jsx(E,{asChild:!0,children:e.jsx("button",{type:"button",className:w("hover:bg-muted inline-flex h-7 items-center rounded px-2 font-mono text-xs",$?"bg-muted text-foreground":"text-muted-foreground"),children:f.label})}),e.jsx(L,{align:"start",className:"w-72 p-1",children:e.jsx(X,{entries:S??[],selectedPath:r,firstFileByDirectory:b.firstFileByDirectory,onSelectPath:l})})]}),x<y.length-1?e.jsx("span",{className:"text-muted-foreground text-xs",children:"/"}):null]},`${f.path}:${x}`)})]})}),j?e.jsx(q,{appName:s,appFile:`${j.path},1,1`,className:"text-muted-foreground hover:text-foreground text-xs",children:"Open in editor"}):null]}),e.jsx("div",{className:"min-h-0 flex-1",children:e.jsx(Z,{appName:s,file:j})})]})}function te({name:s}){const t=k(),a=C(),o=t.formData?.get("intent"),n=o==="stop"?"Stopping App":o==="restart"?"Restarting App":null,u=K();return e.jsxs(t.Form,{method:"POST",action:"/start",children:[a,P,e.jsx("input",{type:"hidden",name:"name",value:s}),e.jsx("button",{type:"submit",name:"intent",value:u?"restart":"stop",className:"h-full border-r px-3 py-4 font-mono text-xs leading-none uppercase",children:n||(u?"Restart App":"Stop App")})]})}function O({port:s}){const t=k(),a=C();return e.jsxs(t.Form,{method:"POST",action:"/start",children:[a,P,e.jsx("input",{type:"hidden",name:"port",value:s}),e.jsx(A,{varient:"mono",type:"submit",name:"intent",value:"stop-port",children:t.state==="idle"?"Stop Port":"Stopping Port"})]})}function re({name:s}){const t=k(),a=C();return t.data?.status==="app-not-started"?t.data.error==="port-unavailable"?e.jsxs("div",{children:["The port is unavailable. Would you like to stop whatever is running on that port and try again?",e.jsx(O,{port:t.data.port})]}):e.jsx("div",{children:"An unknown error has happened."}):e.jsxs(t.Form,{method:"POST",action:"/start",children:[a,P,e.jsx("input",{type:"hidden",name:"name",value:s}),t.state==="idle"?e.jsx(A,{type:"submit",name:"intent",value:"start",varient:"mono",children:"Start App"}):e.jsx("div",{children:e.jsx(T,{children:"Starting App"})})]})}function se({name:s,port:t,portIsAvailable:a,isRunning:o,baseUrl:n,id:u,initialRoute:h,ref:r}){const l=M(),[i,d]=c.useState(!1);return o||i?e.jsx(ne,{baseUrl:n,id:u,name:s,ref:r,initialRoute:h}):a===!1?e.jsxs("div",{className:"flex flex-col items-center justify-center",children:[e.jsxs("p",{className:"max-w-xs pb-5 text-center",role:"status",children:["The port for this app is unavailable. It could be that you're running it ",e.jsx("a",{href:U({domain:l.domain,port:t}),className:"underline",target:"_blank",rel:"noreferrer",children:"elsewhere"}),". ",e.jsx(W,{onClick:()=>d(!0),children:"Show here anyway"})]}),e.jsx(O,{port:t})]}):e.jsx("div",{className:"flex h-full flex-col items-center justify-center",children:e.jsx(re,{name:s})})}function ne({baseUrl:s,id:t,name:a,initialRoute:o,ref:n}){const u=F(),[h,r]=c.useState(0),l=t+h,i=c.useRef(null),d=new URL(o,s),[m,p]=c.useState(d),g=c.useRef(t);g.current!==t&&(g.current=t,p(d));function b(j){if(j){const y=new URL(j,s);p(y),r(N=>N+1)}}return c.useImperativeHandle(n,()=>({handleExtrnalNavigation:b})),e.jsx(H,{children:e.jsxs("div",{className:"flex h-full grow flex-col",children:[e.jsxs("div",{className:"flex items-center justify-between border-b pl-1.5",children:[e.jsx("div",{className:"mr-2 flex items-center justify-center gap-2 px-1",children:e.jsxs(B,{children:[e.jsx(I,{asChild:!0,children:e.jsx("button",{type:"button",className:"flex aspect-square h-full w-full items-center justify-center p-1 transition disabled:opacity-40",onClick:()=>{r(j=>j+1)},children:e.jsx(v,{name:"Refresh","aria-hidden":"true"})})}),e.jsx(D,{children:"Refresh"})]})}),e.jsx("div",{className:"bg-background text-foreground flex flex-1 items-center border-x p-3 leading-none",children:e.jsx("a",{href:m.toString(),target:"_blank",rel:"noreferrer",children:m.toString()})}),e.jsx(te,{name:a}),e.jsxs(B,{children:[e.jsx(I,{asChild:!0,children:e.jsx("a",{href:m.toString(),target:"_blank",rel:"noreferrer",className:Q("flex aspect-square items-center justify-center px-3.5"),children:e.jsx(v,{name:"ExternalLink"})})}),e.jsx(D,{children:"Open in new tab"})]})]}),e.jsx("div",{className:"bg-background flex h-full w-full grow",children:e.jsx("iframe",{title:a,ref:i,src:m.toString(),className:"bg-background h-full w-full grow",style:{colorScheme:u},allow:"clipboard-write"},l)})]})})}function we({id:s,appInfo:t,inBrowserBrowserRef:a}){const o=M(),[n]=z(),u=F();if(!t)return e.jsx("p",{children:"No app here. Sorry."});const{isRunning:h,dev:r,name:l,portIsAvailable:i,title:d}=t;if(!r)return e.jsx("div",{className:"flex h-full items-center justify-center text-lg",children:e.jsx("p",{children:"No preview available for this app."})});if(ENV.EPICSHOP_DEPLOYED&&t.stackBlitzUrl){const m=new URL(t.stackBlitzUrl);return m.searchParams.set("embed","1"),m.searchParams.set("theme",u),e.jsx(le,{title:d,url:m.toString(),loadingContent:e.jsx(T,{children:e.jsxs("span",{children:["Loading"," ",e.jsxs("a",{className:"underline",href:t.stackBlitzUrl,children:['"',d,'"']})]})})})}if(r.type==="script"){const m=U({domain:o.domain,port:r.portNumber});return e.jsx(se,{ref:a,isRunning:h,id:s??l,name:l,portIsAvailable:i,port:r.portNumber,baseUrl:m,initialRoute:n.get("pathname")??r.initialRoute})}else return r.type==="browser"||r.type==="export"?e.jsxs("div",{className:"scrollbar-thin scrollbar-thumb-scrollbar relative h-full grow overflow-y-auto",children:[e.jsxs("a",{href:r.pathname,target:"_blank",rel:"noreferrer",className:w("bg-muted text-foreground hover:bg-muted/80 absolute right-5 bottom-5 flex items-center justify-center rounded-full p-2.5 transition"),children:[e.jsx(v,{name:"ExternalLink","aria-hidden":"true"}),e.jsx("span",{className:"sr-only",children:"Open in New Window"})]}),e.jsx("iframe",{title:d,src:r.pathname,className:"bg-background h-full w-full grow",style:{colorScheme:u},allow:"clipboard-write"})]}):r.type==="file"?e.jsx(ee,{appName:t.appName??l}):e.jsx("div",{className:"flex h-full items-center justify-center text-lg",children:e.jsxs("p",{children:["Preview for dev type of ",e.jsx("code",{children:r.type})," not supported."]})})}function le({url:s,title:t,loadingContent:a}){const o=F(),[n,u]=c.useState(!1);return e.jsxs("div",{className:"h-full w-full grow",children:[n?null:e.jsx("div",{className:"absolute inset-0 z-10 flex items-center justify-center",children:a}),e.jsx("iframe",{onLoad:()=>u(!0),onError:()=>u(!0),src:s,className:w("h-full w-full grow transition-opacity duration-300",n?"opacity-100":"opacity-0"),title:t,sandbox:"allow-forms allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox",allow:"clipboard-write",style:{colorScheme:o}})]})}export{we as P};
|
|
2
|
+
//# sourceMappingURL=preview-Dav9KrHN.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview-BS9wGiJ3.js","sources":["../../../app/components/file-app-explorer.tsx","../../../app/routes/start.tsx","../../../app/components/in-browser-browser.tsx","../../../app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/__shared/preview.tsx"],"sourcesContent":["import * as React from 'react'\nimport { Mdx } from '#app/utils/mdx.tsx'\nimport { LaunchEditor } from '#app/routes/launch-editor.tsx'\nimport { cn } from '#app/utils/misc.tsx'\nimport { Popover, PopoverContent, PopoverTrigger } from './ui/popover.tsx'\nimport { Icon } from './icons.tsx'\n\ntype PreviewKind = 'text' | 'markdown' | 'image' | 'video' | 'binary'\n\ntype AppFile = {\n\tpath: string\n\tmimeType: string\n\tkind: PreviewKind\n\tlanguage: string | null\n}\n\ntype FileListData = {\n\tfiles: Array<AppFile>\n}\n\ntype FilePreviewData = {\n\tcode: string\n}\n\ntype DirectoryEntry = {\n\tname: string\n\tpath: string\n\tisDirectory: boolean\n}\n\ntype BreadcrumbPart = {\n\tlabel: string\n\tpath: string\n\tparentPath: string\n\tisDirectory: boolean\n}\n\ntype FileIndexes = {\n\tfilesByPath: Map<string, AppFile>\n\tchildrenByDirectory: Map<string, Array<DirectoryEntry>>\n\tfirstFileByDirectory: Map<string, string>\n}\n\nfunction toFileUrl(appName: string, filePath: string) {\n\treturn `/app/${encodeURIComponent(appName)}/${filePath\n\t\t.split('/')\n\t\t.map(encodeURIComponent)\n\t\t.join('/')}`\n}\n\nfunction buildIndexes(files: Array<AppFile>): FileIndexes {\n\tconst filesByPath = new Map<string, AppFile>()\n\tconst childrenByDirectory = new Map<string, Array<DirectoryEntry>>()\n\tconst childKeys = new Map<string, Set<string>>()\n\tconst firstFileByDirectory = new Map<string, string>()\n\n\tfunction addChild(directoryPath: string, entry: DirectoryEntry) {\n\t\tlet keys = childKeys.get(directoryPath)\n\t\tif (!keys) {\n\t\t\tkeys = new Set()\n\t\t\tchildKeys.set(directoryPath, keys)\n\t\t}\n\t\tif (keys.has(entry.path)) return\n\t\tkeys.add(entry.path)\n\n\t\tlet children = childrenByDirectory.get(directoryPath)\n\t\tif (!children) {\n\t\t\tchildren = []\n\t\t\tchildrenByDirectory.set(directoryPath, children)\n\t\t}\n\t\tchildren.push(entry)\n\t}\n\n\tconst sortedFiles = [...files].sort((a, b) => a.path.localeCompare(b.path))\n\tfor (const file of sortedFiles) {\n\t\tfilesByPath.set(file.path, file)\n\t\tfirstFileByDirectory.set('', firstFileByDirectory.get('') ?? file.path)\n\n\t\tconst parts = file.path.split('/').filter(Boolean)\n\t\tfor (const [index, part] of parts.entries()) {\n\t\t\tconst nodePath = parts.slice(0, index + 1).join('/')\n\t\t\tconst parentPath = index === 0 ? '' : parts.slice(0, index).join('/')\n\t\t\tconst isDirectory = index < parts.length - 1\n\t\t\taddChild(parentPath, {\n\t\t\t\tname: part,\n\t\t\t\tpath: nodePath,\n\t\t\t\tisDirectory,\n\t\t\t})\n\t\t\tif (isDirectory && !firstFileByDirectory.has(nodePath)) {\n\t\t\t\tfirstFileByDirectory.set(nodePath, file.path)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const children of childrenByDirectory.values()) {\n\t\tchildren.sort((a, b) => {\n\t\t\tif (a.isDirectory && !b.isDirectory) return -1\n\t\t\tif (!a.isDirectory && b.isDirectory) return 1\n\t\t\treturn a.name.localeCompare(b.name)\n\t\t})\n\t}\n\n\treturn { filesByPath, childrenByDirectory, firstFileByDirectory }\n}\n\nfunction getBreadcrumbParts(pathValue: string | null): Array<BreadcrumbPart> {\n\tconst breadcrumbs: Array<BreadcrumbPart> = []\n\tif (!pathValue) return breadcrumbs\n\n\tconst parts = pathValue.split('/').filter(Boolean)\n\tfor (const [index, part] of parts.entries()) {\n\t\tbreadcrumbs.push({\n\t\t\tlabel: part,\n\t\t\tpath: parts.slice(0, index + 1).join('/'),\n\t\t\tparentPath: index === 0 ? '' : parts.slice(0, index).join('/'),\n\t\t\tisDirectory: index < parts.length - 1,\n\t\t})\n\t}\n\treturn breadcrumbs\n}\n\nfunction getSelectablePath(\n\tentry: DirectoryEntry,\n\tfirstFileByDirectory: Map<string, string>,\n) {\n\tif (!entry.isDirectory) return entry.path\n\treturn firstFileByDirectory.get(entry.path) ?? null\n}\n\nfunction SiblingEntryList({\n\tentries,\n\tselectedPath,\n\tfirstFileByDirectory,\n\tonSelectPath,\n}: {\n\tentries: Array<DirectoryEntry>\n\tselectedPath: string | null\n\tfirstFileByDirectory: Map<string, string>\n\tonSelectPath: (nextPath: string) => void\n}) {\n\tif (entries.length === 0) {\n\t\treturn (\n\t\t\t<p className=\"text-muted-foreground px-3 py-2 text-xs\">\n\t\t\t\tNo files available.\n\t\t\t</p>\n\t\t)\n\t}\n\treturn (\n\t\t<div className=\"max-h-72 overflow-y-auto\">\n\t\t\t{entries.map((entry) => {\n\t\t\t\tconst nextPath = getSelectablePath(entry, firstFileByDirectory)\n\t\t\t\tconst isSelected = !entry.isDirectory && selectedPath === entry.path\n\t\t\t\treturn (\n\t\t\t\t\t<button\n\t\t\t\t\t\tkey={entry.path}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\tif (nextPath) onSelectPath(nextPath)\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t'hover:bg-muted/70 flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-sm',\n\t\t\t\t\t\t\tisSelected ? 'bg-muted' : null,\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Icon\n\t\t\t\t\t\t\tname=\"Files\"\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t'size-3.5 shrink-0',\n\t\t\t\t\t\t\t\tentry.isDirectory\n\t\t\t\t\t\t\t\t\t? 'text-muted-foreground'\n\t\t\t\t\t\t\t\t\t: 'text-foreground/80',\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\taria-hidden\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<span className=\"truncate\">\n\t\t\t\t\t\t\t{entry.name}\n\t\t\t\t\t\t\t{entry.isDirectory ? '/' : ''}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</button>\n\t\t\t\t)\n\t\t\t})}\n\t\t</div>\n\t)\n}\n\nfunction FileContent({\n\tappName,\n\tfile,\n}: {\n\tappName: string\n\tfile: AppFile | null\n}) {\n\tconst [mdxCode, setMdxCode] = React.useState<string | null>(null)\n\tconst [errorMessage, setErrorMessage] = React.useState<string | null>(null)\n\tconst [isLoadingCode, setIsLoadingCode] = React.useState(false)\n\tconst fileUrl = file ? toFileUrl(appName, file.path) : null\n\tconst shouldCompileMdx = file?.kind === 'text' || file?.kind === 'markdown'\n\n\tReact.useEffect(() => {\n\t\tif (!file || !shouldCompileMdx) {\n\t\t\tsetMdxCode(null)\n\t\t\tsetErrorMessage(null)\n\t\t\tsetIsLoadingCode(false)\n\t\t\treturn\n\t\t}\n\t\tconst controller = new AbortController()\n\t\tsetMdxCode(null)\n\t\tsetErrorMessage(null)\n\t\tsetIsLoadingCode(true)\n\n\t\tconst params = new URLSearchParams({\n\t\t\tpath: file.path,\n\t\t\tkind: file.kind,\n\t\t\t...(file.language ? { language: file.language } : {}),\n\t\t})\n\t\tfetch(`/app/${encodeURIComponent(appName)}/file-preview?${params}`, {\n\t\t\tsignal: controller.signal,\n\t\t})\n\t\t\t.then(async (response) => {\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(`Failed to render file (${response.status})`)\n\t\t\t\t}\n\t\t\t\tconst payload = (await response.json()) as FilePreviewData\n\t\t\t\tsetMdxCode(payload.code)\n\t\t\t\tsetIsLoadingCode(false)\n\t\t\t})\n\t\t\t.catch((error: unknown) => {\n\t\t\t\tif (controller.signal.aborted) return\n\t\t\t\tsetErrorMessage(error instanceof Error ? error.message : String(error))\n\t\t\t\tsetIsLoadingCode(false)\n\t\t\t})\n\n\t\treturn () => controller.abort()\n\t}, [appName, file, shouldCompileMdx])\n\n\tif (!file) {\n\t\treturn (\n\t\t\t<div className=\"text-muted-foreground flex h-full items-center justify-center p-6 text-sm\">\n\t\t\t\tChoose a file from the file picker.\n\t\t\t</div>\n\t\t)\n\t}\n\n\tif (errorMessage) {\n\t\treturn (\n\t\t\t<div className=\"text-foreground-destructive p-4 text-sm\">\n\t\t\t\t{errorMessage}\n\t\t\t</div>\n\t\t)\n\t}\n\n\tif (shouldCompileMdx) {\n\t\tif (isLoadingCode || !mdxCode) {\n\t\t\treturn (\n\t\t\t\t<div className=\"text-muted-foreground flex h-full items-center justify-center p-6 text-sm\">\n\t\t\t\t\tRendering file...\n\t\t\t\t</div>\n\t\t\t)\n\t\t}\n\t\tconst wrapperClassName =\n\t\t\tfile.kind === 'text'\n\t\t\t\t? 'max-w-none [&_.group]:w-full [&_pre]:w-full'\n\t\t\t\t: 'prose dark:prose-invert max-w-none'\n\t\treturn (\n\t\t\t<div className=\"h-full overflow-auto p-4\">\n\t\t\t\t<div className={wrapperClassName}>\n\t\t\t\t\t<Mdx code={mdxCode} />\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t)\n\t}\n\n\tif (file.kind === 'image' && fileUrl) {\n\t\treturn (\n\t\t\t<div className=\"flex h-full items-center justify-center p-4\">\n\t\t\t\t<img\n\t\t\t\t\tsrc={fileUrl}\n\t\t\t\t\talt={file.path}\n\t\t\t\t\tclassName=\"border-border max-h-full max-w-full rounded border object-contain\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t)\n\t}\n\n\tif (file.kind === 'video' && fileUrl) {\n\t\treturn (\n\t\t\t<div className=\"flex h-full items-center justify-center p-4\">\n\t\t\t\t<video\n\t\t\t\t\tsrc={fileUrl}\n\t\t\t\t\tcontrols\n\t\t\t\t\tclassName=\"bg-background border-border max-h-full max-w-full rounded border\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t)\n\t}\n\n\treturn (\n\t\t<div className=\"flex h-full flex-col items-center justify-center gap-3 p-6 text-center\">\n\t\t\t<p className=\"text-muted-foreground text-sm\">\n\t\t\t\tPreview is not available for this file type ({file.mimeType}).\n\t\t\t</p>\n\t\t\t{fileUrl ? (\n\t\t\t\t<a\n\t\t\t\t\thref={fileUrl}\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noreferrer\"\n\t\t\t\t\tclassName=\"text-sm underline\"\n\t\t\t\t>\n\t\t\t\t\tOpen file\n\t\t\t\t</a>\n\t\t\t) : null}\n\t\t</div>\n\t)\n}\n\nexport function FileAppExplorer({ appName }: { appName: string }) {\n\tconst [files, setFiles] = React.useState<Array<AppFile>>([])\n\tconst [isLoadingFiles, setIsLoadingFiles] = React.useState(true)\n\tconst [filesError, setFilesError] = React.useState<string | null>(null)\n\tconst [selectedPath, setSelectedPath] = React.useState<string | null>(null)\n\tconst [filePickerOpen, setFilePickerOpen] = React.useState(false)\n\tconst [fileQuery, setFileQuery] = React.useState('')\n\tconst breadcrumbScrollRef = React.useRef<HTMLDivElement | null>(null)\n\n\tReact.useEffect(() => {\n\t\tconst controller = new AbortController()\n\t\tsetFiles([])\n\t\tsetIsLoadingFiles(true)\n\t\tsetFilesError(null)\n\t\tsetSelectedPath(null)\n\t\tfetch(`/app/${encodeURIComponent(appName)}/files`, {\n\t\t\tsignal: controller.signal,\n\t\t})\n\t\t\t.then(async (response) => {\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(`Failed to load files (${response.status})`)\n\t\t\t\t}\n\t\t\t\tconst payload = (await response.json()) as FileListData\n\t\t\t\tsetFiles(payload.files ?? [])\n\t\t\t\tsetIsLoadingFiles(false)\n\t\t\t})\n\t\t\t.catch((error: unknown) => {\n\t\t\t\tif (controller.signal.aborted) return\n\t\t\t\tsetFilesError(error instanceof Error ? error.message : String(error))\n\t\t\t\tsetIsLoadingFiles(false)\n\t\t\t})\n\n\t\treturn () => controller.abort()\n\t}, [appName])\n\n\tconst indexes = React.useMemo(() => buildIndexes(files), [files])\n\tconst selectedFile = selectedPath\n\t\t? (indexes.filesByPath.get(selectedPath) ?? null)\n\t\t: null\n\tconst breadcrumbs = React.useMemo(\n\t\t() => getBreadcrumbParts(selectedPath),\n\t\t[selectedPath],\n\t)\n\tconst filteredFiles = React.useMemo(() => {\n\t\tconst normalizedQuery = fileQuery.trim().toLowerCase()\n\t\tif (!normalizedQuery) return files\n\t\treturn files.filter((file) =>\n\t\t\tfile.path.toLowerCase().includes(normalizedQuery),\n\t\t)\n\t}, [fileQuery, files])\n\n\tReact.useEffect(() => {\n\t\tif (selectedPath && indexes.filesByPath.has(selectedPath)) return\n\t\tsetSelectedPath(files[0]?.path ?? null)\n\t}, [files, indexes.filesByPath, selectedPath])\n\n\tReact.useEffect(() => {\n\t\tconst element = breadcrumbScrollRef.current\n\t\tif (!element) return\n\t\tconst raf = requestAnimationFrame(() => {\n\t\t\telement.scrollLeft = element.scrollWidth\n\t\t})\n\t\treturn () => cancelAnimationFrame(raf)\n\t}, [selectedPath])\n\n\tif (isLoadingFiles) {\n\t\treturn (\n\t\t\t<div className=\"text-muted-foreground flex h-full items-center justify-center text-sm\">\n\t\t\t\tLoading files...\n\t\t\t</div>\n\t\t)\n\t}\n\n\tif (filesError) {\n\t\treturn (\n\t\t\t<div className=\"text-foreground-destructive flex h-full items-center justify-center px-3 text-sm\">\n\t\t\t\t{filesError}\n\t\t\t</div>\n\t\t)\n\t}\n\n\tif (files.length === 0) {\n\t\treturn (\n\t\t\t<div className=\"text-muted-foreground flex h-full items-center justify-center text-sm\">\n\t\t\t\tNo files to display.\n\t\t\t</div>\n\t\t)\n\t}\n\n\treturn (\n\t\t<div className=\"bg-background flex h-full min-h-0 w-full flex-col\">\n\t\t\t<div className=\"border-border flex h-10 shrink-0 items-center gap-2 border-b px-2\">\n\t\t\t\t<Popover open={filePickerOpen} onOpenChange={setFilePickerOpen}>\n\t\t\t\t\t<PopoverTrigger asChild>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\taria-label=\"Open file chooser\"\n\t\t\t\t\t\t\tclassName=\"hover:bg-muted text-foreground inline-flex h-8 w-8 items-center justify-center rounded border border-transparent\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Icon name=\"Files\" className=\"size-4\" />\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</PopoverTrigger>\n\t\t\t\t\t<PopoverContent align=\"start\" className=\"w-[min(420px,80vw)] p-0\">\n\t\t\t\t\t\t<div className=\"border-border border-b p-2\">\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\t\tvalue={fileQuery}\n\t\t\t\t\t\t\t\tonChange={(event) => setFileQuery(event.currentTarget.value)}\n\t\t\t\t\t\t\t\tplaceholder=\"Filter files...\"\n\t\t\t\t\t\t\t\tclassName=\"border-border bg-background w-full rounded border px-2 py-1 text-sm outline-none\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"max-h-80 overflow-y-auto p-1\">\n\t\t\t\t\t\t\t{filteredFiles.length ? (\n\t\t\t\t\t\t\t\tfilteredFiles.map((file) => (\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\tkey={file.path}\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\t\tsetSelectedPath(file.path)\n\t\t\t\t\t\t\t\t\t\t\tsetFilePickerOpen(false)\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t'hover:bg-muted/70 block w-full rounded px-2 py-1.5 text-left font-mono text-xs',\n\t\t\t\t\t\t\t\t\t\t\tselectedPath === file.path ? 'bg-muted' : null,\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{file.path}\n\t\t\t\t\t\t\t\t\t</button>\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<p className=\"text-muted-foreground px-2 py-2 text-xs\">\n\t\t\t\t\t\t\t\t\tNo matching files.\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</PopoverContent>\n\t\t\t\t</Popover>\n\n\t\t\t\t<div\n\t\t\t\t\tref={breadcrumbScrollRef}\n\t\t\t\t\tclassName=\"scrollbar-thin scrollbar-thumb-scrollbar min-w-0 flex-1 overflow-x-auto\"\n\t\t\t\t>\n\t\t\t\t\t<div className=\"flex w-max min-w-full items-center gap-1 pr-2\">\n\t\t\t\t\t\t{breadcrumbs.length > 0 ? (\n\t\t\t\t\t\t\t<span className=\"text-muted-foreground text-xs\">/</span>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{breadcrumbs.map((crumb, index) => {\n\t\t\t\t\t\t\tconst siblings = indexes.childrenByDirectory.get(crumb.parentPath)\n\t\t\t\t\t\t\tconst isCurrent = crumb.path === selectedPath\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<React.Fragment key={`${crumb.path}:${index}`}>\n\t\t\t\t\t\t\t\t\t<Popover>\n\t\t\t\t\t\t\t\t\t\t<PopoverTrigger asChild>\n\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\t\t'hover:bg-muted inline-flex h-7 items-center rounded px-2 font-mono text-xs',\n\t\t\t\t\t\t\t\t\t\t\t\t\tisCurrent\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-muted text-foreground'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'text-muted-foreground',\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{crumb.label}\n\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t</PopoverTrigger>\n\t\t\t\t\t\t\t\t\t\t<PopoverContent align=\"start\" className=\"w-72 p-1\">\n\t\t\t\t\t\t\t\t\t\t\t<SiblingEntryList\n\t\t\t\t\t\t\t\t\t\t\t\tentries={siblings ?? []}\n\t\t\t\t\t\t\t\t\t\t\t\tselectedPath={selectedPath}\n\t\t\t\t\t\t\t\t\t\t\t\tfirstFileByDirectory={indexes.firstFileByDirectory}\n\t\t\t\t\t\t\t\t\t\t\t\tonSelectPath={setSelectedPath}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t</PopoverContent>\n\t\t\t\t\t\t\t\t\t</Popover>\n\t\t\t\t\t\t\t\t\t{index < breadcrumbs.length - 1 ? (\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground text-xs\">/</span>\n\t\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t</React.Fragment>\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t})}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{selectedFile ? (\n\t\t\t\t\t<LaunchEditor\n\t\t\t\t\t\tappName={appName}\n\t\t\t\t\t\tappFile={`${selectedFile.path},1,1`}\n\t\t\t\t\t\tclassName=\"text-muted-foreground hover:text-foreground text-xs\"\n\t\t\t\t\t>\n\t\t\t\t\t\tOpen in editor\n\t\t\t\t\t</LaunchEditor>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t\t<div className=\"min-h-0 flex-1\">\n\t\t\t\t<FileContent appName={appName} file={selectedFile} />\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n","import { type Route } from './+types/start'\nimport { 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, 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 }: Route.ActionArgs) {\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=\"bg-background flex h-full w-full grow\">\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=\"bg-background h-full w-full grow\"\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 { FileAppExplorer } from '#app/components/file-app-explorer.tsx'\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 (!dev) {\n\t\treturn (\n\t\t\t<div className=\"flex h-full items-center justify-center text-lg\">\n\t\t\t\t<p>No preview available for this app.</p>\n\t\t\t</div>\n\t\t)\n\t}\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'bg-muted text-foreground hover:bg-muted/80 absolute right-5 bottom-5 flex items-center justify-center rounded-full p-2.5 transition',\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=\"bg-background h-full w-full grow\"\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 if (dev.type === 'file') {\n\t\treturn <FileAppExplorer appName={appInfo.appName ?? name} />\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":["toFileUrl","appName","filePath","buildIndexes","files","filesByPath","childrenByDirectory","childKeys","firstFileByDirectory","addChild","directoryPath","entry","keys","children","sortedFiles","a","b","file","parts","index","part","nodePath","parentPath","isDirectory","getBreadcrumbParts","pathValue","breadcrumbs","getSelectablePath","SiblingEntryList","entries","selectedPath","onSelectPath","jsx","nextPath","isSelected","jsxs","cn","Icon","FileContent","mdxCode","setMdxCode","React.useState","errorMessage","setErrorMessage","isLoadingCode","setIsLoadingCode","fileUrl","shouldCompileMdx","React.useEffect","controller","params","response","payload","error","wrapperClassName","Mdx","FileAppExplorer","setFiles","isLoadingFiles","setIsLoadingFiles","filesError","setFilesError","setSelectedPath","filePickerOpen","setFilePickerOpen","fileQuery","setFileQuery","breadcrumbScrollRef","React.useRef","indexes","React.useMemo","selectedFile","filteredFiles","normalizedQuery","element","raf","Popover","PopoverTrigger","PopoverContent","event","crumb","siblings","isCurrent","React.Fragment","LaunchEditor","AppStopper","name","fetcher","useFetcher","peRedirectInput","usePERedirectInput","inFlightIntent","formData","get","inFlightState","altDown","useAltDown","Form","method","action","showProgressBarField","type","value","className","PortStopper","port","Button","varient","state","AppStarter","data","status","Loading","InBrowserBrowser","portIsAvailable","isRunning","baseUrl","id","initialRoute","ref","requestInfo","useRequestInfo","showUnmanaged","setShowUnmanaged","useState","InBrowserBrowserForRealz","getBaseUrl","LinkButton","theme","useTheme","iframeKeyNumber","setIframeKeyNumber","iframeKey","iframeRef","useRef","appUrl","iframeSrcUrl","setIframeSrcUrl","currentId","handleExtrnalNavigation","pathname","newUrl","prev","useImperativeHandle","TooltipProvider","Tooltip","TooltipTrigger","TooltipContent","clsx","Preview","appInfo","inBrowserBrowserRef","searchParams","useSearchParams","dev","title","url","StackBlitzEmbed","loadingContent","iframeLoaded","setIframeLoaded"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"preview-Dav9KrHN.js","sources":["../../../app/components/file-app-explorer.tsx","../../../app/routes/start.tsx","../../../app/components/in-browser-browser.tsx","../../../app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/__shared/preview.tsx"],"sourcesContent":["import * as React from 'react'\nimport { Mdx } from '#app/utils/mdx.tsx'\nimport { LaunchEditor } from '#app/routes/launch-editor.tsx'\nimport { cn } from '#app/utils/misc.tsx'\nimport { Popover, PopoverContent, PopoverTrigger } from './ui/popover.tsx'\nimport { Icon } from './icons.tsx'\n\ntype PreviewKind = 'text' | 'markdown' | 'image' | 'video' | 'binary'\n\ntype AppFile = {\n\tpath: string\n\tmimeType: string\n\tkind: PreviewKind\n\tlanguage: string | null\n}\n\ntype FileListData = {\n\tfiles: Array<AppFile>\n}\n\ntype FilePreviewData = {\n\tcode: string\n}\n\ntype DirectoryEntry = {\n\tname: string\n\tpath: string\n\tisDirectory: boolean\n}\n\ntype BreadcrumbPart = {\n\tlabel: string\n\tpath: string\n\tparentPath: string\n\tisDirectory: boolean\n}\n\ntype FileIndexes = {\n\tfilesByPath: Map<string, AppFile>\n\tchildrenByDirectory: Map<string, Array<DirectoryEntry>>\n\tfirstFileByDirectory: Map<string, string>\n}\n\nfunction toFileUrl(appName: string, filePath: string) {\n\treturn `/app/${encodeURIComponent(appName)}/${filePath\n\t\t.split('/')\n\t\t.map(encodeURIComponent)\n\t\t.join('/')}`\n}\n\nfunction buildIndexes(files: Array<AppFile>): FileIndexes {\n\tconst filesByPath = new Map<string, AppFile>()\n\tconst childrenByDirectory = new Map<string, Array<DirectoryEntry>>()\n\tconst childKeys = new Map<string, Set<string>>()\n\tconst firstFileByDirectory = new Map<string, string>()\n\n\tfunction addChild(directoryPath: string, entry: DirectoryEntry) {\n\t\tlet keys = childKeys.get(directoryPath)\n\t\tif (!keys) {\n\t\t\tkeys = new Set()\n\t\t\tchildKeys.set(directoryPath, keys)\n\t\t}\n\t\tif (keys.has(entry.path)) return\n\t\tkeys.add(entry.path)\n\n\t\tlet children = childrenByDirectory.get(directoryPath)\n\t\tif (!children) {\n\t\t\tchildren = []\n\t\t\tchildrenByDirectory.set(directoryPath, children)\n\t\t}\n\t\tchildren.push(entry)\n\t}\n\n\tconst sortedFiles = [...files].sort((a, b) => a.path.localeCompare(b.path))\n\tfor (const file of sortedFiles) {\n\t\tfilesByPath.set(file.path, file)\n\t\tfirstFileByDirectory.set('', firstFileByDirectory.get('') ?? file.path)\n\n\t\tconst parts = file.path.split('/').filter(Boolean)\n\t\tfor (const [index, part] of parts.entries()) {\n\t\t\tconst nodePath = parts.slice(0, index + 1).join('/')\n\t\t\tconst parentPath = index === 0 ? '' : parts.slice(0, index).join('/')\n\t\t\tconst isDirectory = index < parts.length - 1\n\t\t\taddChild(parentPath, {\n\t\t\t\tname: part,\n\t\t\t\tpath: nodePath,\n\t\t\t\tisDirectory,\n\t\t\t})\n\t\t\tif (isDirectory && !firstFileByDirectory.has(nodePath)) {\n\t\t\t\tfirstFileByDirectory.set(nodePath, file.path)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const children of childrenByDirectory.values()) {\n\t\tchildren.sort((a, b) => {\n\t\t\tif (a.isDirectory && !b.isDirectory) return -1\n\t\t\tif (!a.isDirectory && b.isDirectory) return 1\n\t\t\treturn a.name.localeCompare(b.name)\n\t\t})\n\t}\n\n\treturn { filesByPath, childrenByDirectory, firstFileByDirectory }\n}\n\nfunction getBreadcrumbParts(pathValue: string | null): Array<BreadcrumbPart> {\n\tconst breadcrumbs: Array<BreadcrumbPart> = []\n\tif (!pathValue) return breadcrumbs\n\n\tconst parts = pathValue.split('/').filter(Boolean)\n\tfor (const [index, part] of parts.entries()) {\n\t\tbreadcrumbs.push({\n\t\t\tlabel: part,\n\t\t\tpath: parts.slice(0, index + 1).join('/'),\n\t\t\tparentPath: index === 0 ? '' : parts.slice(0, index).join('/'),\n\t\t\tisDirectory: index < parts.length - 1,\n\t\t})\n\t}\n\treturn breadcrumbs\n}\n\nfunction getSelectablePath(\n\tentry: DirectoryEntry,\n\tfirstFileByDirectory: Map<string, string>,\n) {\n\tif (!entry.isDirectory) return entry.path\n\treturn firstFileByDirectory.get(entry.path) ?? null\n}\n\nfunction SiblingEntryList({\n\tentries,\n\tselectedPath,\n\tfirstFileByDirectory,\n\tonSelectPath,\n}: {\n\tentries: Array<DirectoryEntry>\n\tselectedPath: string | null\n\tfirstFileByDirectory: Map<string, string>\n\tonSelectPath: (nextPath: string) => void\n}) {\n\tif (entries.length === 0) {\n\t\treturn (\n\t\t\t<p className=\"text-muted-foreground px-3 py-2 text-xs\">\n\t\t\t\tNo files available.\n\t\t\t</p>\n\t\t)\n\t}\n\treturn (\n\t\t<div className=\"max-h-72 overflow-y-auto\">\n\t\t\t{entries.map((entry) => {\n\t\t\t\tconst nextPath = getSelectablePath(entry, firstFileByDirectory)\n\t\t\t\tconst isSelected = !entry.isDirectory && selectedPath === entry.path\n\t\t\t\treturn (\n\t\t\t\t\t<button\n\t\t\t\t\t\tkey={entry.path}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\tif (nextPath) onSelectPath(nextPath)\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t'hover:bg-muted/70 flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-sm',\n\t\t\t\t\t\t\tisSelected ? 'bg-muted' : null,\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Icon\n\t\t\t\t\t\t\tname=\"Files\"\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t'size-3.5 shrink-0',\n\t\t\t\t\t\t\t\tentry.isDirectory\n\t\t\t\t\t\t\t\t\t? 'text-muted-foreground'\n\t\t\t\t\t\t\t\t\t: 'text-foreground/80',\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\taria-hidden\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<span className=\"truncate\">\n\t\t\t\t\t\t\t{entry.name}\n\t\t\t\t\t\t\t{entry.isDirectory ? '/' : ''}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</button>\n\t\t\t\t)\n\t\t\t})}\n\t\t</div>\n\t)\n}\n\nfunction FileContent({\n\tappName,\n\tfile,\n}: {\n\tappName: string\n\tfile: AppFile | null\n}) {\n\tconst [mdxCode, setMdxCode] = React.useState<string | null>(null)\n\tconst [errorMessage, setErrorMessage] = React.useState<string | null>(null)\n\tconst [isLoadingCode, setIsLoadingCode] = React.useState(false)\n\tconst fileUrl = file ? toFileUrl(appName, file.path) : null\n\tconst shouldCompileMdx = file?.kind === 'text' || file?.kind === 'markdown'\n\n\tReact.useEffect(() => {\n\t\tif (!file || !shouldCompileMdx) {\n\t\t\tsetMdxCode(null)\n\t\t\tsetErrorMessage(null)\n\t\t\tsetIsLoadingCode(false)\n\t\t\treturn\n\t\t}\n\t\tconst controller = new AbortController()\n\t\tsetMdxCode(null)\n\t\tsetErrorMessage(null)\n\t\tsetIsLoadingCode(true)\n\n\t\tconst params = new URLSearchParams({\n\t\t\tpath: file.path,\n\t\t\tkind: file.kind,\n\t\t\t...(file.language ? { language: file.language } : {}),\n\t\t})\n\t\tfetch(`/app/${encodeURIComponent(appName)}/file-preview?${params}`, {\n\t\t\tsignal: controller.signal,\n\t\t})\n\t\t\t.then(async (response) => {\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(`Failed to render file (${response.status})`)\n\t\t\t\t}\n\t\t\t\tconst payload = (await response.json()) as FilePreviewData\n\t\t\t\tsetMdxCode(payload.code)\n\t\t\t\tsetIsLoadingCode(false)\n\t\t\t})\n\t\t\t.catch((error: unknown) => {\n\t\t\t\tif (controller.signal.aborted) return\n\t\t\t\tsetErrorMessage(error instanceof Error ? error.message : String(error))\n\t\t\t\tsetIsLoadingCode(false)\n\t\t\t})\n\n\t\treturn () => controller.abort()\n\t}, [appName, file, shouldCompileMdx])\n\n\tif (!file) {\n\t\treturn (\n\t\t\t<div className=\"text-muted-foreground flex h-full items-center justify-center p-6 text-sm\">\n\t\t\t\tChoose a file from the file picker.\n\t\t\t</div>\n\t\t)\n\t}\n\n\tif (errorMessage) {\n\t\treturn (\n\t\t\t<div className=\"text-foreground-destructive p-4 text-sm\">\n\t\t\t\t{errorMessage}\n\t\t\t</div>\n\t\t)\n\t}\n\n\tif (shouldCompileMdx) {\n\t\tif (isLoadingCode || !mdxCode) {\n\t\t\treturn (\n\t\t\t\t<div className=\"text-muted-foreground flex h-full items-center justify-center p-6 text-sm\">\n\t\t\t\t\tRendering file...\n\t\t\t\t</div>\n\t\t\t)\n\t\t}\n\t\tconst wrapperClassName =\n\t\t\tfile.kind === 'text'\n\t\t\t\t? 'max-w-none [&_.group]:w-full [&_pre]:w-full'\n\t\t\t\t: 'prose dark:prose-invert max-w-none'\n\t\treturn (\n\t\t\t<div className=\"h-full overflow-auto p-4\">\n\t\t\t\t<div className={wrapperClassName}>\n\t\t\t\t\t<Mdx code={mdxCode} />\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t)\n\t}\n\n\tif (file.kind === 'image' && fileUrl) {\n\t\treturn (\n\t\t\t<div className=\"flex h-full items-center justify-center p-4\">\n\t\t\t\t<img\n\t\t\t\t\tsrc={fileUrl}\n\t\t\t\t\talt={file.path}\n\t\t\t\t\tclassName=\"border-border max-h-full max-w-full rounded border object-contain\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t)\n\t}\n\n\tif (file.kind === 'video' && fileUrl) {\n\t\treturn (\n\t\t\t<div className=\"flex h-full items-center justify-center p-4\">\n\t\t\t\t<video\n\t\t\t\t\tsrc={fileUrl}\n\t\t\t\t\tcontrols\n\t\t\t\t\tclassName=\"bg-background border-border max-h-full max-w-full rounded border\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t)\n\t}\n\n\treturn (\n\t\t<div className=\"flex h-full flex-col items-center justify-center gap-3 p-6 text-center\">\n\t\t\t<p className=\"text-muted-foreground text-sm\">\n\t\t\t\tPreview is not available for this file type ({file.mimeType}).\n\t\t\t</p>\n\t\t\t{fileUrl ? (\n\t\t\t\t<a\n\t\t\t\t\thref={fileUrl}\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noreferrer\"\n\t\t\t\t\tclassName=\"text-sm underline\"\n\t\t\t\t>\n\t\t\t\t\tOpen file\n\t\t\t\t</a>\n\t\t\t) : null}\n\t\t</div>\n\t)\n}\n\nexport function FileAppExplorer({ appName }: { appName: string }) {\n\tconst [files, setFiles] = React.useState<Array<AppFile>>([])\n\tconst [isLoadingFiles, setIsLoadingFiles] = React.useState(true)\n\tconst [filesError, setFilesError] = React.useState<string | null>(null)\n\tconst [selectedPath, setSelectedPath] = React.useState<string | null>(null)\n\tconst [filePickerOpen, setFilePickerOpen] = React.useState(false)\n\tconst [fileQuery, setFileQuery] = React.useState('')\n\tconst breadcrumbScrollRef = React.useRef<HTMLDivElement | null>(null)\n\n\tReact.useEffect(() => {\n\t\tconst controller = new AbortController()\n\t\tsetFiles([])\n\t\tsetIsLoadingFiles(true)\n\t\tsetFilesError(null)\n\t\tsetSelectedPath(null)\n\t\tfetch(`/app/${encodeURIComponent(appName)}/files`, {\n\t\t\tsignal: controller.signal,\n\t\t})\n\t\t\t.then(async (response) => {\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(`Failed to load files (${response.status})`)\n\t\t\t\t}\n\t\t\t\tconst payload = (await response.json()) as FileListData\n\t\t\t\tsetFiles(payload.files ?? [])\n\t\t\t\tsetIsLoadingFiles(false)\n\t\t\t})\n\t\t\t.catch((error: unknown) => {\n\t\t\t\tif (controller.signal.aborted) return\n\t\t\t\tsetFilesError(error instanceof Error ? error.message : String(error))\n\t\t\t\tsetIsLoadingFiles(false)\n\t\t\t})\n\n\t\treturn () => controller.abort()\n\t}, [appName])\n\n\tconst indexes = React.useMemo(() => buildIndexes(files), [files])\n\tconst selectedFile = selectedPath\n\t\t? (indexes.filesByPath.get(selectedPath) ?? null)\n\t\t: null\n\tconst breadcrumbs = React.useMemo(\n\t\t() => getBreadcrumbParts(selectedPath),\n\t\t[selectedPath],\n\t)\n\tconst filteredFiles = React.useMemo(() => {\n\t\tconst normalizedQuery = fileQuery.trim().toLowerCase()\n\t\tif (!normalizedQuery) return files\n\t\treturn files.filter((file) =>\n\t\t\tfile.path.toLowerCase().includes(normalizedQuery),\n\t\t)\n\t}, [fileQuery, files])\n\n\tReact.useEffect(() => {\n\t\tif (selectedPath && indexes.filesByPath.has(selectedPath)) return\n\t\tsetSelectedPath(files[0]?.path ?? null)\n\t}, [files, indexes.filesByPath, selectedPath])\n\n\tReact.useEffect(() => {\n\t\tconst element = breadcrumbScrollRef.current\n\t\tif (!element) return\n\t\tconst raf = requestAnimationFrame(() => {\n\t\t\telement.scrollLeft = element.scrollWidth\n\t\t})\n\t\treturn () => cancelAnimationFrame(raf)\n\t}, [selectedPath])\n\n\tif (isLoadingFiles) {\n\t\treturn (\n\t\t\t<div className=\"text-muted-foreground flex h-full items-center justify-center text-sm\">\n\t\t\t\tLoading files...\n\t\t\t</div>\n\t\t)\n\t}\n\n\tif (filesError) {\n\t\treturn (\n\t\t\t<div className=\"text-foreground-destructive flex h-full items-center justify-center px-3 text-sm\">\n\t\t\t\t{filesError}\n\t\t\t</div>\n\t\t)\n\t}\n\n\tif (files.length === 0) {\n\t\treturn (\n\t\t\t<div className=\"text-muted-foreground flex h-full items-center justify-center text-sm\">\n\t\t\t\tNo files to display.\n\t\t\t</div>\n\t\t)\n\t}\n\n\treturn (\n\t\t<div className=\"bg-background flex h-full min-h-0 w-full flex-col\">\n\t\t\t<div className=\"border-border flex h-10 shrink-0 items-center gap-2 border-b px-2\">\n\t\t\t\t<Popover open={filePickerOpen} onOpenChange={setFilePickerOpen}>\n\t\t\t\t\t<PopoverTrigger asChild>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\taria-label=\"Open file chooser\"\n\t\t\t\t\t\t\tclassName=\"hover:bg-muted text-foreground inline-flex h-8 w-8 items-center justify-center rounded border border-transparent\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Icon name=\"Files\" className=\"size-4\" />\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</PopoverTrigger>\n\t\t\t\t\t<PopoverContent align=\"start\" className=\"w-[min(420px,80vw)] p-0\">\n\t\t\t\t\t\t<div className=\"border-border border-b p-2\">\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\t\tvalue={fileQuery}\n\t\t\t\t\t\t\t\tonChange={(event) => setFileQuery(event.currentTarget.value)}\n\t\t\t\t\t\t\t\tplaceholder=\"Filter files...\"\n\t\t\t\t\t\t\t\tclassName=\"border-border bg-background w-full rounded border px-2 py-1 text-sm outline-none\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"max-h-80 overflow-y-auto p-1\">\n\t\t\t\t\t\t\t{filteredFiles.length ? (\n\t\t\t\t\t\t\t\tfilteredFiles.map((file) => (\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\tkey={file.path}\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\t\tsetSelectedPath(file.path)\n\t\t\t\t\t\t\t\t\t\t\tsetFilePickerOpen(false)\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t'hover:bg-muted/70 block w-full rounded px-2 py-1.5 text-left font-mono text-xs',\n\t\t\t\t\t\t\t\t\t\t\tselectedPath === file.path ? 'bg-muted' : null,\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{file.path}\n\t\t\t\t\t\t\t\t\t</button>\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<p className=\"text-muted-foreground px-2 py-2 text-xs\">\n\t\t\t\t\t\t\t\t\tNo matching files.\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</PopoverContent>\n\t\t\t\t</Popover>\n\n\t\t\t\t<div\n\t\t\t\t\tref={breadcrumbScrollRef}\n\t\t\t\t\tclassName=\"scrollbar-thin scrollbar-thumb-scrollbar min-w-0 flex-1 overflow-x-auto\"\n\t\t\t\t>\n\t\t\t\t\t<div className=\"flex w-max min-w-full items-center gap-1 pr-2\">\n\t\t\t\t\t\t{breadcrumbs.length > 0 ? (\n\t\t\t\t\t\t\t<span className=\"text-muted-foreground text-xs\">/</span>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{breadcrumbs.map((crumb, index) => {\n\t\t\t\t\t\t\tconst siblings = indexes.childrenByDirectory.get(crumb.parentPath)\n\t\t\t\t\t\t\tconst isCurrent = crumb.path === selectedPath\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<React.Fragment key={`${crumb.path}:${index}`}>\n\t\t\t\t\t\t\t\t\t<Popover>\n\t\t\t\t\t\t\t\t\t\t<PopoverTrigger asChild>\n\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\t\t'hover:bg-muted inline-flex h-7 items-center rounded px-2 font-mono text-xs',\n\t\t\t\t\t\t\t\t\t\t\t\t\tisCurrent\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-muted text-foreground'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'text-muted-foreground',\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{crumb.label}\n\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t</PopoverTrigger>\n\t\t\t\t\t\t\t\t\t\t<PopoverContent align=\"start\" className=\"w-72 p-1\">\n\t\t\t\t\t\t\t\t\t\t\t<SiblingEntryList\n\t\t\t\t\t\t\t\t\t\t\t\tentries={siblings ?? []}\n\t\t\t\t\t\t\t\t\t\t\t\tselectedPath={selectedPath}\n\t\t\t\t\t\t\t\t\t\t\t\tfirstFileByDirectory={indexes.firstFileByDirectory}\n\t\t\t\t\t\t\t\t\t\t\t\tonSelectPath={setSelectedPath}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t</PopoverContent>\n\t\t\t\t\t\t\t\t\t</Popover>\n\t\t\t\t\t\t\t\t\t{index < breadcrumbs.length - 1 ? (\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground text-xs\">/</span>\n\t\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t</React.Fragment>\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t})}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{selectedFile ? (\n\t\t\t\t\t<LaunchEditor\n\t\t\t\t\t\tappName={appName}\n\t\t\t\t\t\tappFile={`${selectedFile.path},1,1`}\n\t\t\t\t\t\tclassName=\"text-muted-foreground hover:text-foreground text-xs\"\n\t\t\t\t\t>\n\t\t\t\t\t\tOpen in editor\n\t\t\t\t\t</LaunchEditor>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t\t<div className=\"min-h-0 flex-1\">\n\t\t\t\t<FileContent appName={appName} file={selectedFile} />\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n","import { type Route } from './+types/start'\nimport { 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, 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 }: Route.ActionArgs) {\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=\"bg-background flex h-full w-full grow\">\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=\"bg-background h-full w-full grow\"\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 { FileAppExplorer } from '#app/components/file-app-explorer.tsx'\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 (!dev) {\n\t\treturn (\n\t\t\t<div className=\"flex h-full items-center justify-center text-lg\">\n\t\t\t\t<p>No preview available for this app.</p>\n\t\t\t</div>\n\t\t)\n\t}\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'bg-muted text-foreground hover:bg-muted/80 absolute right-5 bottom-5 flex items-center justify-center rounded-full p-2.5 transition',\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=\"bg-background h-full w-full grow\"\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 if (dev.type === 'file') {\n\t\treturn <FileAppExplorer appName={appInfo.appName ?? name} />\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":["toFileUrl","appName","filePath","buildIndexes","files","filesByPath","childrenByDirectory","childKeys","firstFileByDirectory","addChild","directoryPath","entry","keys","children","sortedFiles","a","b","file","parts","index","part","nodePath","parentPath","isDirectory","getBreadcrumbParts","pathValue","breadcrumbs","getSelectablePath","SiblingEntryList","entries","selectedPath","onSelectPath","jsx","nextPath","isSelected","jsxs","cn","Icon","FileContent","mdxCode","setMdxCode","React.useState","errorMessage","setErrorMessage","isLoadingCode","setIsLoadingCode","fileUrl","shouldCompileMdx","React.useEffect","controller","params","response","payload","error","wrapperClassName","Mdx","FileAppExplorer","setFiles","isLoadingFiles","setIsLoadingFiles","filesError","setFilesError","setSelectedPath","filePickerOpen","setFilePickerOpen","fileQuery","setFileQuery","breadcrumbScrollRef","React.useRef","indexes","React.useMemo","selectedFile","filteredFiles","normalizedQuery","element","raf","Popover","PopoverTrigger","PopoverContent","event","crumb","siblings","isCurrent","React.Fragment","LaunchEditor","AppStopper","name","fetcher","useFetcher","peRedirectInput","usePERedirectInput","inFlightIntent","formData","get","inFlightState","altDown","useAltDown","Form","method","action","showProgressBarField","type","value","className","PortStopper","port","Button","varient","state","AppStarter","data","status","Loading","InBrowserBrowser","portIsAvailable","isRunning","baseUrl","id","initialRoute","ref","requestInfo","useRequestInfo","showUnmanaged","setShowUnmanaged","useState","InBrowserBrowserForRealz","getBaseUrl","LinkButton","theme","useTheme","iframeKeyNumber","setIframeKeyNumber","iframeKey","iframeRef","useRef","appUrl","iframeSrcUrl","setIframeSrcUrl","currentId","handleExtrnalNavigation","pathname","newUrl","prev","useImperativeHandle","TooltipProvider","Tooltip","TooltipTrigger","TooltipContent","clsx","Preview","appInfo","inBrowserBrowserRef","searchParams","useSearchParams","dev","title","url","StackBlitzEmbed","loadingContent","iframeLoaded","setIframeLoaded"],"mappings":"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"}
|