4import CSS.Safer (cssSafer)
5import DOM.Hook (useBoundingBox)
6import Data.Array (elem, foldl, head, intercalate, snoc, uncons, zip, (:))
7import Data.Int (round, toNumber)
8import Data.Maybe (Maybe(..), maybe)
9import Data.String (codePointFromChar)
10import Data.String as String
11import Data.Tuple.Nested ((/\))
13import Random.PseudoRandom (mkSeed, randomRs)
14import React.Basic (JSX, element)
15import React.Basic.DOM (unsafeCreateDOMComponent)
16import React.Basic.DOM as HTML
17import React.Basic.DOM.SVG as SVG
18import React.Basic.Hooks (ReactComponent)
19import React.Basic.Hooks as React
21import Scroll.Hook (useScrollYPosition)
22import Theme.Styles (makeStyles, useTheme)
23import Theme.Types (CSSTheme)
25foreign import landingPageDarkRaw ∷ ∀ r. Raw r
27foreign import landingPageLightRaw ∷ ∀ r. Raw r
29mkLandingPageBackground ∷ Effect (ReactComponent { className ∷ String })
30mkLandingPageBackground = do
32 makeStyles \(theme ∷ CSSTheme) ->
35 { fill: theme.backgroundColour
39 { cx: if theme.isLight then "721" else "175"
40 , cy: if theme.isLight then "355" else "310"
41 , rx: if theme.isLight then "647" else "330"
42 , ry: if theme.isLight then "310" else "252"
46 { fill: if theme.isLight then "url(#_Linear9)" else "url(#_Linear12)"
50 { fontFamily: "PragmataPro"
52 , fill: theme.textColour
54 , keyword: cssSafer { fill: theme.green }
55 , typeName: cssSafer { fill: theme.pink }
56 , "@keyframes animatedCursor":
58 { "0%": { opacity: "0.0" }
59 , "50%": { opacity: "1.0" }
63 { animation: "$animatedCursor 0.5s linear infinite"
64 , animationDirection: "alternate"
66 , "@keyframes movingCloud1":
68 { "0%": { transform: "translate3d(-140%, 4%, 0)" }
69 , "100%": { transform: "translate3d(70%, -4%, 0)" }
73 { animation: "$movingCloud1 200s linear infinite"
74 , animationDelay: "-150s"
76 , "@keyframes movingCloud2":
78 { "0%": { transform: "translate3d(-10%, 0%, 0)" }
79 , "100%": { transform: "translate3d(10%, 0%, 0)" }
83 { animation: "$movingCloud2 120s linear infinite"
84 , animationDirection: "alternate"
88 { animation: "$movingCloud2 120s linear infinite"
89 , animationDelay: "-120s"
90 , animationDirection: "alternate"
92 , "@keyframes twinkle":
94 { "0%": { fill: "rgba(250, 250, 250, 0.2)" }
95 , "50%": { fill: "rgba(250, 250, 250, 0.7)" }
96 , "70%": { fill: "rgba(250, 250, 250, 0.4)" }
97 , "100%": { fill: "rgba(250, 250, 250, 1.0)" }
99 , "@keyframes twinkleFast":
101 { "0%": { fill: "rgba(250, 250, 250, 0.7)" }
102 , "100%": { fill: "rgba(250, 250, 250, 0.78)" }
106 { animation: "$twinkleFast 0.2s ease-in-out infinite"
107 , animationDirection: "alternate"
111 { animation: "$twinkle 8s ease-in-out infinite"
112 , animationDelay: "4s"
113 , animationDirection: "alternate"
117 { animation: "$twinkle 4s ease-in-out infinite"
118 , animationDelay: "3s"
119 , animationDirection: "alternate"
121 , "@keyframes rotateStars":
123 { "0%": { transform: "rotate(0deg) scale(1,1)" }
124 , "100%": { transform: "rotate(1deg) scale(1.01, 1.01)" }
128 { animation: "$rotateStars 30s linear infinite"
129 , transformOrigin: "top center"
130 , animationDirection: "alternate"
133 React.component "LandingPageBackground" \{ className } -> React.do
136 scrollY <- useScrollYPosition
137 bb /\ ref <- useBoundingBox
139 scrolled = case bb of
141 Just vh -> scrollY / vh.height
143 $ element (unsafeCreateDOMComponent "svg") -- [TODO] make PR for react-basic to allow for `ref` on svg
145 , viewBox: "0 0 800 447"
146 , preserveAspectRatio: "xMidYMax slice"
147 , xmlns: "http://www.w3.org/2000/svg"
148 , fillRule: "evenodd"
149 , clipRule: "evenodd"
150 , strokeLinejoin: "round"
151 , strokeMiterlimit: "2"
156 , d: "M0 0h800v447H0z"
162 { d: "M0 0h800v447H0z"
167 { clipPath: "url(#a)"
170 { d: "M800.828 448.051V-3.513H-3.028v451.564h803.856z"
171 , fill: "url(#_Linear2)"
174 { className: classes.lightEllipsis
175 , fill: "url(#_Radial3)"
177 , stars classes.stars classes
180 { d: "M610.291 177.316c-35.879-.428-84.67-.691-138.392-.689-38.684.002-74.811.141-105.402.38-49.14-2.745-80.877-9.954-80.877-14.718 0-1.333 2.655.335 7.294-.896 96.772-.175 171.302-1.213 171.302-2.464 0-1.107-59.462-3.372-140.106-3.687 38.487-2.059 102.543-4.972 173.823-4.972 11.996 0 23.761.07 35.216.206 20.76-4.638 60.945-2.784 107.07-2.784 36.08 0 68.525-.075 90.882 2.983 32.491.899 72.142-1.186 114.922-.379 38.68.729 74.805 1.273 105.395 1.613 49.101 3.673 80.599 8.516 80.54 13.279-.016 1.334-2.504 2.58-7.158 3.723-96.763-1.654-171.297-2.024-171.312-.774-.013 1.108 58.473 3.15 139.105 4.989-38.514 3.085-101.653 4.38-172.925 3.033-11.995-.227-23.757-.52-35.209-.872-20.815 4.245-61.034 6.631-107.154 5.759-24.754-.468-47.786-1.809-67.014-3.73z"
181 , fill: if theme.isLight then "#ddf0fb" else "#240c3c"
182 , className: classes.movingCloud1
186 { d: "M609.299 171.607c-35.88-.338-84.67-.545-138.392-.543-38.684.001-74.812.111-105.402.299-49.141-2.166-80.694-5.519-80.694-9.279 0-1.052 2.472-2.073 7.111-3.045 96.772-.137 171.302-.957 171.302-1.944-.001-.874-58.506-1.614-139.15-1.862 38.471-3.009 101.587-4.973 172.867-4.973 11.996 0 23.761.055 35.216.162 20.76-3.66 60.945-6.143 107.07-6.143 36.08 0 68.525 1.519 90.882 3.933 32.491.709 72.142 1.432 114.922 2.069 38.68.575 74.805 1.005 105.395 1.272 49.101 2.9 80.598 6.722 80.54 10.482-.017 1.052-2.505 2.036-7.158 2.938-96.763-1.305-171.297-1.597-171.312-.611-.014.875 58.473 2.486 139.104 3.938-38.514 2.435-101.653 3.457-172.924 2.394a3771.84 3771.84 0 01-35.21-.688c-20.814 3.35-61.033 5.234-107.153 4.545-24.754-.369-47.786-1.428-67.014-2.944z"
187 , fill: if theme.isLight then "#d8effd" else "#120b38"
188 , className: classes.movingCloud1
192 { d: "M684.234 222.269c-12.108-1.642-29.721-2.832-29.76-5.218-.063-3.861 34.226-4.225 76.6-3.504 42.375.721 75.9-2.707 76.956 1.008 2.476 8.704-12.549 8.552-32.56 10.141-12.403-.58-28.24.216-44.434.479-16.193.263-35.197-1.331-46.802-2.906z"
193 , fill: if theme.isLight then "#d8effd" else "url(#_Linear4)"
194 , className: classes.movingCloud2
198 { d: "M130.064 213.821c42.381 0 76.789.237 76.789 4.224 0 3.986-14.408 4.223-56.789 4.223-42.38 0-96.788.208-96.788-4.223s34.408-4.224 76.788-4.224z"
199 , fill: if theme.isLight then "#d8effd" else "#a7847d"
200 , className: classes.movingCloud3
202 , mountains theme.isLight scrolled
204 , buildings theme.isLight scrolled
207 { fill: theme.backgroundColour
208 , d: "M-3.028 431.298h826.844v20.006H-3.028z"
211 , laptop classes scrolled
222 , laptopBackground ∷ String
223 , laptopBody ∷ String
224 , screenText ∷ String
229laptop classes scrolled =
233 { d: "M606.064 273.603c0-6.261-5.076-11.337-11.337-11.337H399.673c-6.261 0-11.337 5.076-11.337 11.337v151.521a5.001 5.001 0 005 5h207.728a5.004 5.004 0 003.536-1.464 5.004 5.004 0 001.464-3.536V273.603z"
237 { d: "M606.064 273.603c0-6.261-5.076-11.337-11.337-11.337H399.673c-6.261 0-11.337 5.076-11.337 11.337v151.521a5.001 5.001 0 005 5h207.728a5.004 5.004 0 003.536-1.464 5.004 5.004 0 001.464-3.536V273.603z"
241 { d: "M599.193 275.905a5.528 5.528 0 00-5.526-5.526H400.604a5.528 5.528 0 00-5.526 5.526v141.039a5.528 5.528 0 005.526 5.526h193.063a5.528 5.528 0 005.526-5.526V275.905z"
243 , className: classes.laptopBackground
246 { d: "M655.04 427.228a.458.458 0 00-.457-.457H340.83v4.975a5 5 0 005 5h304.21a5 5 0 005-5v-4.518z"
247 , className: classes.laptopBody
271 , className: classes.screenText
273 textToTspans classes scrolled 407.0
280 , "hoistJoker f (Joker a) ="
287buildings ∷ Boolean -> Number -> JSX
288buildings isLight scrolled =
290 { transform: "translate(0 " <> show (scrolled * 30.0) <> ")"
293 { d: "M195.171 451.298V159.153H92.709v292.145h102.462z"
294 , fill: if isLight then "url(#_Linear4)" else "url(#_Linear5)"
297 { d: "M289.755 451.298V232.811h-57.232v218.487h57.232z"
298 , fill: if isLight then "url(#_Linear5)" else "url(#_Linear6)"
300 , SVG.path -- 4 shade
301 { d: "M207.557 451.298V232.811h25.314v218.487h-25.314zM92.709 451.298V159.153h25.314v292.145H92.709z"
302 , fill: if isLight then "#b4dbfb" else "#130425"
304 , SVG.path -- 5 shade
305 { d: "M323.451 451.298V327.713h-71.555v123.585h71.555z"
306 , fill: if isLight then "url(#_Linear6)" else "url(#_Linear7)"
307 , transform: "translate(0 " <> show (scrolled * 60.0) <> ")"
310 { d: "M323.451 451.298V327.713h-16.692v123.585h16.692z"
311 , fill: if isLight then "url(#_Linear7)" else "url(#_Linear8)"
312 , transform: "translate(0 " <> show (scrolled * 60.0) <> ")"
315 { d: "M272.498 451.298v-95.771H135.943v95.771h136.555z"
316 , fill: if isLight then "url(#_Linear8)" else "url(#_Linear9)"
317 , transform: "translate(0 " <> show (scrolled * 100.0) <> ")"
319 , SVG.path -- 3 shade
320 { d: "M173.526 451.298v-95.771h-37.583v95.771h37.583z"
321 , fill: if isLight then "#b4dbfb" else "#2e1232"
322 , transform: "translate(0 " <> show (scrolled * 100.0) <> ")"
325 { d: "M85.122 451.298V272.442h-97.675v178.856h97.675z"
326 , fill: if isLight then "#b4dbfb" else "url(#_Linear10)"
327 , transform: "translate(0 " <> show (scrolled * 100.0) <> ")"
329 , SVG.path -- 1 shade
330 { d: "M66.649 456.944V272.442H-3.028v184.502h69.677z"
331 , fill: if isLight then "#b4dbfb" else "url(#_Linear11)"
332 , transform: "translate(0 " <> show (scrolled * 100.0) <> ")"
337mountains ∷ Boolean -> Number -> JSX
338mountains isLight scrolled =
340 { transform: "translate(0 " <> show (scrolled * 70.0) <> ")"
343 { d: "M476.098 218.299l81.644 216.765H352.334l123.764-216.765z"
344 , fill: if isLight then "#4560a0" else "#04102d"
347 { d: "M476.098 218.299l21.823 203.477 81.177-8.706-103-194.771z"
348 , fill: if isLight then "#5e84ba" else "#250849"
352 { d: "M369.627 308.648l88.326 125.788H252.546l117.081-125.788z"
353 , fill: if isLight then "#4560a0" else "#04102d"
357 { d: "M326.11 338.332l68.193 96.732H188.896l137.214-96.732zM508.168 243.792l163.526 204.747H415.051l93.117-204.747z"
358 , fill: if isLight then "#4560a0" else "#04102d"
360 , SVG.path -- 1 and 2 light (right)
361 { d: "M508.168 245.355l63.395 75.115-54.953 46.379-8.442-121.494zM369.612 308.648l54.182 77.687-58.981 41.135 4.799-118.822zM326.184 338.326l16.813 23.99-16.887 11.835.074-35.825z"
362 , fill: if isLight then "#5e84ba" else "#250849"
365 , SVG.path -- rightmost dark (left)
366 { d: "M633.647 230.42L809.34 435.064H457.953L633.647 230.42z"
367 , fill: if isLight then "#4560a0" else "#04102d"
369 , SVG.path -- rightmost light (right)
370 { d: "M633.647 230.42L735.22 475.549l86.041-35.653L633.647 230.42z"
371 , fill: if isLight then "#5e84ba" else "#250849"
373 , SVG.path -- peak left
374 { d: "M643.625 254.384c-.571.219-.745.283-1.863.82-2.864 1.395-6.079 3.209-9.184 2.64-6.187-1.134-10.854-6.283-16.725-6.738l17.786-20.729 9.986 24.007z"
375 , fill: if isLight then "#d9d9d9" else "#655578"
377 , SVG.path -- peak right
378 { d: "M654.229 253.4c-.494-.028-.875-.013-.875-.013-3.255-.486-6.617-.192-9.677.982l-.052.02-9.978-23.97 20.582 22.981z"
379 , fill: if isLight then "#fff" else "#9d779e"
391 Number -> Number -> String -> Array JSX
392textToTspans classes scrolled x str = (foldl toTspan { distance: 0.0, acc: [] } lines).acc <> [ cursor ]
394 toTspan { distance, acc } line = case uncons (words line) of
395 Nothing -> { distance: distance + 1.2, acc }
396 Just { head: "", tail: [] } -> { distance: distance + 1.2, acc }
397 Just { head, tail } -> do
400 newElems = _.spans $ foldl mkNewElems { prefix: " ", spans: [] } tail
402 mkNewElems { prefix, spans } = case _ of
403 "" -> { prefix: prefix <> " ", spans }
410 { children: [ HTML.text $ prefix <> word ]
411 , className: classNameForWord word
418 , dy: show distance <> "em"
419 , children: [ HTML.text head ]
423 pimped = first : newElems
424 { distance: 1.2, acc: acc <> pimped }
426 classNameForWord = case _ of
428 | elem keyword keywords -> classes.keyword
430 | isTypeName typeName -> classes.typeName
435 firstLetter = String.take 1 s
437 inRange c1 c2 = between (codePointFromChar c1) (codePointFromChar c2)
440 String.toCodePointArray
442 >>> maybe false (inRange 'a' 'z' || inRange 'A' 'Z')
444 String.toUpper firstLetter == firstLetter && isLetter firstLetter
472 split = String.split (String.Pattern " ") line
474 lines = String.split (String.Pattern "\n") scrolledString
476 scrolledString = String.take charactersToTake str
478 doneAfterXPercent = 0.5 -- [WARN] Don't set to 0
480 charactersToTake = round $ scrolled * (1.0 / doneAfterXPercent) * len
482 len = toNumber $ String.length str
488 , children: [ HTML.text "█" ]
489 , className: classes.cursor
490 , id: "landing-page-cursor"
494defs = if _ then lightDefs else darkDefs
506 , gradientUnits: "userSpaceOnUse"
507 , gradientTransform: "matrix(0 451.564 -803.856 0 398.9 -3.513)"
511 , stopColor: "#68adfc"
515 , stopColor: "#cbe0f8"
519 , stopColor: "#d2cbf8"
529 , gradientUnits: "userSpaceOnUse"
530 , gradientTransform: "scale(293.8304) rotate(83.86 -.11 .484)"
534 , stopColor: "#bde8f7"
538 , stopColor: "#86b6fe"
548 , gradientUnits: "userSpaceOnUse"
549 , gradientTransform: "scale(219.3256) rotate(84.988 -.028 1.133)"
553 , stopColor: "#bde8f7"
557 , stopColor: "#86b6fe"
567 , gradientUnits: "userSpaceOnUse"
568 , gradientTransform: "rotate(90 -6.304 321.409) scale(123.585)"
572 , stopColor: "#bde8f7"
576 , stopColor: "#86b6fe"
586 , gradientUnits: "userSpaceOnUse"
587 , gradientTransform: "scale(132.4342) rotate(68.9 -1.112 2.415)"
591 , stopColor: "#bde8f7"
595 , stopColor: "#86b6fe"
605 , gradientUnits: "userSpaceOnUse"
606 , gradientTransform: "rotate(90 -88.788 166.635) scale(190.951)"
610 , stopColor: "#bde8f7"
614 , stopColor: "#86b6fe"
624 , gradientUnits: "userSpaceOnUse"
625 , gradientTransform: "matrix(314.21 0 0 9.97478 340.83 431.758)"
629 , stopColor: "#a9abc1"
633 , stopColor: "#65687d"
637 , stopColor: "#c3c5d2"
641 , stopColor: "#64677c"
645 , stopColor: "#a4a7c2"
654 , gradientUnits: "userSpaceOnUse"
655 , gradientTransform: "matrix(647.316 0 0 309.935 720.558 355.142)"
659 , stopColor: "#cfcfcf"
664 , stopColor: "#fff552"
682 , gradientUnits: "userSpaceOnUse"
683 , gradientTransform: "matrix(0 451.564 -803.856 0 398.9 -3.513)"
687 , stopColor: "#000434"
691 , stopColor: "#171043"
695 , stopColor: "#573774"
699 , stopColor: "#b8734e"
709 , gradientUnits: "userSpaceOnUse"
710 , gradientTransform: "matrix(-.53832 -17.3125 1.44013 -.04478 749.812 228.864)"
714 , stopColor: "#e59f77"
718 , stopColor: "#a46075"
722 , stopColor: "#914d74"
732 , gradientUnits: "userSpaceOnUse"
733 , gradientTransform: "scale(293.8304) rotate(83.86 -.11 .484)"
737 , stopColor: "#08001b"
741 , stopColor: "#571547"
751 , gradientUnits: "userSpaceOnUse"
752 , gradientTransform: "scale(219.3256) rotate(84.988 -.028 1.133)"
756 , stopColor: "#000418"
760 , stopColor: "#571548"
770 , gradientUnits: "userSpaceOnUse"
771 , gradientTransform: "matrix(46.7869 123.585 -71.5547 27.0893 259.972 327.713)"
775 , stopColor: "#000418"
779 , stopColor: "#571548"
789 , gradientUnits: "userSpaceOnUse"
790 , gradientTransform: "rotate(90 -6.304 321.409) scale(123.585)"
794 , stopColor: "#3a0843"
798 , stopColor: "#9f5c6a"
808 , gradientUnits: "userSpaceOnUse"
809 , gradientTransform: "scale(101.643) rotate(28.107 -6.187 4.945)"
813 , stopColor: "#2c1d2f"
817 , stopColor: "#480445"
827 , gradientUnits: "userSpaceOnUse"
828 , gradientTransform: "scale(-106.68957) rotate(-86.136 -1.645 -.807)"
832 , stopColor: "#2c1d2f"
836 , stopColor: "#480445"
846 , gradientUnits: "userSpaceOnUse"
847 , gradientTransform: "matrix(20.5602 0 -184.503 0 11.25 272.442)"
851 , stopColor: "#000624"
855 , stopColor: "#4f1557"
865 , gradientUnits: "userSpaceOnUse"
866 , gradientTransform: "matrix(314.21 0 0 9.97478 340.83 431.758)"
870 , stopColor: "#a9abc1"
874 , stopColor: "#65687d"
878 , stopColor: "#c3c5d2"
882 , stopColor: "#64677c"
886 , stopColor: "#a4a7c2"
895 , gradientUnits: "userSpaceOnUse"
896 , gradientTransform: "matrix(330.479 0 0 252.189 174.599 310.24)"
905 , stopColor: "#fff552"
915stars ∷ ∀ css. String -> { twinklingStar1 ∷ String, twinklingStar2 ∷ String, twinklingStar3 ∷ String | css } -> JSX
916stars className classes =
920 xs = randomRs 0.0 800.0 n $ mkSeed 84
922 ys = randomRs (-20.0) 200.0 n $ mkSeed 4398
924 radiuses = randomRs 0.1 1.3 n (mkSeed 15)
926 opacities = randomRs 0.5 0.9 n (mkSeed 92)
929 randomRs 1 50 n (mkSeed 3)
931 1 -> classes.twinklingStar1
932 2 -> classes.twinklingStar2
933 3 -> classes.twinklingStar2
936 zipped = xs <%> ys <%> radiuses <%> classNames <%> opacities
939 cx /\ cy /\ r /\ className' /\ opacity <- zipped
946 , className: className'
947 , opacity: show (opacity - cy / 200.0)
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