5import Data.Array (foldMap, intercalate)
6import Data.Array as Array
7import Data.Array.NonEmpty as NEA
8import Data.Either (Either(..))
9import Data.Function.Uncurried (mkFn2)
10import Data.Maybe (Maybe(..), fromMaybe, isJust)
11import Data.Monoid (guard)
12import Data.Nullable (Nullable)
13import Data.Nullable as Nullable
14import Data.String as String
15import Data.Traversable (mapAccumL)
16import Data.Tuple.Nested ((/\))
17import Debug.Trace (spy)
19import Effect.Aff (launchAff_)
20import Effect.Class (liftEffect)
21import JSS (jssClasses)
22import Justifill (justifill)
23import Milkis.Impl (FetchImpl)
24import React.Basic (JSX, ReactComponent, fragment)
25import React.Basic.DOM (unsafeCreateDOMComponent)
26import React.Basic.DOM as R
27import React.Basic.Helpers (jsx)
28import React.Basic.Hooks (ReactChildren, component, componentWithChildren, element, memo, reactChildrenToArray, useReducer, useState)
29import React.Basic.Hooks as React
30import Shared.Models.Body (CompileResult)
31import Unsafe.Coerce (unsafeCoerce)
32import Yoga.Box.Component as Box
33import Yoga.ClickAway.Component as ClickAway
34import Yoga.CloseIcon.Component as CloseIcon
35import Yoga.CompileEditor.Component (mkCompileEditor)
36import Yoga.Compiler.Api (apiCompiler)
37import Yoga.Compiler.Types (Compiler)
38import Yoga.FillInTheGaps.Component as FillInTheGaps
39import Yoga.FillInTheGaps.Logic (findResult, parseSegments, toCode)
40import Yoga.Header.Component (mkHeader)
41import Yoga.Helpers ((?||))
42import Yoga.InlineCode.Component as InlineCode
43import Yoga.Modal.Component as Modal
44import Yoga.Theme (fromTheme)
45import Yoga.Theme.CSSBaseline (mkCssBaseline)
46import Yoga.Theme.Default (darkTheme)
47import Yoga.Theme.Provider (mkThemeProvider)
48import Yoga.Theme.Styles (makeStylesJSS)
49import Yoga.Theme.Types (CSSTheme)
50import Yoga.Typography.Header (HeadingLevel(..), mkH)
51import Yoga.Typography.Paragraph (mkP)
52import Yoga.WithSidebar.Component (makeComponent) as WithSidebar
58 , menuLinks ∷ Array { name ∷ String, link ∷ String }
68 { mdxType ∷ Nullable String
69 , children ∷ Nullable String
70 , className ∷ Nullable String
76 = ShowModal Modal.Props
84 { children ∷ ReactChildren JSX
85 , siteInfo ∷ SiteQueryResult
88mkLayout fetchImpl = do
89 themeProvider <- mkThemeProvider
90 modal <- memo Modal.makeComponent
91 clickAway <- memo ClickAway.makeComponent
92 mdxProviderComponent <- memo $ mkMdxProviderComponent (apiCompiler fetchImpl)
93 componentWithChildren "MDXLayout" \{ children, siteInfo } -> React.do
94 maybeModalProps /\ dispatch <-
95 useReducer Nothing case _, _ of
96 _, ShowModal props -> Just props
97 _, HideModal -> Nothing
99 $ element themeProvider
100 { theme: fromTheme darkTheme
104 # foldMap \modalProps ->
106 [ element clickAway { allowEscape: Just true, onClick: dispatch HideModal, style: Nothing }
107 , element modal (justifill modalProps)
109 , element mdxProviderComponent
112 , showModal: dispatch <<< ShowModal
113 , hideModal: dispatch HideModal
119isQuiz ∷ JSX -> Boolean
121 (unsafeCoerce a).props.mdxType == "pre"
122 && ((unsafeCoerce a).props.children.props.className == "language-purescript")
123 && ((unsafeCoerce a).props.children.props.mdxType == "code")
124 && ((unsafeCoerce a).props.children.props.children # parseSegments # isJust)
126mkMdxProviderComponent ∷
131 { children ∷ ReactChildren JSX
132 , siteInfo ∷ SiteQueryResult
133 , showModal ∷ Modal.Props -> Effect Unit
134 , hideModal :: Effect Unit
137mkMdxProviderComponent compiler = do
138 cssBaseline <- memo mkCssBaseline
139 editor <- memo $ mkCompileEditor compiler
140 box <- memo $ Box.makeComponent
141 sidebar <- memo mkSidebar
142 header <- memo mkHeader
143 yogaInlineCode <- InlineCode.makeComponent
144 quiz <- memo $ mkQuiz compiler
147 closeIcon <- CloseIcon.makeComponent
150 $ jssClasses \(theme ∷ CSSTheme) ->
152 { fontFamily: theme.codeFontFamily # NEA.head
153 , backgroundColor: theme.interfaceColour
155 , border: "1px solid #383c39"
157 , borderRadius: "3px"
160 componentWithChildren "MDXProviderComponent" \{ children, siteInfo, showModal, hideModal } -> React.do
161 classes <- useStyles {}
162 visibleUntil /\ updateVisible <- useState 1
164 onFailure title kids =
168 , icon: element closeIcon { onClick: hideModal, style: Nothing }
173 onSuccess = updateVisible (_ + one)
176 { accum: i + if isQuiz kid then one else zero
177 , value: guard (i < visibleUntil) (pure kid)
180 visibleKids ∷ Array JSX
182 reactChildrenToArray children
183 # mapAccumL mapVisible zero
190 [ jsx header { className: "" } [ R.text siteInfo.site.siteMetadata.title ]
191 , element sidebar { links: siteInfo.site.siteMetadata.menuLinks }
192 , jsx box {} visibleKids
195 mdxComponents /\ _ <-
199 element h ({ level: H2, text: props.children, className: Nothing })
202 element h { level: H3, text: props.children, className: Nothing }
205 element h { level: H4, text: props.children, className: Nothing }
206 , hr: (const $ R.hr {})
210 { children: [ element p { text: props.children } ]
214 R.span { className: classes.code, children: props.children }
216 mkFn2 \(props ∷ PreProps) other -> do
218 _ = spy "other" other
220 childrenQ = Nullable.toMaybe props.children
222 propsQ = (_.props >>> Nullable.toMaybe) =<< childrenQ
224 mdxTypeQ = (_.mdxType >>> Nullable.toMaybe) =<< propsQ
226 childrenQ2 = (_.children >>> Nullable.toMaybe) =<< propsQ
228 classNameQ = (_.className >>> Nullable.toMaybe) =<< propsQ
230 isCode = fromMaybe false (mdxTypeQ <#> eq "code")
235 (fromMaybe 200 >>> show >>> (_ <> "px")) do
238 ( String.split (String.Pattern "\n") code
240 # \x -> (x * 12) + 100
243 language = fromMaybe "" (classNameQ >>= String.stripPrefix (String.Pattern "language-"))
245 segmentsQ = parseSegments (codeQ ?|| "")
246 case isCode, language, segmentsQ of
247 true, "purescript", Just initialSegments -> element quiz { initialSegments, onFailure, onSuccess }
250 { initialCode: fromMaybe "" codeQ
254 false, _, _ -> element (unsafeCreateDOMComponent "pre") props
257 $ element mdxProvider
258 { children: pure $ jsx cssBaseline {} [ siteInfoJSX ]
259 , components: mdxComponents
262mkQuiz ∷ _ -> Effect (ReactComponent _)
264 fillInTheGaps <- FillInTheGaps.makeComponent
265 box <- Box.makeComponent
266 component "Quiz" \({ initialSegments, onFailure, onSuccess } ∷ { initialSegments ∷ _, onFailure ∷ _, onSuccess ∷ _ }) -> React.do
267 segments /\ updateSegments <- useState initialSegments
268 solvedWith /\ updateSolvedWith <- useState Nothing
271 [ element fillInTheGaps
276 code = toCode segments
277 result <- compiler.compileAndRun { code }
278 liftEffect case result of
280 | String.stripSuffix (String.Pattern "\n") r.stdout == (findResult $ join segments) -> do
281 updateSolvedWith (const $ String.stripSuffix (String.Pattern "\n") r.stdout)
283 Right r | r.stdout /= "\n" -> onFailure "Oh shit!" [R.text r.stdout]
284 Right r -> onFailure "Oh shit!" [R.text r.stderr]
285 Left (cr :: CompileResult) -> onFailure "Oh shit!" [R.text (intercalate ", " (cr.result <#> _.message))]
289 updated = update segments
290 case segments, updated of
292 | new == old -> mempty
293 _, _ -> updateSegments (const updated)
299 withSidebar <- WithSidebar.makeComponent
302 $ jssClasses \(theme ∷ CSSTheme) ->
304 component "Sidebar" \{ links } -> React.do
305 classes <- useStyles {}
307 $ element withSidebar
309 { sidebarChildren: [ R.div_ [] ]
310 , notSidebarChildren: [] ∷ Array JSX
314foreign import mdxProvider ∷
317 { components ∷ { | r } -- TODO Don't lie
318 , children ∷ Array JSX
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list