4import Data.Array (foldMap, intercalate)
5import Data.Array as Array
6import Data.Array.NonEmpty as NEA
7import Data.Either (Either(..))
8import Data.Function.Uncurried (mkFn2)
9import Data.Maybe (Maybe(..), fromMaybe, isJust)
10import Data.Monoid (guard)
11import Data.Nullable (Nullable)
12import Data.Nullable as Nullable
13import Data.String as String
14import Data.Traversable (mapAccumL)
15import Data.Tuple.Nested ((/\))
17import Effect.Aff (launchAff_)
18import Effect.Class (liftEffect)
19import JSS (jssClasses)
20import Justifill (justifill)
21import Milkis.Impl (FetchImpl)
22import React.Basic (JSX, ReactComponent, fragment)
23import React.Basic.DOM (css, unsafeCreateDOMComponent)
24import React.Basic.DOM as R
25import React.Basic.Helpers (jsx)
26import React.Basic.Hooks (ReactChildren, component, componentWithChildren, element, memo, reactChildrenToArray, useReducer, useState)
27import React.Basic.Hooks as React
28import Shared.Models.Body (CompileResult)
29import Unsafe.Coerce (unsafeCoerce)
30import Yoga.Box.Component as Box
31import Yoga.ClickAway.Component as ClickAway
32import Yoga.CloseIcon.Component as CloseIcon
33import Yoga.Cluster.Component as Cluster
34import Yoga.CompileEditor.Component (mkCompileEditor)
35import Yoga.Compiler.Api (apiCompiler)
36import Yoga.Compiler.Types (Compiler)
37import Yoga.Cover.Component as Cover
38import Yoga.FillInTheGaps.Component as FillInTheGaps
39import Yoga.FillInTheGaps.Logic (findResult, parseSegments, toCode)
40import Yoga.Header.Component (mkHeader)
41import Yoga.Helpers ((?||))
42import Yoga.Imposter.Component as Imposter
43import Yoga.InlineCode.Component as InlineCode
44import Yoga.Modal.Component as Modal
45import Yoga.Theme (fromTheme)
46import Yoga.Theme.CSSBaseline (mkCssBaseline)
47import Yoga.Theme.Default (darkTheme)
48import Yoga.Theme.Default as Default
49import Yoga.Theme.Provider (mkThemeProvider)
50import Yoga.Theme.Styles (makeStylesJSS)
51import Yoga.Theme.Types (CSSTheme)
52import Yoga.Typography.Header (HeadingLevel(..), mkH)
53import Yoga.Typography.Paragraph (mkP)
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 <- memo Modal.makeComponent
89 imposter <- memo Imposter.makeComponent
90 cover <- memo Cover.makeComponent
91 cluster <- memo Cluster.makeComponent
92 clickAway <- memo ClickAway.makeComponent
93 mdxProviderComponent <- memo $ mkMdxProviderComponent (apiCompiler fetchImpl)
94 componentWithChildren "MDXLayout" \{ children, siteInfo } -> React.do
95 (maybeModalProps ∷ Maybe Modal.Props) /\ dispatch <-
96 useReducer Nothing case _, _ of
97 _, ShowModal props -> Just props
98 _, HideModal -> Nothing
100 $ element themeProvider
101 { theme: fromTheme darkTheme
104 { style: css { maxWidth: "none" }
107 # foldMap \modalProps ->
109 [ element clickAway { allowEscape: Just true, onClick: dispatch HideModal, style: Nothing }
110 , element modal (justifill modalProps)
112 , element mdxProviderComponent
115 , showModal: dispatch <<< ShowModal
116 , hideModal: dispatch HideModal
123isQuiz ∷ JSX -> Boolean
125 (unsafeCoerce a).props.mdxType == "pre"
126 && ((unsafeCoerce a).props.children.props.className == "language-purescript")
127 && ((unsafeCoerce a).props.children.props.mdxType == "code")
128 && ((unsafeCoerce a).props.children.props.children # parseSegments # isJust)
130type MdxProviderProps =
131 { children ∷ ReactChildren JSX
132 , siteInfo ∷ SiteQueryResult
133 , showModal ∷ Modal.Props -> Effect Unit
134 , hideModal ∷ Effect Unit
137mkMdxProviderComponent ∷ ∀ r. { | Compiler r } -> Effect (ReactComponent MdxProviderProps)
138mkMdxProviderComponent compiler = do
139 cssBaseline <- memo (mkCssBaseline Default.fontFaces)
140 editor <- memo $ mkCompileEditor compiler
141 box <- memo $ Box.makeComponent
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 } ∷ MdxProviderProps) -> 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)
175 { accum: i + if isQuiz kid then one else zero
176 , value: guard (i < visibleUntil) (pure kid)
178 visibleKids ∷ Array JSX
180 reactChildrenToArray children
181 # mapAccumL mapVisible zero
187 [ jsx header { className: "" } [ R.text siteInfo.siteMetadata.title ]
188 , jsx box {} visibleKids
191 mdxComponents /\ _ <-
195 element h ({ level: H2, text: props.children, className: Nothing })
198 element h { level: H3, text: props.children, className: Nothing }
201 element h { level: H4, text: props.children, className: Nothing }
202 , hr: (const $ R.hr {})
206 { children: [ element p { text: props.children } ]
210 R.span { className: classes.code, children: props.children }
212 mkFn2 \(props ∷ PreProps) other -> do
214 childrenQ = Nullable.toMaybe props.children
215 propsQ = (_.props >>> Nullable.toMaybe) =<< childrenQ
216 mdxTypeQ = (_.mdxType >>> Nullable.toMaybe) =<< propsQ
217 childrenQ2 = (_.children >>> Nullable.toMaybe) =<< propsQ
218 classNameQ = (_.className >>> Nullable.toMaybe) =<< propsQ
219 isCode = fromMaybe false (mdxTypeQ <#> eq "code")
222 (fromMaybe 200 >>> show >>> (_ <> "px")) do
225 ( String.split (String.Pattern "\n") code
227 # \x -> (x * 12) + 100
229 language = fromMaybe "" (classNameQ >>= String.stripPrefix (String.Pattern "language-"))
230 segmentsQ = parseSegments (codeQ ?|| "")
231 case isCode, language, segmentsQ of
232 true, "purescript", Just initialSegments -> element quiz { initialSegments, onFailure, onSuccess }
235 { initialCode: fromMaybe "" codeQ
239 false, _, _ -> element (unsafeCreateDOMComponent "pre") props
242 $ element mdxProvider
243 { children: pure $ jsx cssBaseline {} [ siteInfoJSX ]
244 , components: mdxComponents
247mkQuiz ∷ _ -> Effect (ReactComponent _)
249 fillInTheGaps <- FillInTheGaps.makeComponent
250 box <- Box.makeComponent
251 component "Quiz" \({ initialSegments, onFailure, onSuccess } ∷ { initialSegments ∷ _, onFailure ∷ _, onSuccess ∷ _ }) -> React.do
252 segments /\ updateSegments <- useState initialSegments
253 solvedWith /\ updateSolvedWith <- useState Nothing
256 [ element fillInTheGaps
260 let code = toCode segments
261 result <- compiler.compileAndRun { code }
262 liftEffect case result of
264 | String.stripSuffix (String.Pattern "\n") r.stdout == (findResult $ join segments) -> do
265 updateSolvedWith (const $ String.stripSuffix (String.Pattern "\n") r.stdout)
268 | r.stdout /= "\n" -> onFailure "Oh shit!" [ R.text r.stdout ]
269 Right r -> onFailure "Oh shit!" [ R.text r.stderr ]
270 Left (cr ∷ CompileResult) -> onFailure "Oh shit!" [ R.text (intercalate ", " (cr.result <#> _.message)) ]
273 let updated = update segments
274 case segments, updated of
276 | new == old -> mempty
277 _, _ -> updateSegments (const updated)
282foreign import mdxProvider ∷
285 { components ∷ { | r } -- TODO Don't lie
286 , 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