4import Components.Container as Container
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 ((/\))
18import Effect.Aff (launchAff_)
19import Effect.Class (liftEffect)
20import Effect.Unsafe (unsafePerformEffect)
21import JSS (jssClasses)
22import Justifill (justifill)
23import Milkis.Impl (FetchImpl)
24import React.Basic (JSX, ReactComponent, fragment)
25import React.Basic.DOM (Props_h1, Props_h2, Props_h3, Props_p, css, unsafeCreateDOMComponent)
26import React.Basic.DOM as R
27import React.Basic.Helpers (jsx)
28import React.Basic.Hooks (ReactChildren, element, memo, mkReducer, reactChildrenFromArray, reactChildrenToArray, reactComponent, reactComponentWithChildren, useReducer, useState)
29import React.Basic.Hooks as React
30import Shared.Models.Body (CompileResult)
31import Unsafe.Coerce (unsafeCoerce)
33import Yoga.Box.Component as Box
34import Yoga.ClickAway.Component as ClickAway
35import Yoga.CloseIcon.Component as CloseIcon
36import Yoga.CompileEditor.Component (mkCompileEditor)
37import Yoga.Compiler.Api (apiCompiler)
38import Yoga.Compiler.Types (Compiler)
39import Yoga.Cover.Component as Cover
40import Yoga.FillInTheGaps.Component as FillInTheGaps
41import Yoga.FillInTheGaps.Logic (Segment, findResult, parseSegments, toCode)
42import Yoga.Header.Component (mkHeader)
43import Yoga.Helpers ((?||))
44import Yoga.Imposter.Component as Imposter
45import Yoga.InlineCode.Component as InlineCode
46import Yoga.Modal.Component as Modal
47import Yoga.Theme (fromTheme)
48import Yoga.Theme.CSSBaseline (mkCssBaseline)
49import Yoga.Theme.Default (darkTheme)
50import Yoga.Theme.Default as Default
51import Yoga.Theme.Provider (mkThemeProvider)
52import Yoga.Theme.Styles (makeStylesJSS)
53import Yoga.Theme.Types (CSSTheme)
58 , menuLinks ∷ Array { name ∷ String, link ∷ String }
67 { mdxType ∷ Nullable String
68 , children ∷ Nullable String
69 , className ∷ Nullable String
75 = ShowModal Modal.Props
82 { children ∷ ReactChildren JSX
83 , siteInfo ∷ SiteQueryResult
86mkLayout fetchImpl = do
87 themeProvider <- mkThemeProvider
88 modal <- Modal.makeComponent
89 imposter <- Imposter.makeComponent
90 cover <- Cover.makeComponent
91 clickAway <- ClickAway.makeComponent
93 mkReducer case _, _ of
94 _, ShowModal props -> Just props
95 _, HideModal -> Nothing
96 mdxProviderComponent <- memo $ mkMdxProviderComponent (apiCompiler fetchImpl)
97 reactComponentWithChildren "MDXLayout" \{ children, siteInfo } -> React.do
98 (maybeModalProps ∷ Maybe Modal.Props) /\ dispatch <- useReducer Nothing reducer
101 { theme: fromTheme darkTheme }
103 { style: css { maxWidth: "none" } }
105 # foldMap \modalProps ->
107 [ element clickAway { allowEscape: Just true, onClick: dispatch HideModal, style: Nothing }
108 , element modal (justifill modalProps)
110 , element Container.component
112 reactChildrenFromArray
113 [ element mdxProviderComponent
116 , showModal: dispatch <<< ShowModal
117 , hideModal: dispatch HideModal
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)
131type MdxProviderProps =
132 { children ∷ ReactChildren JSX
133 , siteInfo ∷ SiteQueryResult
134 , showModal ∷ Modal.Props -> Effect Unit
135 , hideModal ∷ Effect Unit
138mkMdxProviderComponent ∷ ∀ r. { | Compiler r } -> Effect (ReactComponent MdxProviderProps)
139mkMdxProviderComponent compiler = do
140 cssBaseline <- memo (mkCssBaseline Default.fontFaces)
141 editor <- memo $ mkCompileEditor compiler
142 box <- memo $ Box.makeComponent
143 header <- memo mkHeader
144 yogaInlineCode <- InlineCode.makeComponent
145 quiz <- memo $ mkQuiz compiler
146 closeIcon <- CloseIcon.makeComponent
147 reactComponentWithChildren "MDXProviderComponent" \({ children, siteInfo, showModal, hideModal } ∷ MdxProviderProps) -> React.do
148 visibleUntil /\ updateVisible <- useState 1
150 onFailure title kids =
154 , icon: element closeIcon { onClick: hideModal, style: Nothing }
159 onSuccess = updateVisible (_ + one)
161 { accum: i + if isQuiz kid then one else zero
162 , value: guard (i < visibleUntil) (pure kid)
164 visibleKids ∷ Array JSX
166 reactChildrenToArray children
167 # mapAccumL mapVisible zero
173 [ jsx header { className: "" } [ R.text siteInfo.siteMetadata.title ]
174 , jsx box {} visibleKids
177 mdxComponents /\ _ <-
180 \(props ∷ { | Props_h1 }) -> R.h1 props
182 \(props ∷ { | Props_h2 }) -> R.h2 props
184 \(props ∷ { | Props_h3 }) -> R.h3 props
185 , hr: (const $ R.hr {})
187 \(props ∷ { | Props_p }) -> R.p props
190 R.span { children: props.children }
192 mkFn2 \(props ∷ PreProps) other -> do
194 childrenQ = Nullable.toMaybe props.children
195 propsQ = (_.props >>> Nullable.toMaybe) =<< childrenQ
196 mdxTypeQ = (_.mdxType >>> Nullable.toMaybe) =<< propsQ
197 childrenQ2 = (_.children >>> Nullable.toMaybe) =<< propsQ
198 classNameQ = (_.className >>> Nullable.toMaybe) =<< propsQ
199 isCode = fromMaybe false (mdxTypeQ <#> eq "code")
202 (fromMaybe 200 >>> show >>> (_ <> "px")) do
205 ( String.split (String.Pattern "\n") code
207 # \x -> (x * 12) + 100
209 language = fromMaybe "" (classNameQ >>= String.stripPrefix (String.Pattern "language-"))
210 segmentsQ = parseSegments (codeQ ?|| "")
211 case isCode, language, segmentsQ of
212 true, "purescript", Just initialSegments -> element quiz { initialSegments, onFailure, onSuccess }
215 { initialCode: fromMaybe "" codeQ
219 false, _, _ -> element (unsafePerformEffect $ unsafeCreateDOMComponent "pre") props
222 $ element mdxProvider
223 { children: pure $ jsx cssBaseline {} [ siteInfoJSX ]
224 , components: mdxComponents
228 { initialSegments ∷ Array (Array Segment)
229 , onFailure ∷ String -> Array JSX -> Effect Unit
230 , onSuccess ∷ Effect Unit
233mkQuiz ∷ ∀ r. { | Compiler r } -> Effect (ReactComponent QuizProps)
235 fillInTheGaps <- FillInTheGaps.makeComponent
236 box <- Box.makeComponent
237 reactComponent "Quiz" \({ initialSegments, onFailure, onSuccess } ∷ QuizProps) -> React.do
238 segments /\ updateSegments <- useState initialSegments
239 solvedWith /\ updateSolvedWith <- useState Nothing
242 [ element fillInTheGaps
246 let code = toCode segments
247 result <- compiler.compileAndRun { code }
248 liftEffect case result of
250 | String.stripSuffix (String.Pattern "\n") r.stdout == (findResult $ join segments) -> do
251 updateSolvedWith (const $ String.stripSuffix (String.Pattern "\n") r.stdout)
254 | r.stdout /= "\n" -> onFailure "Oh shit!" [ R.text r.stdout ]
255 Right r -> onFailure "Oh shit!" [ R.text r.stderr ]
256 Left (cr ∷ CompileResult) -> onFailure "Oh shit!" [ R.text (intercalate ", " (cr.result <#> _.message)) ]
259 let updated = update segments
260 case segments, updated of
262 | new == old -> mempty
263 _, _ -> updateSegments (const updated)
268foreign import mdxProvider ∷
271 { components ∷ { | r } -- TODO Don't lie
272 , 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