5import Data.Array (intercalate)
6import Data.Array as Array
7import Data.Either (Either(..))
8import Data.Function.Uncurried (mkFn2)
9import Data.Maybe (Maybe(..), fromMaybe, isJust)
10import Data.Nullable (Nullable)
11import Data.Nullable as Nullable
12import Data.String as String
13import Data.Traversable (mapAccumL)
14import Data.Tuple.Nested ((/\))
16import Effect.Aff (launchAff_)
17import Effect.Class (liftEffect)
18import Effect.Unsafe (unsafePerformEffect)
20import Milkis.Impl (FetchImpl)
21import React.Basic (JSX, ReactComponent, fragment)
22import React.Basic.DOM (Props_h1, Props_h2, Props_h3, Props_p, unsafeCreateDOMComponent)
23import React.Basic.DOM as R
24import React.Basic.Emotion as Emotion
25import React.Basic.Hooks (ReactChildren, element, memo, mkReducer, reactChildrenFromArray, reactChildrenToArray, reactComponent, reactComponentWithChildren, useReducer, useState, useState')
26import React.Basic.Hooks as React
27import Shared.Models.Body (CompileResult)
28import Unsafe.Coerce (unsafeCoerce)
29import Yoga ((/>), (</), (</*))
31import Yoga.Block as Block
32import Yoga.Block.Atom.Toggle.Types (TogglePosition(..))
33import Yoga.Block.Container as Container
34import Yoga.Block.Container.Style (DarkOrLightMode(..), colour)
35import Yoga.Block.Icon.SVG as Icon
36import Yoga.Block.Internal (_0)
37import Yoga.Block.Modal as Modal
38import Yoga.CompileEditor.Component (mkCompileEditor)
39import Yoga.Compiler.Api (apiCompiler)
40import Yoga.Compiler.Types (Compiler)
41import Yoga.FillInTheGaps.Component as FillInTheGaps
42import Yoga.FillInTheGaps.Logic (Segment, findResult, parseSegments, toCode)
43import Yoga.Helpers ((?||))
48 , menuLinks ∷ Array { name ∷ String, link ∷ String }
57 { mdxType ∷ Nullable String
58 , children ∷ Nullable String
59 , className ∷ Nullable String
65 = ShowModal Modal.Props
72 { children ∷ ReactChildren MDX
73 , siteInfo ∷ SiteQueryResult
74 , mdxProviderComponent ∷ ReactComponent MdxProviderProps
77mkLayout fetchImpl = do
79 mkReducer case _, _ of
80 _, ShowModal props -> Just props
81 _, HideModal -> Nothing
82 -- mdxProviderComponent <- mkMdxProviderComponent (apiCompiler fetchImpl)
83 reactComponentWithChildren "MDXLayout" \{ children, siteInfo, mdxProviderComponent } -> React.do
84 (maybeModalProps ∷ Maybe Modal.Props) /\ dispatch <- useReducer Nothing reducer
85 darkOrLightMode /\ setDarkOrLightMode <- useState' Nothing
90 DarkMode -> ToggleIsRight
91 LightMode -> ToggleIsLeft
95 { className: "dark-light-toggle"
98 { marginRight: Emotion.str "0"
99 , marginLeft: Emotion.str "auto"
103 ToggleIsLeft -> setDarkOrLightMode (Just LightMode)
104 ToggleIsRight -> setDarkOrLightMode (Just DarkMode)
105 , togglePosition: fromMaybe ToggleIsLeft maybeTogglePosition
110 , stroke: Emotion.str "#eef"
116 , stroke: Emotion.str "yellow"
119 Color.hsl 205.0 1.0 0.80
121 Color.hsl 250.0 1.0 0.1
125 </ { className: "top-box"
128 { background: Emotion.str colour.backgroundLayer5
132 </* { justify: "space-between"
133 , className: "header"
134 , space: "var(--s-2)"
135 , css: Emotion.css { width: Emotion.percent 100.0 }
140 { className: "blog-header"
143 { fontSize: Emotion.str "min(var(--s3), calc(var(--s1) + 1vw))"
144 , textTransform: Emotion.str "uppercase"
145 , letterSpacing: Emotion.str "calc(min(var(--s0) + 3vw), var(--s2) * -1.7)"
146 , fontWeight: Emotion.str "300"
147 , color: Emotion.str colour.text
150 [ R.text siteInfo.siteMetadata.title ]
155 $ element Container.component
156 { themeVariant: darkOrLightMode
159 { className: "blog-content"
164 { minHeight: Emotion.vh 100.0
165 , background: Emotion.str colour.background
166 , borderRadius: Emotion.str "0 var(--s1) var(--s1) 0"
171 </* { className: "centre"
176 { maxWidth: Emotion.ch 90.0
181 { padding: Emotion.px 16
182 , css: Emotion.css { background: Emotion.str colour.background }
184 /> [ element mdxProviderComponent
187 , showModal: dispatch <<< ShowModal
188 , hideModal: dispatch HideModal
189 , modalOpen: isJust maybeModalProps
194 { className: "blog-footer"
197 { background: Emotion.str colour.backgroundLayer1
198 , borderTop: Emotion.str colour.backgroundLayer3
199 , color: Emotion.str colour.text
202 [ R.text "Mark Eibes" ]
206isQuiz ∷ MDX -> Boolean
208 a.props.mdxType == "pre"
209 && ((unsafeCoerce a).props.children.props.className == "language-purescript")
210 && ((unsafeCoerce a).props.children.props.mdxType == "code")
211 && ((unsafeCoerce a).props.children.props.children # parseSegments # isJust)
213type MdxProviderProps =
214 { children ∷ ReactChildren MDX
215 , siteInfo ∷ SiteQueryResult
216 , showModal ∷ Modal.Props -> Effect Unit
217 , hideModal ∷ Effect Unit
218 , modalOpen ∷ Boolean
221mkLiveMdxProviderComponent ∷ FetchImpl -> Effect (ReactComponent MdxProviderProps)
222mkLiveMdxProviderComponent fetchImpl = mkMdxProviderComponent (apiCompiler fetchImpl)
224mkMdxProviderComponent ∷
227 Effect (ReactComponent MdxProviderProps)
228mkMdxProviderComponent compiler = do
229 editor <- memo $ mkCompileEditor compiler
230 quiz <- memo $ mkQuiz compiler
231 reactComponentWithChildren "MDXProviderComponent" do
232 \{ children, siteInfo, showModal, hideModal, modalOpen } -> React.do
233 visibleUntil /\ updateVisible <- useState 1
235 onFailure title kids =
237 { content: fragment kids
239 , setIsOpen: if _ then pure unit else hideModal
241 onSuccess = updateVisible (_ + one)
242 mapVisible ∷ Int -> MDX -> _
244 { accum: i + if isQuiz kid then one else zero
245 , value: if (i < visibleUntil) then Just kid else Nothing
247 visibleKids ∷ Array MDX
249 reactChildrenToArray children
250 # mapAccumL mapVisible zero
253 contentMDX ∷ ReactChildren MDX
254 contentMDX = reactChildrenFromArray visibleKids
258 \(props ∷ { | Props_h1 }) -> R.h2 props
260 \(props ∷ { | Props_h2 }) -> R.h3 props
262 \(props ∷ { | Props_h3 }) -> R.h4 props
263 , hr: (const $ R.hr {})
265 \(props ∷ { | Props_p }) -> R.p props
268 R.span { children: props.children }
270 mkFn2 \(props ∷ PreProps) other -> do
272 childrenQ = Nullable.toMaybe props.children
273 propsQ = (_.props >>> Nullable.toMaybe) =<< childrenQ
274 mdxTypeQ = (_.mdxType >>> Nullable.toMaybe) =<< propsQ
275 childrenQ2 = (_.children >>> Nullable.toMaybe) =<< propsQ
276 classNameQ = (_.className >>> Nullable.toMaybe) =<< propsQ
277 isCode = fromMaybe false (mdxTypeQ <#> eq "code")
280 (fromMaybe 200 >>> show >>> (_ <> "px")) do
283 ( String.split (String.Pattern "\n") code
285 # \x -> (x * 12) + 100
287 language = fromMaybe "" (classNameQ >>= String.stripPrefix (String.Pattern "language-"))
288 segmentsQ = parseSegments (codeQ ?|| "")
289 case isCode, language, segmentsQ of
290 true, "purescript", Just initialSegments -> element quiz { initialSegments, onFailure, onSuccess }
293 { initialCode: fromMaybe "" codeQ
297 false, _, _ -> element (unsafePerformEffect $ unsafeCreateDOMComponent "pre") props
300 $ element mdxProvider
301 { children: contentMDX
302 , components: mdxComponents
306 { initialSegments ∷ Array (Array Segment)
307 , onFailure ∷ String -> Array JSX -> Effect Unit
308 , onSuccess ∷ Effect Unit
311mkQuiz ∷ ∀ r. { | Compiler r } -> Effect (ReactComponent QuizProps)
313 fillInTheGaps <- FillInTheGaps.makeComponent
314 reactComponent "Quiz" \({ initialSegments, onFailure, onSuccess } ∷ QuizProps) -> React.do
315 segments /\ updateSegments <- useState initialSegments
316 solvedWith /\ updateSolvedWith <- useState Nothing
318 $ element fillInTheGaps
322 let code = toCode segments
323 result <- compiler.compileAndRun { code }
324 liftEffect case result of
326 | String.stripSuffix (String.Pattern "\n") r.stdout == (findResult $ join segments) -> do
327 updateSolvedWith (const $ String.stripSuffix (String.Pattern "\n") r.stdout)
330 | r.stdout /= "\n" -> onFailure "Oh shit!" [ R.text r.stdout ]
331 Right r -> onFailure "Oh shit!" [ R.text r.stderr ]
332 Left (cr ∷ CompileResult) -> onFailure "Oh shit!" [ R.text (intercalate ", " (cr.result <#> _.message)) ]
335 let updated = update segments
336 unless (segments == updated) do updateSegments (const updated)
340foreign import mdxProvider ∷
343 { components ∷ { | r } -- TODO Don't lie
344 , children ∷ ReactChildren MDX
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