5import Control.Monad.State (evalStateT, get, put, runStateT)
6import Control.Monad.State as State
7import Control.Monad.Trans.Class (lift)
8import Data.Array (foldMap)
9import Data.Array as Array
10import Data.Array.NonEmpty as NEA
11import Data.Either (Either(..))
12import Data.Foldable (fold)
13import Data.Function.Uncurried (mkFn2)
14import Data.Maybe (Maybe(..), fromMaybe, isJust)
15import Data.Monoid (guard)
16import Data.Monoid.Additive (Additive(..))
17import Data.Newtype (ala)
18import Data.Newtype as NT
19import Data.Nullable (Nullable)
20import Data.Nullable as Nullable
21import Data.String as String
22import Data.Traversable (Accum, mapAccumL)
23import Data.Tuple.Nested ((/\))
24import Debug.Trace (spy)
26import Effect.Aff (launchAff_)
27import Effect.Class (liftEffect)
28import Effect.Class.Console (log)
29import JSS (jss, jssClasses)
30import Justifill (justifill)
31import Milkis.Impl (FetchImpl)
32import React.Basic (JSX, ReactComponent, fragment)
33import React.Basic.DOM (unsafeCreateDOMComponent)
34import React.Basic.DOM as R
35import React.Basic.Helpers (jsx)
36import React.Basic.Hooks (ReactChildren, component, componentWithChildren, element, memo, reactChildrenToArray, readRef, useEffect, useReducer, useRef, useState, writeRef)
37import React.Basic.Hooks as React
38import Unsafe.Coerce (unsafeCoerce)
39import Yoga.Box.Component as Box
40import Yoga.ClickAway.Component as ClickAway
41import Yoga.CompileEditor.Component (mkCompileEditor)
42import Yoga.Compiler.Api (apiCompiler)
43import Yoga.Compiler.Types (Compiler)
44import Yoga.FillInTheGaps.Component as FillInTheGaps
45import Yoga.FillInTheGaps.Logic (findResult, parseSegments, toCode)
46import Yoga.Header.Component (mkHeader)
47import Yoga.Helpers ((?||))
48import Yoga.InlineCode.Component as InlineCode
49import Yoga.Modal.Component as Modal
50import Yoga.Theme (fromTheme)
51import Yoga.Theme.CSSBaseline (mkCssBaseline)
52import Yoga.Theme.Default (darkTheme)
53import Yoga.Theme.Provider (mkThemeProvider)
54import Yoga.Theme.Styles (makeStylesJSS)
55import Yoga.Theme.Types (CSSTheme)
56import Yoga.Typography.Header (HeadingLevel(..), mkH)
57import Yoga.Typography.Paragraph (mkP)
58import Yoga.WithSidebar.Component (makeComponent) as WithSidebar
64 , menuLinks ∷ Array { name ∷ String, link ∷ String }
74 { mdxType ∷ Nullable String
75 , children ∷ Nullable String
76 , className ∷ Nullable String
82 = ShowModal Modal.Props
90 { children ∷ ReactChildren JSX
91 , siteInfo ∷ SiteQueryResult
94mkLayout fetchImpl = do
95 themeProvider <- mkThemeProvider
96 modal <- memo Modal.makeComponent
97 clickAway <- memo ClickAway.makeComponent
98 mdxProviderComponent <- memo $ mkMdxProviderComponent (apiCompiler fetchImpl)
99 componentWithChildren "MDXLayout" \{ children, siteInfo } -> React.do
100 maybeModalProps /\ dispatch <-
101 useReducer Nothing case _, _ of
102 _, ShowModal props -> Just props
103 _, HideModal -> Nothing
105 $ element themeProvider
106 { theme: fromTheme darkTheme
110 # foldMap \modalProps ->
112 [ element clickAway { allowEscape: Just true, onClick: dispatch HideModal, style: Nothing }
113 , element modal (justifill modalProps)
115 , element mdxProviderComponent
116 { children: spy "children" children
118 , showModal: dispatch <<< ShowModal
124isQuiz ∷ JSX -> Boolean
126 (unsafeCoerce a).props.mdxType == "pre"
127 && ((unsafeCoerce a).props.children.props.className == "language-purescript")
128 && ((unsafeCoerce a).props.children.props.mdxType == "code")
129 && ((unsafeCoerce a).props.children.props.children # parseSegments # isJust)
131mkMdxProviderComponent ∷
136 { children ∷ ReactChildren JSX
137 , siteInfo ∷ SiteQueryResult
138 , showModal ∷ Modal.Props -> Effect Unit
141mkMdxProviderComponent compiler = do
142 cssBaseline <- memo mkCssBaseline
143 editor <- memo $ mkCompileEditor compiler
144 box <- memo $ Box.makeComponent
145 sidebar <- memo mkSidebar
146 header <- memo mkHeader
147 yogaInlineCode <- InlineCode.makeComponent
148 quiz <- memo $ mkQuiz compiler
153 $ jssClasses \(theme ∷ CSSTheme) ->
155 { fontFamily: theme.codeFontFamily # NEA.head
156 , backgroundColor: theme.interfaceColour
158 , border: "1px solid #383c39"
160 , borderRadius: "3px"
163 componentWithChildren "MDXProviderComponent" \{ children, siteInfo, showModal } -> React.do
164 classes <- useStyles {}
165 visibleUntil /\ updateVisible <- useState 1
167 onFailure = showModal (justifill { title: "Failed", kids: [ R.text "Try again" ] } ∷ Modal.Props)
168 onSuccess = updateVisible (_ + one)
171 { accum: i + if isQuiz kid then one else zero
172 , value: guard (i < visibleUntil) (pure kid)
174 visibleKids :: Array JSX
175 visibleKids = reactChildrenToArray children
176 # mapAccumL mapVisible zero
183 [ jsx header { className: "" } [ R.text siteInfo.site.siteMetadata.title ]
184 , element sidebar { links: siteInfo.site.siteMetadata.menuLinks }
185 , jsx box {} visibleKids
188 mdxComponents /\ _ <-
192 element h ({ level: H2, text: props.children, className: Nothing })
195 element h { level: H3, text: props.children, className: Nothing }
198 element h { level: H4, text: props.children, className: Nothing }
199 , hr: (const $ R.hr {})
203 { children: [ element p { text: props.children } ]
207 R.span { className: classes.code, children: props.children }
209 \(props ∷ PreProps) other -> do
211 _ = spy "other" other
212 childrenQ = Nullable.toMaybe props.children
214 propsQ = (_.props >>> Nullable.toMaybe) =<< childrenQ
216 mdxTypeQ = (_.mdxType >>> Nullable.toMaybe) =<< propsQ
218 childrenQ2 = (_.children >>> Nullable.toMaybe) =<< propsQ
220 classNameQ = (_.className >>> Nullable.toMaybe) =<< propsQ
222 isCode = fromMaybe false (mdxTypeQ <#> eq "code")
227 (fromMaybe 200 >>> show >>> (_ <> "px")) do
230 ( String.split (String.Pattern "\n") code
232 # \x -> (x * 12) + 100
235 language = fromMaybe "" (classNameQ >>= String.stripPrefix (String.Pattern "language-"))
237 segmentsQ = parseSegments (codeQ ?|| "")
238 case isCode, language, segmentsQ of
239 true, "purescript", Just initialSegments -> element quiz { initialSegments, onFailure, onSuccess }
242 { initialCode: fromMaybe "" codeQ
246 false, _, _ -> element (unsafeCreateDOMComponent "pre") props
249 $ element mdxProvider
250 { children: pure $ jsx cssBaseline {} [ siteInfoJSX ]
251 , components: mdxComponents
254mkQuiz ∷ _ -> Effect (ReactComponent _)
256 fillInTheGaps <- FillInTheGaps.makeComponent
257 box <- Box.makeComponent
258 component "Quiz" \({ initialSegments, onFailure, onSuccess } ∷ { initialSegments ∷ _, onFailure ∷ _, onSuccess :: _}) -> React.do
259 segments /\ updateSegments <- useState initialSegments
260 solved /\ updateSolved <- useState false
263 [ element fillInTheGaps
268 code = toCode segments
269 result <- compiler.compileAndRun { code }
270 liftEffect case result of
272 | String.stripSuffix (String.Pattern "\n") r.stdout == (findResult $ join segments) -> do
273 updateSolved (const true)
279 updated = update segments
280 case segments, updated of
282 | new == old -> mempty
283 _, _ -> updateSegments (const updated)
284 , readOnly: Just solved
289 withSidebar <- WithSidebar.makeComponent
292 $ jssClasses \(theme ∷ CSSTheme) ->
294 component "Sidebar" \{ links } -> React.do
295 classes <- useStyles {}
297 $ element withSidebar
299 { sidebarChildren: [ R.div_ [] ]
300 , notSidebarChildren: [] ∷ Array JSX
304foreign import mdxProvider ∷
307 { components ∷ { | r } -- TODO Don't lie
308 , 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