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
219 childrenQ = Nullable.toMaybe props.children
221 propsQ = (_.props >>> Nullable.toMaybe) =<< childrenQ
223 mdxTypeQ = (_.mdxType >>> Nullable.toMaybe) =<< propsQ
225 childrenQ2 = (_.children >>> Nullable.toMaybe) =<< propsQ
227 classNameQ = (_.className >>> Nullable.toMaybe) =<< propsQ
229 isCode = fromMaybe false (mdxTypeQ <#> eq "code")
234 (fromMaybe 200 >>> show >>> (_ <> "px")) do
237 ( String.split (String.Pattern "\n") code
239 # \x -> (x * 12) + 100
242 language = fromMaybe "" (classNameQ >>= String.stripPrefix (String.Pattern "language-"))
244 segmentsQ = parseSegments (codeQ ?|| "")
245 case isCode, language, segmentsQ of
246 true, "purescript", Just initialSegments -> element quiz { initialSegments, onFailure, onSuccess }
249 { initialCode: fromMaybe "" codeQ
253 false, _, _ -> element (unsafeCreateDOMComponent "pre") props
256 $ element mdxProvider
257 { children: pure $ jsx cssBaseline {} [ siteInfoJSX ]
258 , components: mdxComponents
261mkQuiz ∷ _ -> Effect (ReactComponent _)
263 fillInTheGaps <- FillInTheGaps.makeComponent
264 box <- Box.makeComponent
265 component "Quiz" \({ initialSegments, onFailure, onSuccess } ∷ { initialSegments ∷ _, onFailure ∷ _, onSuccess ∷ _ }) -> React.do
266 segments /\ updateSegments <- useState initialSegments
267 solvedWith /\ updateSolvedWith <- useState Nothing
270 [ element fillInTheGaps
275 code = toCode segments
276 result <- compiler.compileAndRun { code }
277 liftEffect case result of
279 | String.stripSuffix (String.Pattern "\n") r.stdout == (findResult $ join segments) -> do
280 updateSolvedWith (const $ String.stripSuffix (String.Pattern "\n") r.stdout)
282 Right r | r.stdout /= "\n" -> onFailure "Oh shit!" [R.text r.stdout]
283 Right r -> onFailure "Oh shit!" [R.text r.stderr]
284 Left (cr :: CompileResult) -> onFailure "Oh shit!" [R.text (intercalate ", " (cr.result <#> _.message))]
288 updated = update segments
289 case segments, updated of
291 | new == old -> mempty
292 _, _ -> updateSegments (const updated)
298 withSidebar <- WithSidebar.makeComponent
301 $ jssClasses \(theme ∷ CSSTheme) ->
303 component "Sidebar" \{ links } -> React.do
304 classes <- useStyles {}
306 $ element withSidebar
308 { sidebarChildren: [ R.div_ [] ]
309 , notSidebarChildren: [] ∷ Array JSX
313foreign import mdxProvider ∷
316 { components ∷ { | r } -- TODO Don't lie
317 , 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