From 24c98c145a1227b9103dd43ea3bd410359007cb4 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 25 Mar 2021 10:44:21 -0600 Subject: [PATCH 001/116] allow more stuff in examples --- marketing/Shpadoinkle/Marketing/View.hs | 13 ++++++++----- marketing/example.template.top | 16 ++++++++++++++-- marketing/hello-world.example | 2 +- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/marketing/Shpadoinkle/Marketing/View.hs b/marketing/Shpadoinkle/Marketing/View.hs index d3c12800..3eaddacf 100644 --- a/marketing/Shpadoinkle/Marketing/View.hs +++ b/marketing/Shpadoinkle/Marketing/View.hs @@ -176,14 +176,17 @@ compileExample = kleisli $ \(Example cc token nonce _) -> do res <- compile token nonce $ exampleTemplate cc cur' <- readTVarIO mutex case cur' of - Nothing -> - return . pur $ (#snowNonce %~ (+ 1)) . case res of - Left e -> #state .~ EError e - Right _ -> #state .~ EReady + Nothing -> ret res 1 + Just cc' | cc' == cc -> do + atomically $ writeTVar mutex Nothing + ret res 1 Just cc' -> do atomically $ writeTVar mutex Nothing res' <- compile token (nonce + 1) $ exampleTemplate cc' - return . pur $ (#snowNonce %~ (+ 2)) . case res' of + ret res' 2 + where + ret res n = + return . pur $ (#snowNonce %~ (+ n)) . case res of Left e -> #state .~ EError e Right _ -> #state .~ EReady diff --git a/marketing/example.template.top b/marketing/example.template.top index 234f9f0e..ec484dd4 100644 --- a/marketing/example.template.top +++ b/marketing/example.template.top @@ -1,12 +1,24 @@ -{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE OverloadedLabels #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE RankNTypes #-} module Main where import Data.Text +import Data.Generics.Labels () import Prelude hiding (span, div) -import Shpadoinkle.Backend.ParDiff (runParDiff) +import GHC.Generics +import Shpadoinkle.Lens +import Shpadoinkle.Backend.ParDiff (runParDiff) +import Shpadoinkle.Backend.Snabbdom (runSnabbdom) import Shpadoinkle.Html import Shpadoinkle.Html.Utils import Shpadoinkle.Run (runJSorWarp, simple) diff --git a/marketing/hello-world.example b/marketing/hello-world.example index fbaf5671..d71c5943 100644 --- a/marketing/hello-world.example +++ b/marketing/hello-world.example @@ -3,4 +3,4 @@ hello name = view = hello "World" -app = simple runParDiff () (const view) getBody +app = simple runSnabbdom () (const view) getBody -- GitLab From a7597db064be245b770ab4e7d340e4cf5076f8c2 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 25 Mar 2021 15:14:24 -0600 Subject: [PATCH 002/116] add deployment --- etc/motd | 16 +++++ isreal/base.nix | 67 +++++++++++++++++++ isreal/deploy.nix | 16 +++++ isreal/deploy/configuration.nix | 16 +++++ isreal/deploy/hardware-configuration.nix | 6 ++ isreal/deploy/networking.nix | 30 +++++++++ isreal/headers.nginx | 30 +++++++++ isreal/service.nix | 39 +++++++++++ .../Shpadoinkle/Marketing/Disembodied.hs | 10 +-- marketing/Shpadoinkle/Marketing/Types.hs | 28 ++++++-- marketing/Shpadoinkle/Marketing/View.hs | 19 ++++-- marketing/counter.example | 9 +++ marketing/example.template.top | 3 +- 13 files changed, 270 insertions(+), 19 deletions(-) create mode 100644 etc/motd create mode 100644 isreal/base.nix create mode 100644 isreal/deploy.nix create mode 100644 isreal/deploy/configuration.nix create mode 100644 isreal/deploy/hardware-configuration.nix create mode 100644 isreal/deploy/networking.nix create mode 100644 isreal/headers.nginx create mode 100644 isreal/service.nix create mode 100644 marketing/counter.example diff --git a/etc/motd b/etc/motd new file mode 100644 index 00000000..f2659ba3 --- /dev/null +++ b/etc/motd @@ -0,0 +1,16 @@ + . .-. . _ * _ . + * / \ (( _/ \ * . + _ . .--'\/\_ \ ` / \ * ___ + * / \_ _/ ^ \/\'__ /\/\ /\ __/ \ * + / \ / .' _/ / \ *' / \/ \/ .`'\_/\ . + . /\/\ /\/ :' __ ^/ ^/ `--./.' ^ `-.\ _ _:\ _ + / \/ \ _/ \-' __/.' ^ _ \_ .'\ _/ \ . __/ \ + /\ .- `. \/ \ / -. _/ \ -. `_/ \ / `._/ ^ \ + / `-.__ ^ / .-'.--' . / `--./ .-' `-. `-. `. - `. +@/ `. / / _`-. / .-' / . _' \ \ \ .- \% + | | | | + ___ ___ | | ___ _ __ __ _ __| | ___ + / __/ _ \| |/ _ \| '__/ _` |/ _` |/ _ \ + | (_| (_) | | (_) | | | (_| | (_| | (_) | + \___\___/|_|\___/|_| \__,_|\__,_|\___/ + diff --git a/isreal/base.nix b/isreal/base.nix new file mode 100644 index 00000000..38914a2e --- /dev/null +++ b/isreal/base.nix @@ -0,0 +1,67 @@ +{ pkgs, ...}: { + + boot.cleanTmpDir = true; + + time.timeZone = "Etc/UTC"; + + programs.bash = { + enableCompletion = true; + promptInit = '' + export TERM=xterm + ''; + }; + + environment.systemPackages = with pkgs; [ ripgrep curl vim git ]; + + services = { + openssh = { + enable = true; + passwordAuthentication = false; + # Priority 0 (top) in order to override configuration.nix + authorizedKeysFiles = pkgs.lib.mkOverride 0 [ "/etc/ssh/authorized_keys.d/%u" ]; + }; + fail2ban.enable = true; + locate.enable = true; + timesyncd = { + enable = true; + servers = [ + "time.nist.gov" + "time-a-b.nist.gov" + "time-b-b.nist.gov" + "time-c-b.nist.gov" + "time-d-b.nist.gov" + "utcnist.colorado.edu" + "utcnist2.colorado.edu" + ]; + }; + }; + + security = { + audit.enable = true; + auditd.enable = true; + acme = { + email = "isaac.shapira@platonic.systems"; + acceptTerms = true; + }; + }; + + users = { + mutableUsers = false; + users.isaac = { + extraGroups = [ "wheel" ]; + uid = 1234; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKZzKmSxSVqol+YNsGr5Cts5Cr/eIHqCm/KISHsluVtJ isaac@dunlap" + ]; + shell = pkgs.bash; + }; + motd = builtins.readFile ./../etc/motd; + }; + + networking.firewall = { + enable = true; + rejectPackets = true; + allowedTCPPorts = [ 80 443 ]; + }; + +} diff --git a/isreal/deploy.nix b/isreal/deploy.nix new file mode 100644 index 00000000..908558a8 --- /dev/null +++ b/isreal/deploy.nix @@ -0,0 +1,16 @@ +{ + network.description = "colorado"; + isreal-swan = { + deployment = { + targetHost = "104.131.28.246"; + alwaysActivate = true; + }; + imports = [ + ./deploy/configuration.nix + ./module.nix + ./service.nix + ./base.nix + ]; + }; + +} diff --git a/isreal/deploy/configuration.nix b/isreal/deploy/configuration.nix new file mode 100644 index 00000000..ff78ce16 --- /dev/null +++ b/isreal/deploy/configuration.nix @@ -0,0 +1,16 @@ +{ ... }: { + imports = [ + ./hardware-configuration.nix + ./networking.nix # generated at runtime by nixos-infect + ]; + + boot.cleanTmpDir = true; + networking.hostName = "isreal-swan"; + networking.firewall.allowPing = true; + services.openssh.enable = true; + users.users.root.openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDeppey98/6oV5sXi/fy8fqxlc+tyYQlxiuVOhx8IcvZ chloe@daria" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILMaYwUX4MK8kWv0EW0ALvNMDAgIMvaLehvTSN28gf4R chloe@chloe-nixos01" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKZzKmSxSVqol+YNsGr5Cts5Cr/eIHqCm/KISHsluVtJ isaac@dunlap" + ]; +} diff --git a/isreal/deploy/hardware-configuration.nix b/isreal/deploy/hardware-configuration.nix new file mode 100644 index 00000000..484eb7c7 --- /dev/null +++ b/isreal/deploy/hardware-configuration.nix @@ -0,0 +1,6 @@ +{ modulesPath, ... }: +{ + imports = [ (modulesPath + "/profiles/qemu-guest.nix") ]; + boot.loader.grub.device = "/dev/vda"; + fileSystems."/" = { device = "/dev/vda1"; fsType = "ext4"; }; +} diff --git a/isreal/deploy/networking.nix b/isreal/deploy/networking.nix new file mode 100644 index 00000000..d78754c1 --- /dev/null +++ b/isreal/deploy/networking.nix @@ -0,0 +1,30 @@ +{ lib, ... }: { + # This file was populated at runtime with the networking + # details gathered from the active system. + networking = { + nameservers = [ "8.8.8.8" + ]; + defaultGateway = "104.131.0.1"; + defaultGateway6 = ""; + dhcpcd.enable = false; + usePredictableInterfaceNames = lib.mkForce false; + interfaces = { + eth0 = { + ipv4.addresses = [ + { address="104.131.28.246"; prefixLength=18; } +{ address="10.17.0.5"; prefixLength=16; } + ]; + ipv6.addresses = [ + { address="fe80::e8a3:8cff:fe62:6d3b"; prefixLength=64; } + ]; + ipv4.routes = [ { address = "104.131.0.1"; prefixLength = 32; } ]; + ipv6.routes = [ { address = ""; prefixLength = 32; } ]; + }; + + }; + }; + services.udev.extraRules = '' + ATTR{address}=="ea:a3:8c:62:6d:3b", NAME="eth0" + ATTR{address}=="a6:bc:4f:25:08:08", NAME="eth1" + ''; +} diff --git a/isreal/headers.nginx b/isreal/headers.nginx new file mode 100644 index 00000000..076db707 --- /dev/null +++ b/isreal/headers.nginx @@ -0,0 +1,30 @@ +error_page 502 /error_page.html; +error_page 404 /notfound_page.html; +add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive"; +if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; + # + # Custom headers and headers various browsers *should* be OK with but aren't + # + add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; + # + # Tell client that this pre-flight info is valid for 20 days + # + add_header 'Access-Control-Max-Age' 1728000; + add_header 'Content-Type' 'text/plain; charset=utf-8'; + add_header 'Content-Length' 0; + return 204; +} +if ($request_method = 'POST') { + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; + add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; + add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; +} +if ($request_method = 'GET') { + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; + add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; + add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; +} diff --git a/isreal/service.nix b/isreal/service.nix new file mode 100644 index 00000000..cba44782 --- /dev/null +++ b/isreal/service.nix @@ -0,0 +1,39 @@ +let ports = { isreal = 8080; hoogle = 8090; }; +in { +services = { + + nginx = { + enable = true; + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedTlsSettings = true; + recommendedProxySettings = true; + virtualHosts = let + mkLocation = pp: { + forceSSL = true; + enableACME = true; + locations."/error_page.html" = { + root = "/usr/www"; + }; + locations."/notfound_page.html" = { + root = "/usr/www"; + }; + locations."/" = { + proxyPass = "http://localhost:${toString pp}"; + extraConfig = builtins.readFile ./headers.nginx; + }; + }; + in { + "isreal.shpadoinkle.org" = mkLocation ports.isreal; + "hoogle.shpadoinkle.org" = mkLocation ports.hoogle; + }; + }; + + shpadoinkle-isreal = { + enable = true; + port = ports.isreal; + hoogle = ports.hoogle; + }; + +}; +} diff --git a/marketing/Shpadoinkle/Marketing/Disembodied.hs b/marketing/Shpadoinkle/Marketing/Disembodied.hs index d881767d..0c7d3c74 100644 --- a/marketing/Shpadoinkle/Marketing/Disembodied.hs +++ b/marketing/Shpadoinkle/Marketing/Disembodied.hs @@ -23,13 +23,13 @@ import System.Environment (getArgs) import Shpadoinkle (Html, JSM, MonadJSM, TVar) import Shpadoinkle.Disembodied (Disembodied (SiteSpec), writeSite) -import Shpadoinkle.Isreal.Types (Code, SnowToken, genSnowToken) +import Shpadoinkle.Isreal.Types (Code, SnowToken) import Shpadoinkle.Lens (onSum) import Shpadoinkle.Run (Env (Prod)) import Shpadoinkle.Marketing.Types -import Shpadoinkle.Marketing.View (comparisons, fourOhFour, home, - template) +import Shpadoinkle.Marketing.View (comparisons, fourOhFour, + genExampleTokens, home, template) newtype Noop a = Noop (JSM a) deriving newtype (Functor, Applicative, Monad, MonadJSM, MonadIO) @@ -40,7 +40,7 @@ wrap :: Monad m => Prism' Frontend a -> (a -> Html m a) -> a -> b -> Html m Fron wrap l v x = const $ template Prod (x ^. re l) (l `onSum` v x) -site :: Hooglable m => ExampleEffects m => MonadJSM m => SnowToken -> SiteSpec () (SPA m) +site :: Hooglable m => ExampleEffects m => MonadJSM m => Examples SnowToken -> SiteSpec () (SPA m) site token = wrap #_HomeM home (emptyHome token) :<|> wrap #_ComparisonM comparisons . (`Comparison` Nothing) :<|> const fourOhFour @@ -54,5 +54,5 @@ main = do [ "--out", out' ] -> out' [ "-o", out' ] -> out' _ -> error "You must pass --out or -o" - token <- genSnowToken + token <- genExampleTokens writeSite @ (SPA Noop) out () (site token) diff --git a/marketing/Shpadoinkle/Marketing/Types.hs b/marketing/Shpadoinkle/Marketing/Types.hs index db811149..b088687d 100644 --- a/marketing/Shpadoinkle/Marketing/Types.hs +++ b/marketing/Shpadoinkle/Marketing/Types.hs @@ -6,6 +6,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeOperators #-} @@ -14,11 +15,13 @@ module Shpadoinkle.Marketing.Types where +import Control.Lens ((^.)) import Control.Monad.Except (MonadTrans (..)) import Control.Monad.Reader import Data.Aeson (FromJSON, ToJSON) import Data.ByteString.Lazy (fromStrict) -import Data.FileEmbed +import Data.FileEmbed (embedFile) +import Data.Generics.Labels () import Data.Monoid.Generic (GenericMonoid (..), GenericSemigroup (..)) import Data.Text (Text, splitOn) @@ -30,7 +33,6 @@ import Servant.API (Capture, ToHttpApiData (toUrlPiece), type (:<|>) (..), type (:>)) - import Shpadoinkle (NFData, TVar) import Shpadoinkle.Isreal.Types (Code (..), CompileError, SnowNonce, SnowToken) @@ -42,8 +44,9 @@ import Shpadoinkle.Widgets.Types (Input, Pick (One), import Shpadoinkle.Marketing.Types.Hoogle (Target) -newtype Examples = Examples - { helloWorld :: Example +data Examples a = Examples + { helloWorld :: a + , counter :: a } deriving stock (Eq, Ord, Show, Read, Generic) deriving anyclass (FromJSON, ToJSON, NFData) @@ -68,6 +71,10 @@ helloWorldExample :: Code helloWorldExample = Code $ fromStrict $(embedFile "./hello-world.example") +counterExample :: Code +counterExample = Code $ fromStrict $(embedFile "./counter.example") + + exampleTemplate :: Code -> Code exampleTemplate (Code inputHaskell') = Code $ fromStrict $(embedFile "./example.template.top") @@ -81,14 +88,21 @@ topOffset = subtract 1 $ length $ splitOn "\n" $ decodeUtf8 $(embedFile "./examp data Home = Home { hoogle :: Hoogle - , examples :: Examples + , examples :: Examples Example } deriving stock (Eq, Ord, Show, Read, Generic) deriving anyclass (FromJSON, ToJSON, NFData) -emptyHome :: SnowToken -> Home -emptyHome st = Home mempty $ Examples $ Example helloWorldExample st 0 ELoading +emptyExample :: Code -> SnowToken -> Example +emptyExample cc st = Example cc st 0 ELoading + + +emptyHome :: Examples SnowToken -> Home +emptyHome st = Home mempty $ Examples + { helloWorld = emptyExample helloWorldExample (st ^. #helloWorld) + , counter = emptyExample counterExample (st ^. #counter) + } data Hoogle = Hoogle diff --git a/marketing/Shpadoinkle/Marketing/View.hs b/marketing/Shpadoinkle/Marketing/View.hs index 3eaddacf..4cfcddab 100644 --- a/marketing/Shpadoinkle/Marketing/View.hs +++ b/marketing/Shpadoinkle/Marketing/View.hs @@ -236,6 +236,7 @@ home home' = section_ , hero , pitch , onRecordEndo (#examples . #helloWorld) example home' + , onRecordEndo (#examples . #counter) example home' ] @@ -279,12 +280,22 @@ template ev fe v = H.html_ [ v ] ] - where codemirrorCDN = mappend "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.59.4/" + where codemirrorCDN = mappend "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.59.4/" + + +compileExamples :: MonadJSM m => ExampleEffects m => Home -> m Home +compileExamples home' = do + f <- runContinuation compileExample $ home' ^. #examples . #helloWorld + return $ (#examples . #helloWorld %~ f) home' + + +genExampleTokens :: MonadIO m => m (Examples SnowToken) +genExampleTokens = liftIO $ Examples <$> genSnowToken <*> genSnowToken start :: MonadIO m => Route -> m Frontend start = \case - HomeR -> HomeM . emptyHome <$> liftIO genSnowToken + HomeR -> HomeM . emptyHome <$> genExampleTokens ComparisonR f -> pure . ComparisonM $ Comparison f Nothing FourOhFourR -> pure FourOhFourM @@ -297,7 +308,3 @@ startJS r = do x -> pure x -compileExamples :: MonadJSM m => ExampleEffects m => Home -> m Home -compileExamples home' = do - f <- runContinuation compileExample $ home' ^. #examples . #helloWorld - return $ (#examples . #helloWorld %~ f) home' diff --git a/marketing/counter.example b/marketing/counter.example new file mode 100644 index 00000000..727297d4 --- /dev/null +++ b/marketing/counter.example @@ -0,0 +1,9 @@ +view x = button + [ onClick (+ 1) ] + [ text $ "You have clicked " <> pack (show x) + <> " time" <> if x == 1 then "" else "s" + ] + +app = simple runSnabbdom 0 view getBody + +a95a8f21ab163dc9331e7f14E9a diff --git a/marketing/example.template.top b/marketing/example.template.top index ec484dd4..bae49935 100644 --- a/marketing/example.template.top +++ b/marketing/example.template.top @@ -7,6 +7,8 @@ {-# LANGUAGE TypeApplications #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ExtendedDefaultRules #-} +{-# OPTIONS_GHC -fno-warn-type-defaults #-} module Main where @@ -23,4 +25,3 @@ import Shpadoinkle.Html import Shpadoinkle.Html.Utils import Shpadoinkle.Run (runJSorWarp, simple) import Shpadoinkle - -- GitLab From c883c15279cf6d33c1d6616009068dbbb9e7ed3e Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 25 Mar 2021 15:31:10 -0600 Subject: [PATCH 003/116] nixops scripts --- .gitlab-ci.yml | 18 ++++++++++++++++++ nix/base.nix | 2 +- nix/deploy-isreal.sh | 6 ++++++ nix/test-isreal.sh | 6 ++++++ 4 files changed, 31 insertions(+), 1 deletion(-) create mode 100755 nix/deploy-isreal.sh create mode 100755 nix/test-isreal.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5f16922e..1a101ce6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -27,6 +27,14 @@ Packages GHC 8.6.5 on Linux: script: - nix-build --argstr compiler ghc865 --fallback +Isreal NixOps: + stage: Linux Build + needs: + - Check Style + - Hlint + script: + - nix-shell --command "./nix/test-isreal.sh" + Packages GHCJS 8.6 on Linux: stage: Linux Build needs: @@ -132,6 +140,16 @@ Marketing: # artifacts: # untracked: true +Isreal: + stage: Push Artifacts + needs: + - Packages GHCJS 8.6 on Linux + script: + - nix-shell --command "./nix/deploy-isreal.sh" + only: + - master + - isreal + pages: stage: Push Artifacts needs: diff --git a/nix/base.nix b/nix/base.nix index 0dbd6b82..dc1301ea 100644 --- a/nix/base.nix +++ b/nix/base.nix @@ -95,7 +95,7 @@ let inherit withHoogle; packages = _: if pack == "all" then attrValues packages else [ packages.${pack} ]; COMPILER = util.compilerjs; - buildInputs = ghcTools ++ [ ack util.cannibalize ]; + buildInputs = ghcTools ++ [ ack util.cannibalize nixops ]; shellHook = '' cat ${../etc/figlet} ./nix/hpackall.sh | grep generated diff --git a/nix/deploy-isreal.sh b/nix/deploy-isreal.sh new file mode 100755 index 00000000..5770ceee --- /dev/null +++ b/nix/deploy-isreal.sh @@ -0,0 +1,6 @@ +nixops destroy --all && nixops delete --all +which nixops; nixops --version +nixops create -d colorado isreal/deploy.nix +hash=$(cat ./chan.nix | tr -d '"') +nixops deploy -d colorado --allow-reboot -I nixpkgs="https://github.com/NixOS/nixpkgs/archive/$hash.tar.gz" +nixops destroy --all && nixops delete --all diff --git a/nix/test-isreal.sh b/nix/test-isreal.sh new file mode 100755 index 00000000..1dd8b454 --- /dev/null +++ b/nix/test-isreal.sh @@ -0,0 +1,6 @@ +nixops destroy --all && nixops delete --all +which nixops; nixops --version +nixops create -d tmp isreal/deploy.nix +hash=$(cat ./chan.nix | tr -d '"') +nixops deploy -d tmp --allow-reboot -I nixpkgs="https://github.com/NixOS/nixpkgs/archive/$hash.tar.gz" +nixops destroy --all && nixops delete --all -- GitLab From 1c1222a671bbe48457937647ee3c26d55c12dc82 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 25 Mar 2021 21:37:40 -0600 Subject: [PATCH 004/116] include token in cache bust --- isreal/Main.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/isreal/Main.hs b/isreal/Main.hs index 10a86752..c1b59b48 100644 --- a/isreal/Main.hs +++ b/isreal/Main.hs @@ -13,7 +13,7 @@ import Control.Monad.IO.Class (MonadIO (..)) import Data.ByteString.Lazy as BSL (writeFile) import Data.String (fromString) import Data.Text as T (Text, intercalate, unpack) -import Data.Text.Lazy as T (pack, unlines) +import Data.Text.Lazy as T (fromStrict, pack, unlines) import qualified Data.Text.Lazy.IO as T import Network.Wai.Handler.Warp (run) import Servant @@ -53,7 +53,9 @@ compile options@Options {..} snow nonce (Code code) = liftIO $ do , " " , " " , " " - , " " + , " " , " " , " " , " " -- GitLab From 3ac2b0d1250141da1baec027ea894f77e9a14b1c Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Fri, 26 Mar 2021 01:53:56 -0600 Subject: [PATCH 005/116] britney spears --- isreal/deploy.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isreal/deploy.nix b/isreal/deploy.nix index 908558a8..fe8898f5 100644 --- a/isreal/deploy.nix +++ b/isreal/deploy.nix @@ -3,7 +3,7 @@ isreal-swan = { deployment = { targetHost = "104.131.28.246"; - alwaysActivate = true; + alwaysActivatie = true; }; imports = [ ./deploy/configuration.nix -- GitLab From 54c018b02de0c5c5d1d5ebeda0700929e3cfced5 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Fri, 26 Mar 2021 01:57:26 -0600 Subject: [PATCH 006/116] the cure --- isreal/deploy.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isreal/deploy.nix b/isreal/deploy.nix index fe8898f5..908558a8 100644 --- a/isreal/deploy.nix +++ b/isreal/deploy.nix @@ -3,7 +3,7 @@ isreal-swan = { deployment = { targetHost = "104.131.28.246"; - alwaysActivatie = true; + alwaysActivate = true; }; imports = [ ./deploy/configuration.nix -- GitLab From 99e50c1351aacf36f146f6654bd4348629d286eb Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Fri, 26 Mar 2021 12:05:29 -0600 Subject: [PATCH 007/116] no more marketing docs seperation --- .gitlab-ci.yml | 30 +- README.adoc | 1 - docs/.gitignore | 2 - docs/antora-playbook.yml | 25 - docs/antora.yml | 5 - docs/default.nix | 69 --- docs/keybase.txt | 56 -- docs/modules/ROOT/assets/images/logo.png | Bin 102910 -> 0 bytes docs/ui/css/extra.css | 309 ----------- docs/ui/img/caution.svg | 8 - docs/ui/img/important.svg | 8 - docs/ui/img/note.svg | 8 - docs/ui/img/tip.svg | 8 - docs/ui/img/warning.svg | 8 - docs/ui/js/copy.js | 25 - docs/ui/partials/head-meta.html | 4 - docs/ui/partials/header-content.html | 28 - docs/ui/partials/header-meta.html | 2 - isreal/README.md | 8 + isreal/package.yaml | 2 +- marketing/Shpadoinkle/Marketing/Run.hs | 124 ----- nix/base.nix | 4 +- nix/build-pages.sh | 17 +- nix/overlay-shpadoinkle.nix | 4 +- snowman/LICENSE | 252 ++++++++- snowman/README.md | 6 +- {marketing => website}/CHANGELOG.md | 0 {marketing => website}/LICENSE | 2 +- {marketing => website}/README.md | 0 .../Shpadoinkle/Website}/Disembodied.hs | 28 +- website/Shpadoinkle/Website/Run.hs | 122 +++++ .../Shpadoinkle/Website}/Style.hs | 2 +- .../Shpadoinkle/Website}/Tailwind.hs | 2 +- .../Shpadoinkle/Website}/Types.hs | 49 +- .../Shpadoinkle/Website}/Types/Hoogle.hs | 2 +- .../Shpadoinkle/Website}/View.hs | 10 +- {marketing => website}/counter.example | 0 {marketing => website}/default.nix | 12 +- .../pages => website/docs}/comparison.adoc | 0 .../ROOT/pages => website/docs}/concepts.adoc | 0 .../adding-to-your-project.adoc | 0 .../getting-started/extend-an-example.adoc | 0 .../docs}/getting-started/index.adoc | 0 website/docs/getting-started/index.html | 122 +++++ .../ROOT/pages => website/docs}/index.adoc | 0 website/docs/index.html | 490 ++++++++++++++++++ {docs/modules/ROOT => website/docs}/nav.adoc | 0 .../docs}/packages/backends.adoc | 0 .../docs}/packages/console.adoc | 0 .../pages => website/docs}/packages/core.adoc | 0 .../pages => website/docs}/packages/html.adoc | 0 .../docs}/packages/index.adoc | 0 .../pages => website/docs}/packages/lens.adoc | 0 .../docs}/packages/router.adoc | 0 .../docs}/packages/widgets.adoc | 0 .../docs}/tutorial/calculator.adoc | 0 .../docs}/tutorial/composing.adoc | 0 .../docs}/tutorial/immediate-execution.adoc | 0 .../docs}/tutorial/index.adoc | 0 .../example.template.bottom | 0 {marketing => website}/example.template.top | 0 {marketing => website}/hello-world.example | 0 {marketing => website}/package.yaml | 33 +- {marketing => website}/static/logo.png | Bin {marketing => website}/static/style.css | 0 .../static/tailwind.min.css | 0 66 files changed, 1056 insertions(+), 831 deletions(-) delete mode 100644 docs/.gitignore delete mode 100644 docs/antora-playbook.yml delete mode 100644 docs/antora.yml delete mode 100644 docs/default.nix delete mode 100644 docs/keybase.txt delete mode 100644 docs/modules/ROOT/assets/images/logo.png delete mode 100644 docs/ui/css/extra.css delete mode 100644 docs/ui/img/caution.svg delete mode 100644 docs/ui/img/important.svg delete mode 100644 docs/ui/img/note.svg delete mode 100644 docs/ui/img/tip.svg delete mode 100644 docs/ui/img/warning.svg delete mode 100644 docs/ui/js/copy.js delete mode 100644 docs/ui/partials/head-meta.html delete mode 100644 docs/ui/partials/header-content.html delete mode 100644 docs/ui/partials/header-meta.html delete mode 100644 marketing/Shpadoinkle/Marketing/Run.hs rename {marketing => website}/CHANGELOG.md (100%) rename {marketing => website}/LICENSE (99%) rename {marketing => website}/README.md (100%) rename {marketing/Shpadoinkle/Marketing => website/Shpadoinkle/Website}/Disembodied.hs (66%) create mode 100644 website/Shpadoinkle/Website/Run.hs rename {marketing/Shpadoinkle/Marketing => website/Shpadoinkle/Website}/Style.hs (79%) rename {marketing/Shpadoinkle/Marketing => website/Shpadoinkle/Website}/Tailwind.hs (78%) rename {marketing/Shpadoinkle/Marketing => website/Shpadoinkle/Website}/Types.hs (71%) rename {marketing/Shpadoinkle/Marketing => website/Shpadoinkle/Website}/Types/Hoogle.hs (97%) rename {marketing/Shpadoinkle/Marketing => website/Shpadoinkle/Website}/View.hs (98%) rename {marketing => website}/counter.example (100%) rename {marketing => website}/default.nix (65%) rename {docs/modules/ROOT/pages => website/docs}/comparison.adoc (100%) rename {docs/modules/ROOT/pages => website/docs}/concepts.adoc (100%) rename {docs/modules/ROOT/pages => website/docs}/getting-started/adding-to-your-project.adoc (100%) rename {docs/modules/ROOT/pages => website/docs}/getting-started/extend-an-example.adoc (100%) rename {docs/modules/ROOT/pages => website/docs}/getting-started/index.adoc (100%) create mode 100644 website/docs/getting-started/index.html rename {docs/modules/ROOT/pages => website/docs}/index.adoc (100%) create mode 100644 website/docs/index.html rename {docs/modules/ROOT => website/docs}/nav.adoc (100%) rename {docs/modules/ROOT/pages => website/docs}/packages/backends.adoc (100%) rename {docs/modules/ROOT/pages => website/docs}/packages/console.adoc (100%) rename {docs/modules/ROOT/pages => website/docs}/packages/core.adoc (100%) rename {docs/modules/ROOT/pages => website/docs}/packages/html.adoc (100%) rename {docs/modules/ROOT/pages => website/docs}/packages/index.adoc (100%) rename {docs/modules/ROOT/pages => website/docs}/packages/lens.adoc (100%) rename {docs/modules/ROOT/pages => website/docs}/packages/router.adoc (100%) rename {docs/modules/ROOT/pages => website/docs}/packages/widgets.adoc (100%) rename {docs/modules/ROOT/pages => website/docs}/tutorial/calculator.adoc (100%) rename {docs/modules/ROOT/pages => website/docs}/tutorial/composing.adoc (100%) rename {docs/modules/ROOT/pages => website/docs}/tutorial/immediate-execution.adoc (100%) rename {docs/modules/ROOT/pages => website/docs}/tutorial/index.adoc (100%) rename {marketing => website}/example.template.bottom (100%) rename {marketing => website}/example.template.top (100%) rename {marketing => website}/hello-world.example (100%) rename {marketing => website}/package.yaml (71%) rename {marketing => website}/static/logo.png (100%) rename {marketing => website}/static/style.css (100%) rename {marketing => website}/static/tailwind.min.css (100%) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3f0f7086..00f87cdd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -109,38 +109,14 @@ Dev Shell GHCJS 8.6 on Darwin: script: - nix-shell --run "echo works" --argstr compiler ghc865 --argstr system x86_64-darwin --arg isJS true --fallback -Antora: - stage: Documentation - needs: - - Check Style - - Hlint - script: - - nix-build docs --fallback - -Marketing: +Website: stage: Documentation needs: - Check Style - Packages GHCJS 8.6 on Linux - Packages GHC 8.6.5 on Linux script: - - nix-build marketing --fallback - -# test-865-ghc: -# stage: test -# script: -# - nix-build test.nix --argstr compiler ghc865 --arg isJS false --fallback -# - rm -r result -# artifacts: -# untracked: true -# -# test-865-ghcjs: -# stage: test -# script: -# - nix-build test.nix --argstr compiler ghc865 --arg isJS true --fallback -# - rm -r result -# artifacts: -# untracked: true + - nix-build website --fallback Isreal: stage: Push Artifacts @@ -168,8 +144,6 @@ pages: - public only: - master - - docs - Upload to Cachix: stage: Push Artifacts diff --git a/README.adoc b/README.adoc index 3626bb76..e9592bb6 100644 --- a/README.adoc +++ b/README.adoc @@ -6,7 +6,6 @@ image::docs/modules/ROOT/assets/images/logo.png[Eddie is loved forever,150,150] https://gitlab.com/platonic/shpadoinkle/commits/master[image:https://gitlab.com/platonic/shpadoinkle/badges/master/pipeline.svg[pipeline status]] https://shpadoinkle.zulipchat.com/register[image:https://img.shields.io/badge/zulip-join_chat-orange.svg[Community]] -https://opensource.org/licenses/BSD-3-Clause[image:https://img.shields.io/badge/License-BSD%203--Clause-blue.svg[BSD-3]] https://builtwithnix.org[image:https://img.shields.io/badge/built%20with-nix-41439a[built with nix]] https://shpadoinkle.cachix.org[image:https://img.shields.io/badge/Cachix-up%20to%20date-green[cachix]] diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index d21fa07c..00000000 --- a/docs/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -public -theme diff --git a/docs/antora-playbook.yml b/docs/antora-playbook.yml deleted file mode 100644 index 93ced104..00000000 --- a/docs/antora-playbook.yml +++ /dev/null @@ -1,25 +0,0 @@ -site: - title: Shpadoinkle Documentation - url: / - start_page: docs::index.adoc -content: - sources: - - url: .. - start_path: ./docs - branches: HEAD -ui: - bundle: - url: ./theme - supplemental_files: ./ui -asciidoc: - attributes: - experimental: '' - sectlinks: '' - sectanchors: '' - hide-uri-scheme: '' - stem: true - icons: font - gitlab: https://gitlab.com/platonic/shpadoinkle/-/tree/master - hackage: https://hackage.haskell.org/package/Shpadoinkle -output: - dir: ./public diff --git a/docs/antora.yml b/docs/antora.yml deleted file mode 100644 index 6ec98524..00000000 --- a/docs/antora.yml +++ /dev/null @@ -1,5 +0,0 @@ -name: docs -title: Shpadoinkle Documentation -version: 'master' -nav: - - modules/ROOT/nav.adoc diff --git a/docs/default.nix b/docs/default.nix deleted file mode 100644 index e8719d89..00000000 --- a/docs/default.nix +++ /dev/null @@ -1,69 +0,0 @@ -{ chan ? "20.03" }: -let pkgs = import ../nix/pkgs.nix { inherit chan; }; -in -with pkgs; -let - - theme = fetchurl { - url = https://gitlab.com/robinbb/antora-ui-default/-/jobs/artifacts/Shpadoinkle/raw/build/ui-bundle.zip?job=bundle-stable; - sha256 = "1g2xkll0brjlwsyxgvsb6ff9dihmdaf4hix4mc2b7bmaf9fgay19"; - }; - - - util = import ../nix/util.nix { inherit pkgs; }; - - src = util.gitignore [ - "*.md" - "*.nix" - "*/**.hs" - "*.ghc*" - "**/package.yml" - "*.cabal" - "*dist*" - "*result*" - "*.md" - "keybase*" - ] ../.; - -in -stdenv.mkDerivation { - - name = "Shpadoinkle-documentation"; - - buildInputs = [ antora inotify-tools git ]; - - shellHook = '' - SHPADOINKLE_TOP="$(git rev-parse --show-toplevel)" - SHPADOINKLE_DOCS="$SHPADOINKLE_TOP"/docs - cat "$SHPADOINKLE_TOP"/etc/figlet - rm -rf "$SHPADOINKLE_DOCS"/theme - ln -s ${theme} "$SHPADOINKLE_DOCS"/theme - function serve-docs() ( - set -euo pipefail - echo "Building initial docs..." - cd "$SHPADOINKLE_DOCS" - antora antora-playbook - echo "Serving on port 8080..." - ${haskellPackages.wai-app-static}/bin/warp -d public -p 8080 & - echo "Watching for changes..." - while inotifywait -e modify -r .; do antora antora-playbook; done - ) - echo "" - echo "Build and serve docs by running" - echo "serve-docs" - ''; - - buildCommand = '' - set -euo pipefail - mkdir "$out" - HOME="$PWD" - export HOME - cp -r "${src}"/. . - chmod +w ./docs - ln -s "${theme}" ./docs/theme - cd docs - antora antora-playbook - cp -r public/. "$out" - ''; - -} diff --git a/docs/keybase.txt b/docs/keybase.txt deleted file mode 100644 index c23b7da0..00000000 --- a/docs/keybase.txt +++ /dev/null @@ -1,56 +0,0 @@ -================================================================== -https://keybase.io/fresheyeball --------------------------------------------------------------------- - -I hereby claim: - - * I am an admin of https://Shpadoinkle.org - * I am fresheyeball (https://keybase.io/fresheyeball) on keybase. - * I have a public key ASAW_kJQ9MCVx8q5IgY7_oKz8GSy8M9M5M9J3-RsGZIv3wo - -To do so, I am signing this object: - -{ - "body": { - "key": { - "eldest_kid": "0120fd5f1b1e1b7d8163fe90b4c426b1f7dff5c9878b1a35b29bf5ccb6d5071d7e2b0a", - "host": "keybase.io", - "kid": "012016fe4250f4c095c7cab922063bfe82b3f064b2f0cf4ce4cf49dfe46c19922fdf0a", - "uid": "0d25a821296d8aa10b2cb6e7e4ae2119", - "username": "fresheyeball" - }, - "merkle_root": { - "ctime": 1608662200, - "hash": "a666500fc01da0b494570a409ed1769a644356ea2374091c8e85397f9a2a9264201fc1fda1ff58fefe4865db2e2b919501e485362d7894aec1bc245150697f2e", - "hash_meta": "fd81b897c1bb0f717ad189419ecd7fb36c75a267214658180f92d0656703ccb6", - "seqno": 18478745 - }, - "service": { - "entropy": "gYF78QZ9uOBHqTVnQG9iVYQa", - "hostname": "Shpadoinkle.org", - "protocol": "https:" - }, - "type": "web_service_binding", - "version": 2 - }, - "client": { - "name": "keybase.io go client", - "version": "5.5.0" - }, - "ctime": 1608662236, - "expire_in": 504576000, - "prev": "3f992af92bb272af327eb0c6073215fff99769b586349bd4b1e1225a94d1f240", - "seqno": 85, - "tag": "signature" -} - -which yields the signature: - -hKRib2R5hqhkZXRhY2hlZMOpaGFzaF90eXBlCqNrZXnEIwEgFv5CUPTAlcfKuSIGO/6Cs/BksvDPTOTPSd/kbBmSL98Kp3BheWxvYWTESpcCVcQgP5kq+Suycq8yfrDGBzIV//mXabWGNJvUseEiWpTR8kDEIESkpF0/VJ8NRvYmPapgDKgMe0nb7KE+57mvfwefMmT1AgHCo3NpZ8RAIUlGGlR6ml7TB8lEsC0tR3bZBaXSsD+FuW04Yee6CV+iMeuYVJm6DGW2h3z9YekCR9sJ1FbaT9tWrZhVOyPyCahzaWdfdHlwZSCkaGFzaIKkdHlwZQildmFsdWXEIB9v/kUGkEacZ6ELj2ONn5hxdX2DzhY/U1ZHf0pfI7E4o3RhZ80CAqd2ZXJzaW9uAQ== - -And finally, I am proving ownership of this host by posting or -appending to this document. - -View my publicly-auditable identity here: https://keybase.io/fresheyeball - -================================================================== diff --git a/docs/modules/ROOT/assets/images/logo.png b/docs/modules/ROOT/assets/images/logo.png deleted file mode 100644 index 987aa59e9762c6ca48c6e603fee18a4952da20a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102910 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 diff --git a/docs/ui/css/extra.css b/docs/ui/css/extra.css deleted file mode 100644 index 3f692b81..00000000 --- a/docs/ui/css/extra.css +++ /dev/null @@ -1,309 +0,0 @@ -.doc .admonitionblock .icon { - border-radius: 1rem; -} - -i.fa[class^='icon-'], -i.fa[class*=' icon-']::before { - content: ""; - height: 1.25rem; - width: 1.25rem; - margin-right: 0.25rem; - margin-left: -0.5rem; -} - -i.fa.icon-note::before { - background: no-repeat url("../img/note.svg"); -} - -i.fa.icon-tip::before { - background: no-repeat url("../img/tip.svg"); -} - -i.fa.icon-important::before { - background: no-repeat url("../img/important.svg"); -} - -i.fa.icon-warning::before { - background: no-repeat url("../img/warning.svg"); -} - -i.fa.icon-caution::before { - background: no-repeat url("../img/caution.svg"); -} - -.admonitionblock{ - border: 1px dashed; -} - -.admonitionblock.note{ - border-color: #217ee7; -} - -.admonitionblock.warning{ - border-color: #e18114; -} - -.admonitionblock.tip{ - border-color: #41af46; -} - -.admonitionblock.caution{ - border-color: #a0439c; -} - -.doc .admonitionblock td.content{ - background: none; -} - -.doc pre.highlight code, .doc pre:not(.highlight){ - border: 1px solid #fafafa; -} - -.conum[data-value]{ - background: rgb(51, 51, 88); - color: white; -} - -code{ - font-family: Fira Code, Consolas, Monoid, Hack, Droid Sans, Roboto Mono, monospace !important; -} - -.doc p code{ - background: rgb(27,29,30); - color: rgb(211,207,201); - padding: 0 4px; - border-radius: 3px; - box-shadow: 0 0 1.75px rgba(0,0,0,0.4); -} - -.edit-this-page{ - display:none; -} - -.home h1{ - letter-spacing:10px; - text-align:center; - margin-bottom:0; - text-transform: uppercase; -} - -.nav-menu h3.title{ - display: none; -} - -.breadcrumbs li:first-child, .nav-panel-explore, .home-link{ - display: none; -} - -.toolbar{ - opacity: 0; - pointer-events: none; - height: 1.2rem; -} - -.navbar, footer.footer{ - background: #131415; -} - -.nav, .toolbar{ - background: #131415; - color: rgb(173,166,156); -} - -.content, body{ - background: #181a1b; -} - -.doc h2:not(.discrete) { - border-bottom-color: rgb(56, 61, 64); -} - -.doc, .doc h1, .doc h2, .doc h3, .doc h4, .doc h5, .doc h6{ - color: rgb(200,195,188); -} - -.doc a{ - color: rgb(94, 171, 237); -} - -.doc code { - background-image: initial; - background-color: rgb(27, 29, 30); - box-shadow: rgba(0, 0, 0, 0.4) 0px 0px 1.75px; - padding: 0 4px; - border-color: rgb(49, 54, 56); -} - -.doc pre.highlight code, .doc pre:not(.highlight) { - border-color: rgb(49, 54, 56); -} - -.doc .listingblock pre:not(.highlight), .doc .literalblock pre, .doc pre.highlight code { - background-image: initial; - background-color: rgb(27, 29, 30); - box-shadow: rgb(41, 44, 46) 0px 0px 1.75px inset; -} - -.hljs-class .hljs-title, .hljs-type { - color: rgb(139, 166, 197); -} - -.hljs-keyword, .hljs-selector-tag, .hljs-subst { - color: rgb(200, 195, 188); -} - -.hljs-section, .hljs-selector-id, .hljs-title { - color: rgb(255, 97, 97); -} - -.hljs-doctag, .hljs-string { - color: rgb(239, 53, 100); -} - -.hljs-literal, .hljs-number, .hljs-tag .hljs-attr, .hljs-template-variable, .hljs-variable { - color: rgb(114, 255, 255); -} - -.doc table.tableblock > :not(thead) th, .doc table.tableblock td { - border-top-color: rgb(56, 61, 64); - border-bottom-color: rgb(56, 61, 64); -} - -table { - border-color: #545b5e; -} - - -.doc table.tableblock thead th { - border-bottom-color: rgb(56, 61, 64); -} - -::-webkit-scrollbar { - background-color: #202324; - color: #aba499; -} - -::-webkit-scrollbar-corner { - background-color: #181a1b; -} - -::-webkit-scrollbar-thumb { - background-color: #454a4d; -} - -::selection { - background-color: #004daa !important; - color: #e8e6e3 !important; -} - -.toc-menu { - color: rgb(173, 166, 156); -} - -.sidebar.toc .toc-menu a:focus { - background-image: initial; - background-color: rgb(27, 29, 30); -} - -.toc .toc-menu h3 { - color: rgb(200, 195, 188); -} - -.toc .toc-menu ul { - list-style-image: initial; -} - -.toc .toc-menu a.is-active { - border-left-color: rgb(19, 91, 172); - color: rgb(200, 195, 188); -} - -.toc .toc-menu a { - color: inherit; - border-left-color: rgb(56, 61, 64); - text-decoration-color: initial; -} - -.toc .toc-menu a:hover { - color: rgb(94, 171, 237); -} - -@media screen and (min-width: 769px){ - .doc>h1.page:first-child{ - margin-top: 0; - } -} - -.doc .quoteblock, .doc .verseblock { - background-image: initial; - background-color: rgb(27, 29, 30); - border-left-color: rgb(109, 102, 90); - color: rgb(173, 166, 156); -} - -.hljs-built_in, .hljs-builtin-name { - color: rgb(79, 211, 255); -} - -.doc .admonitionblock .title, .doc .exampleblock .title, .doc .imageblock .title, .doc .listingblock .title, .doc .literalblock .title, .doc .openblock .title, .doc .tableblock caption { - color: rgb(173, 166, 156); -} - -.hljs-attribute, .hljs-name, .hljs-tag { - color: rgb(127, 174, 255); -} - -@media screen and (max-width: 1023px){ - .navbar-menu { - background-image: initial; - background-color: rgb(24, 26, 27); - } -} - -.navbar-burger{ - color: rgb(200,195,188); -} - -.nav-container{ - position: static; - visibility: visible; - width: 100%; -} - -.nav{ - top: 0; - height: auto; -} - -.nav-panel-menu { - height: auto; -} - -@media screen and (min-width: 1024px){ - .nav{ - top: 3.5rem; - height: calc(100vh - 3.5rem); - } - .nav-panel-menu { - height: 100%; - } - .nav-container { - width: 15rem; - /* position: static; */ - } -} - -hr{ - border: 0; - border-bottom: 1px dashed #6b6b6b; -} - -@media screen and (max-width: 1023px){ - .navbar-menu .navbar-link:hover, .navbar-menu a.navbar-item:hover{ - background: #202325; - } -} - -.language-bash{ - cursor: copy; -} diff --git a/docs/ui/img/caution.svg b/docs/ui/img/caution.svg deleted file mode 100644 index 98c94214..00000000 --- a/docs/ui/img/caution.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/docs/ui/img/important.svg b/docs/ui/img/important.svg deleted file mode 100644 index 3ddcc813..00000000 --- a/docs/ui/img/important.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/docs/ui/img/note.svg b/docs/ui/img/note.svg deleted file mode 100644 index 7d778539..00000000 --- a/docs/ui/img/note.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/docs/ui/img/tip.svg b/docs/ui/img/tip.svg deleted file mode 100644 index 1405622e..00000000 --- a/docs/ui/img/tip.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/docs/ui/img/warning.svg b/docs/ui/img/warning.svg deleted file mode 100644 index 3ddcc813..00000000 --- a/docs/ui/img/warning.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/docs/ui/js/copy.js b/docs/ui/js/copy.js deleted file mode 100644 index 036afc92..00000000 --- a/docs/ui/js/copy.js +++ /dev/null @@ -1,25 +0,0 @@ -var inited = false -function init(){ - if(inited) return - inited = true - // const codes = document.getElementsByTagName("code") - const codes = document.getElementsByClassName("language-bash") - for(i = 0; i < codes.length; i++){ copyCode(codes[i]) } -} -init() -window.addEventListener("load", init) - -function copyCode(code){ - code.title = "Click To Copy" - code.addEventListener("click", () => { - navigator.clipboard.writeText(code.textContent) - // const textArea = document.createElement('textarea') - // textArea.style.position = "absolute" - // textArea.style.left = "-99999px" - // textArea.textContent = code.textContent - // document.body.append(textArea) - // textArea.select() - // document.execCommand("copy") - // document.body.removeChild(textArea) - }) -} diff --git a/docs/ui/partials/head-meta.html b/docs/ui/partials/head-meta.html deleted file mode 100644 index 334e7df8..00000000 --- a/docs/ui/partials/head-meta.html +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/docs/ui/partials/header-content.html b/docs/ui/partials/header-content.html deleted file mode 100644 index 073e4823..00000000 --- a/docs/ui/partials/header-content.html +++ /dev/null @@ -1,28 +0,0 @@ -

diff --git a/docs/ui/partials/header-meta.html b/docs/ui/partials/header-meta.html deleted file mode 100644 index 5af1fc64..00000000 --- a/docs/ui/partials/header-meta.html +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/isreal/README.md b/isreal/README.md index 0f13daf0..eb319fc4 100644 --- a/isreal/README.md +++ b/isreal/README.md @@ -1,5 +1,13 @@ # Isreal Swan +[![Goldwater](https://gitlab.com/platonic/shpadoinkle/badges/master/pipeline.svg)](https://gitlab.com/platonic/shpadoinkle) +[![Haddock](https://img.shields.io/badge/haddock-master-informational)](https://shpadoinkle.org/isreal) +[![GPL-3](https://img.shields.io/badge/License-GPL%203--Clause-blue.svg)](https://opensource.org/licenses/gpl-3.0.html) +[![built with nix](https://img.shields.io/badge/built%20with-nix-41439a)](https://builtwithnix.org) +[![Hackage](https://img.shields.io/hackage/v/Shpadoinkle-isreal.svg)](https://hackage.haskell.org/package/Shpadoinkle-html) +[![Hackage Deps](https://img.shields.io/hackage-deps/v/Shpadoinkle-isreal.svg)](http://packdeps.haskellers.com/reverse/Shpadoinkle-html) +[![Hackage CI](https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle-isreal/badge)](https://matrix.hackage.haskell.org/#/package/Shpadoinkle-html) + Snowman as a service > If you build me a snowman, then I'll build one for you. diff --git a/isreal/package.yaml b/isreal/package.yaml index 83a01ccf..1d9732b4 100644 --- a/isreal/package.yaml +++ b/isreal/package.yaml @@ -1,5 +1,5 @@ name: Shpadoinkle-isreal -license: BSD3 +license: GPL-3 license-file: LICENSE version: 0.0.0.1 author: Isaac Shapira diff --git a/marketing/Shpadoinkle/Marketing/Run.hs b/marketing/Shpadoinkle/Marketing/Run.hs deleted file mode 100644 index bc7d75fb..00000000 --- a/marketing/Shpadoinkle/Marketing/Run.hs +++ /dev/null @@ -1,124 +0,0 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE ExplicitNamespaces #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeApplications #-} - - -module Main where - - -import Control.Monad.Catch (MonadThrow) -import Control.Monad.IO.Class (MonadIO (..)) -import Control.Monad.Reader -import Data.Proxy (Proxy (..)) -import Data.Text (Text) -import Servant.API (type (:<|>) ((:<|>))) -#ifndef ghcjs_HOST_OS -import Servant.Server as Servant (serve) -#endif - -#ifndef ghcjs_HOST_OS -import Shpadoinkle (JSM, MonadJSM, - MonadUnliftIO (..), TVar, - constUpdate, liftJSM, - newTVarIO) -#else -import Shpadoinkle (JSM, MonadUnliftIO (..), - TVar, constUpdate, liftJSM, - newTVarIO) -#endif -import Shpadoinkle.Backend.Snabbdom (runSnabbdom, stage) -import Shpadoinkle.Isreal.Types as Swan (API, Code, - CompileError, - SnowNonce, - SnowToken (..)) -import Shpadoinkle.Router (fullPageSPA', - withHydration) -import Shpadoinkle.Router.Client (BaseUrl (..), - ClientEnv (..), ClientM, - Scheme (Https), client, - runXHR') -import Shpadoinkle.Widgets.Types (Search (..)) -#ifndef ghcjs_HOST_OS -import Shpadoinkle.Router.Server (serveUI) -import Shpadoinkle.Run (Env (Dev), liveWithBackend, - runJSorWarp) -#else -import Shpadoinkle.Run (runJSorWarp) -#endif - -import Shpadoinkle.DeveloperTools -import Shpadoinkle.Marketing.Types (Frontend, Hooglable (..), - HoogleAPI, - Route (FourOhFourR), SPA, - Swan (..), routes) -import Shpadoinkle.Marketing.Types.Hoogle (Target) -#ifndef ghcjs_HOST_OS -import Shpadoinkle.Marketing.View (start, startJS, template, - view) -#else -import Shpadoinkle.Marketing.View (start, startJS, view) -#endif - - -newtype App a = App { runApp :: ReaderT (TVar (Maybe Code)) JSM a } - deriving (Functor, Applicative, Monad, MonadIO, MonadThrow, MonadReader (TVar (Maybe Code))) -#ifndef ghcjs_HOST_OS - deriving (MonadJSM) -#endif - - -instance MonadUnliftIO App where - withRunInIO inner = App $ ReaderT $ \r -> - withRunInIO $ \run -> inner (run . flip runReaderT r . runApp) - - -isrealEnv, hoogleEnv :: ClientEnv -isrealEnv = ClientEnv $ BaseUrl Https "isreal.shpadoinkle.org" 443 "" -hoogleEnv = ClientEnv $ BaseUrl Https "hoogle.shpadoinkle.org" 443 "" - - -instance Swan App where - compile t n c = liftJSM $ runXHR' (compileM t n c) isrealEnv - clean t = liftJSM $ runXHR' (cleanM t) isrealEnv - - -instance Hooglable App where - findTargets s = liftJSM $ runXHR' (findTargetsM s) hoogleEnv - - -compileM :: SnowToken -> SnowNonce -> Code -> ClientM (Either CompileError Text) -cleanM :: SnowToken -> ClientM Text -(_ :<|> compileM :<|> cleanM :<|> _) - = client (Proxy @ Swan.API) - - -findTargetsM :: Search -> ClientM [Target] -findTargetsM (Search s) = client (Proxy @ HoogleAPI) (Just "json") (Just s) (Just 1) (Just 7) - - -app :: JSM () -app = do - mutex <- newTVarIO Nothing - model :: TVar Frontend <- newTVarIO =<< runReaderT (runApp (start FourOhFourR)) mutex - withDeveloperTools model - fullPageSPA' @(SPA JSM) (flip runReaderT mutex . runApp) runSnabbdom model (withHydration startJS) view stage - (fmap constUpdate . startJS) routes - - -main :: IO () -main = runJSorWarp 8080 app - - -#ifndef ghcjs_HOST_OS - -dev :: IO () -dev = liveWithBackend 8080 app . pure $ Servant.serve (Proxy @ (SPA IO)) $ serveUI @ (SPA IO) "." (\r -> do - x <- start r - return $ template @App Dev x (view x)) routes - -#endif diff --git a/nix/base.nix b/nix/base.nix index dc1301ea..7132fb83 100644 --- a/nix/base.nix +++ b/nix/base.nix @@ -64,8 +64,8 @@ let Shpadoinkle-template Shpadoinkle-tests; - Shpadoinkle-examples = cannibal haskell.packages.${util.compilerjs}.Shpadoinkle-examples; - Shpadoinkle-marketing = cannibal haskell.packages.${util.compilerjs}.Shpadoinkle-marketing; + Shpadoinkle-examples = cannibal haskell.packages.${util.compilerjs}.Shpadoinkle-examples; + Shpadoinkle-website = cannibal haskell.packages.${util.compilerjs}.Shpadoinkle-website; } // ( diff --git a/nix/build-pages.sh b/nix/build-pages.sh index bf9b8fe2..12ddf077 100755 --- a/nix/build-pages.sh +++ b/nix/build-pages.sh @@ -8,13 +8,6 @@ mkdir -p public/examples cp -r result/bin/*.jsexe public/examples rm result* -echo "Building Antora docs" -nix-build docs --fallback - -echo "Copying in Antora docs" -cp -r result/* public -rm result* - echo "Building Haddocks and copying in" ./nix/fish.sh rm result* @@ -22,11 +15,11 @@ rm result* echo "Proving to Keybase" cp docs/keybase.txt public/keybase.txt -# echo "Building Marketing" -# nix-build marketing --fallback +echo "Building Website" +nix-build website --fallback -# echo "Copying in Marketing" -# cp -r result/* public -# rm result* +echo "Copying in Website" +cp -r result/* public +rm result* chmod -R 755 public/* diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index 6a20b86b..c88536a6 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -154,10 +154,10 @@ in { Shpadoinkle-backend-static = call "Shpadoinkle-backend-static" ../backends/static; Shpadoinkle-backend-pardiff = call "Shpadoinkle-backend-pardiff" ../backends/pardiff; Shpadoinkle-console = call "Shpadoinkle-console" ../console; - Shpadoinkle-developer-tools = addDev (call "Shpadoinkle-developer-tools" ../developer-tools); + Shpadoinkle-developer-tools = addDev (call "Shpadoinkle-developer-tools" ../developer-tools); Shpadoinkle-disembodied = call "Shpadoinkle-disembodied" ../disembodied; Shpadoinkle-lens = call "Shpadoinkle-lens" ../lens; - Shpadoinkle-marketing = call "Shpadoinkle-marketing" ../marketing; + Shpadoinkle-website = call "Shpadoinkle-website" ../website; Shpadoinkle-html = call "Shpadoinkle-html" ../html; Shpadoinkle-router = call "Shpadoinkle-router" ../router; Shpadoinkle-streaming = call "Shpadoinkle-streaming" ../streaming; diff --git a/snowman/LICENSE b/snowman/LICENSE index 87b3af29..ccce70c1 100644 --- a/snowman/LICENSE +++ b/snowman/LICENSE @@ -1,27 +1,225 @@ -Shpadoinkle Snowman aka S11 Core -Copyright © 2020 Isaac Shapira -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Shpadoinkle nor the names of its contributors may be - used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. +Platonic Systems Limited, hereby disclaims all copyright interest in the program “Shpadoinkle-snowman” written by Isaac Shapira. + +signature of Isaac Shapira 03-19-2021 +Isaac Shapira, Principal + + +GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright © 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +Preamble +The GNU General Public License is a free, copyleft license for software and other kinds of works. + +The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS +0. Definitions. +“This License” refers to version 3 of the GNU General Public License. + +“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. + +“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. + +To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. + +A “covered work” means either the unmodified Program or a work based on the Program. + +To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. + +To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. + +1. Source Code. +The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. + +A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. + +The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +2. Basic Permissions. +All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. + +3. Protecting Users' Legal Rights From Anti-Circumvention Law. +No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. + +4. Conveying Verbatim Copies. +You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. + +5. Conveying Modified Source Versions. +You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: + +a) The work must carry prominent notices stating that you modified it, and giving a relevant date. +b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. +c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. +d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. +A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. + +6. Conveying Non-Source Forms. +You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: + +a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. +b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. +c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. +d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. +e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. +A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. + +A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). + +The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. + +7. Additional Terms. +“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: + +a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or +b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or +c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or +d) Limiting the use for publicity purposes of names of licensors or authors of the material; or +e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or +f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. +All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. + +8. Termination. +You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. + +9. Acceptance Not Required for Having Copies. +You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. + +10. Automatic Licensing of Downstream Recipients. +Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. + +An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. + +11. Patents. +A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. + +In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. + +A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. + +12. No Surrender of Others' Freedom. +If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. + +13. Use with the GNU Affero General Public License. +Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. + +14. Revised Versions of this License. +The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. + +Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. + +15. Disclaimer of Warranty. +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. Limitation of Liability. +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +17. Interpretation of Sections 15 and 16. +If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. +The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. + +You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . + +The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . diff --git a/snowman/README.md b/snowman/README.md index a4ea96d7..e85e019d 100644 --- a/snowman/README.md +++ b/snowman/README.md @@ -1,5 +1,9 @@ # Snowman +[![Goldwater](https://gitlab.com/platonic/shpadoinkle/badges/master/pipeline.svg)](https://gitlab.com/platonic/shpadoinkle) +[![GPL-3](https://img.shields.io/badge/License-GPL%203--Clause-blue.svg)](https://opensource.org/licenses/gpl-3.0.html) +[![built with nix](https://img.shields.io/badge/built%20with-nix-41439a)](https://builtwithnix.org) + ☃️ ⟶ 🥔 ☃️ ### We can make him tall. Or we can make him not so tall... @@ -71,4 +75,4 @@ nix-shell --arg withHoogle true --command "hoogle serve" ### License -The code to generate a snowman is BSD licensed like the rest of this project. However the code generated on your local machine is CC0. +The code to generate a snowman is GPL-3 licensed. However the code generated on your local machine is CC0. diff --git a/marketing/CHANGELOG.md b/website/CHANGELOG.md similarity index 100% rename from marketing/CHANGELOG.md rename to website/CHANGELOG.md diff --git a/marketing/LICENSE b/website/LICENSE similarity index 99% rename from marketing/LICENSE rename to website/LICENSE index bb1ccf03..9f12b929 100644 --- a/marketing/LICENSE +++ b/website/LICENSE @@ -1,4 +1,4 @@ -Platonic Systems Limited, hereby disclaims all copyright interest in the program “Shpadoinkle-marketing” written by Isaac Shapira. +Platonic Systems Limited, hereby disclaims all copyright interest in the program “Shpadoinkle-website” written by Isaac Shapira. signature of Isaac Shapira 03-19-2021 Isaac Shapira, Principal diff --git a/marketing/README.md b/website/README.md similarity index 100% rename from marketing/README.md rename to website/README.md diff --git a/marketing/Shpadoinkle/Marketing/Disembodied.hs b/website/Shpadoinkle/Website/Disembodied.hs similarity index 66% rename from marketing/Shpadoinkle/Marketing/Disembodied.hs rename to website/Shpadoinkle/Website/Disembodied.hs index 0c7d3c74..bfa329f8 100644 --- a/marketing/Shpadoinkle/Marketing/Disembodied.hs +++ b/website/Shpadoinkle/Website/Disembodied.hs @@ -14,22 +14,22 @@ module Main where -import Control.Lens (Prism', re, (^.)) +import Control.Lens (Prism', re, (^.)) import Control.Monad.IO.Class import Control.Monad.Reader -import Data.Generics.Labels () -import Servant.API (type (:<|>) ((:<|>))) -import System.Environment (getArgs) - -import Shpadoinkle (Html, JSM, MonadJSM, TVar) -import Shpadoinkle.Disembodied (Disembodied (SiteSpec), writeSite) -import Shpadoinkle.Isreal.Types (Code, SnowToken) -import Shpadoinkle.Lens (onSum) -import Shpadoinkle.Run (Env (Prod)) - -import Shpadoinkle.Marketing.Types -import Shpadoinkle.Marketing.View (comparisons, fourOhFour, - genExampleTokens, home, template) +import Data.Generics.Labels () +import Servant.API (type (:<|>) ((:<|>))) +import System.Environment (getArgs) + +import Shpadoinkle (Html, JSM, MonadJSM, TVar) +import Shpadoinkle.Disembodied (Disembodied (SiteSpec), writeSite) +import Shpadoinkle.Isreal.Types (Code, SnowToken) +import Shpadoinkle.Lens (onSum) +import Shpadoinkle.Run (Env (Prod)) + +import Shpadoinkle.Website.Types +import Shpadoinkle.Website.View (comparisons, fourOhFour, + genExampleTokens, home, template) newtype Noop a = Noop (JSM a) deriving newtype (Functor, Applicative, Monad, MonadJSM, MonadIO) diff --git a/website/Shpadoinkle/Website/Run.hs b/website/Shpadoinkle/Website/Run.hs new file mode 100644 index 00000000..779b8533 --- /dev/null +++ b/website/Shpadoinkle/Website/Run.hs @@ -0,0 +1,122 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE ExplicitNamespaces #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} + + +module Main where + + +import Control.Monad.Catch (MonadThrow) +import Control.Monad.IO.Class (MonadIO (..)) +import Control.Monad.Reader +import Data.Proxy (Proxy (..)) +import Data.Text (Text) +import Servant.API (type (:<|>) ((:<|>))) +#ifndef ghcjs_HOST_OS +import Servant.Server as Servant (serve) +#endif + +#ifndef ghcjs_HOST_OS +import Shpadoinkle (JSM, MonadJSM, + MonadUnliftIO (..), TVar, + constUpdate, liftJSM, + newTVarIO) +#else +import Shpadoinkle (JSM, MonadUnliftIO (..), + TVar, constUpdate, liftJSM, + newTVarIO) +#endif +import Shpadoinkle.Backend.Snabbdom (runSnabbdom, stage) +import Shpadoinkle.Isreal.Types as Swan (API, Code, + CompileError, + SnowNonce, + SnowToken (..)) +import Shpadoinkle.Router (fullPageSPA', withHydration) +import Shpadoinkle.Router.Client (BaseUrl (..), ClientEnv (..), + ClientM, Scheme (Https), + client, runXHR') +import Shpadoinkle.Widgets.Types (Search (..)) +#ifndef ghcjs_HOST_OS +import Shpadoinkle.Router.Server (serveUI) +import Shpadoinkle.Run (Env (Dev), liveWithBackend, + runJSorWarp) +#else +import Shpadoinkle.Run (runJSorWarp) +#endif + +import Shpadoinkle.DeveloperTools +import Shpadoinkle.Website.Types (Frontend, Hooglable (..), + HoogleAPI, + Route (FourOhFourR), SPA, + Swan (..), routes) +import Shpadoinkle.Website.Types.Hoogle (Target) +#ifndef ghcjs_HOST_OS +import Shpadoinkle.Website.View (start, startJS, template, + view) +#else +import Shpadoinkle.Website.View (start, startJS, view) +#endif + + +newtype App a = App { runApp :: ReaderT (TVar (Maybe Code)) JSM a } + deriving (Functor, Applicative, Monad, MonadIO, MonadThrow, MonadReader (TVar (Maybe Code))) +#ifndef ghcjs_HOST_OS + deriving (MonadJSM) +#endif + + +instance MonadUnliftIO App where + withRunInIO inner = App $ ReaderT $ \r -> + withRunInIO $ \run -> inner (run . flip runReaderT r . runApp) + + +isrealEnv, hoogleEnv :: ClientEnv +isrealEnv = ClientEnv $ BaseUrl Https "isreal.shpadoinkle.org" 443 "" +hoogleEnv = ClientEnv $ BaseUrl Https "hoogle.shpadoinkle.org" 443 "" + + +instance Swan App where + compile t n c = liftJSM $ runXHR' (compileM t n c) isrealEnv + clean t = liftJSM $ runXHR' (cleanM t) isrealEnv + + +instance Hooglable App where + findTargets s = liftJSM $ runXHR' (findTargetsM s) hoogleEnv + + +compileM :: SnowToken -> SnowNonce -> Code -> ClientM (Either CompileError Text) +cleanM :: SnowToken -> ClientM Text +(_ :<|> compileM :<|> cleanM :<|> _) + = client (Proxy @ Swan.API) + + +findTargetsM :: Search -> ClientM [Target] +findTargetsM (Search s) = client (Proxy @ HoogleAPI) (Just "json") (Just s) (Just 1) (Just 7) + + +app :: JSM () +app = do + mutex <- newTVarIO Nothing + model :: TVar Frontend <- newTVarIO =<< runReaderT (runApp (start FourOhFourR)) mutex + withDeveloperTools model + fullPageSPA' @(SPA JSM) (flip runReaderT mutex . runApp) runSnabbdom model (withHydration startJS) view stage + (fmap constUpdate . startJS) routes + + +main :: IO () +main = runJSorWarp 8080 app + + +#ifndef ghcjs_HOST_OS + +dev :: IO () +dev = liveWithBackend 8080 app . pure $ Servant.serve (Proxy @ (SPA IO)) $ serveUI @ (SPA IO) "." (\r -> do + x <- start r + return $ template @App Dev x (view x)) routes + +#endif diff --git a/marketing/Shpadoinkle/Marketing/Style.hs b/website/Shpadoinkle/Website/Style.hs similarity index 79% rename from marketing/Shpadoinkle/Marketing/Style.hs rename to website/Shpadoinkle/Website/Style.hs index 2856e63f..c50131aa 100644 --- a/marketing/Shpadoinkle/Marketing/Style.hs +++ b/website/Shpadoinkle/Website/Style.hs @@ -2,7 +2,7 @@ {-# LANGUAGE TemplateHaskell #-} -module Shpadoinkle.Marketing.Style where +module Shpadoinkle.Website.Style where import Shpadoinkle.Html.TH.CSS diff --git a/marketing/Shpadoinkle/Marketing/Tailwind.hs b/website/Shpadoinkle/Website/Tailwind.hs similarity index 78% rename from marketing/Shpadoinkle/Marketing/Tailwind.hs rename to website/Shpadoinkle/Website/Tailwind.hs index 9f3e74c2..7e014fd4 100644 --- a/marketing/Shpadoinkle/Marketing/Tailwind.hs +++ b/website/Shpadoinkle/Website/Tailwind.hs @@ -2,7 +2,7 @@ {-# LANGUAGE TemplateHaskell #-} -module Shpadoinkle.Marketing.Tailwind where +module Shpadoinkle.Website.Tailwind where import Shpadoinkle.Html.TH.CSS diff --git a/marketing/Shpadoinkle/Marketing/Types.hs b/website/Shpadoinkle/Website/Types.hs similarity index 71% rename from marketing/Shpadoinkle/Marketing/Types.hs rename to website/Shpadoinkle/Website/Types.hs index b088687d..28b294a5 100644 --- a/marketing/Shpadoinkle/Marketing/Types.hs +++ b/website/Shpadoinkle/Website/Types.hs @@ -12,36 +12,35 @@ {-# LANGUAGE TypeOperators #-} -module Shpadoinkle.Marketing.Types where +module Shpadoinkle.Website.Types where -import Control.Lens ((^.)) -import Control.Monad.Except (MonadTrans (..)) +import Control.Lens ((^.)) +import Control.Monad.Except (MonadTrans (..)) import Control.Monad.Reader -import Data.Aeson (FromJSON, ToJSON) -import Data.ByteString.Lazy (fromStrict) -import Data.FileEmbed (embedFile) -import Data.Generics.Labels () -import Data.Monoid.Generic (GenericMonoid (..), - GenericSemigroup (..)) -import Data.Text (Text, splitOn) -import Data.Text.Encoding (decodeUtf8) -import GHC.Generics (Generic) -import Servant.API (Capture, - FromHttpApiData (parseUrlPiece), - Get, JSON, QueryParam, - ToHttpApiData (toUrlPiece), - type (:<|>) (..), - type (:>)) -import Shpadoinkle (NFData, TVar) -import Shpadoinkle.Isreal.Types (Code (..), CompileError, - SnowNonce, SnowToken) -import Shpadoinkle.Router (HasRouter (..), View) +import Data.Aeson (FromJSON, ToJSON) +import Data.ByteString.Lazy (fromStrict) +import Data.FileEmbed (embedFile) +import Data.Generics.Labels () +import Data.Monoid.Generic (GenericMonoid (..), + GenericSemigroup (..)) +import Data.Text (Text, splitOn) +import Data.Text.Encoding (decodeUtf8) +import GHC.Generics (Generic) +import Servant.API (Capture, + FromHttpApiData (parseUrlPiece), + Get, JSON, QueryParam, + ToHttpApiData (toUrlPiece), + type (:<|>) (..), type (:>)) +import Shpadoinkle (NFData, TVar) +import Shpadoinkle.Isreal.Types (Code (..), CompileError, + SnowNonce, SnowToken) +import Shpadoinkle.Router (HasRouter (..), View) import Shpadoinkle.Widgets.Form.Dropdown -import Shpadoinkle.Widgets.Types (Input, Pick (One), - Search (..)) +import Shpadoinkle.Widgets.Types (Input, Pick (One), + Search (..)) -import Shpadoinkle.Marketing.Types.Hoogle (Target) +import Shpadoinkle.Website.Types.Hoogle (Target) data Examples a = Examples diff --git a/marketing/Shpadoinkle/Marketing/Types/Hoogle.hs b/website/Shpadoinkle/Website/Types/Hoogle.hs similarity index 97% rename from marketing/Shpadoinkle/Marketing/Types/Hoogle.hs rename to website/Shpadoinkle/Website/Types/Hoogle.hs index 1d0f79e2..ebfc0d4b 100644 --- a/marketing/Shpadoinkle/Marketing/Types/Hoogle.hs +++ b/website/Shpadoinkle/Website/Types/Hoogle.hs @@ -3,7 +3,7 @@ {-# LANGUAGE OverloadedStrings #-} -module Shpadoinkle.Marketing.Types.Hoogle where +module Shpadoinkle.Website.Types.Hoogle where import Data.Aeson (FromJSON (parseJSON), ToJSON (toJSON), diff --git a/marketing/Shpadoinkle/Marketing/View.hs b/website/Shpadoinkle/Website/View.hs similarity index 98% rename from marketing/Shpadoinkle/Marketing/View.hs rename to website/Shpadoinkle/Website/View.hs index 4cfcddab..6bf1503c 100644 --- a/marketing/Shpadoinkle/Marketing/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -9,7 +9,7 @@ {-# OPTIONS_GHC -fno-warn-type-defaults #-} -module Shpadoinkle.Marketing.View where +module Shpadoinkle.Website.View where import Control.Lens (to, (%~), (.~), (<>~), @@ -36,17 +36,17 @@ import Shpadoinkle.Html as H import Shpadoinkle.Isreal.Types as Swan import Shpadoinkle.Lens (onRecord, onRecordEndo, onSum) -import qualified Shpadoinkle.Marketing.Tailwind as T import Shpadoinkle.Router (toHydration) import Shpadoinkle.Run (Env, entrypoint) + +import qualified Shpadoinkle.Website.Tailwind as T +import Shpadoinkle.Website.Types +import Shpadoinkle.Website.Types.Hoogle import Shpadoinkle.Widgets.Form.Dropdown import qualified Shpadoinkle.Widgets.Form.Input as I import Shpadoinkle.Widgets.Types (Pick (One), Search (..), withOptions) -import Shpadoinkle.Marketing.Types -import Shpadoinkle.Marketing.Types.Hoogle - default (Text) diff --git a/marketing/counter.example b/website/counter.example similarity index 100% rename from marketing/counter.example rename to website/counter.example diff --git a/marketing/default.nix b/website/default.nix similarity index 65% rename from marketing/default.nix rename to website/default.nix index 95e9aaa7..82ddfc0e 100644 --- a/marketing/default.nix +++ b/website/default.nix @@ -10,10 +10,16 @@ let util = import ../nix/util.nix { inherit pkgs compiler; isJS = true; }; opti = if optimize then util.doCannibalize else (x: x); file = if optimize then "all.min.js" else "all.js"; + docs = pkgs.runCommand "docs" { + src = ./docs; + buildInputs = [ pkgs.asciidoctor ]; + } '' + find . -name "*.adoc" + ''; in - pkgs.runCommand "marketing" {} '' + pkgs.runCommand "website" {} '' mkdir $out ln -s ${./static} $out/static - ln -s ${opti pkgsJS.haskell.packages.${util.compilerjs}.Shpadoinkle-marketing}/bin/run.jsexe/${file} $out/all.min.js - ${pkgs.haskell.packages.${compiler}.Shpadoinkle-marketing}/bin/disembodied -o $out + ln -s ${opti pkgsJS.haskell.packages.${util.compilerjs}.Shpadoinkle-website}/bin/run.jsexe/${file} $out/all.min.js + ${pkgs.haskell.packages.${compiler}.Shpadoinkle-website}/bin/disembodied -o $out '' diff --git a/docs/modules/ROOT/pages/comparison.adoc b/website/docs/comparison.adoc similarity index 100% rename from docs/modules/ROOT/pages/comparison.adoc rename to website/docs/comparison.adoc diff --git a/docs/modules/ROOT/pages/concepts.adoc b/website/docs/concepts.adoc similarity index 100% rename from docs/modules/ROOT/pages/concepts.adoc rename to website/docs/concepts.adoc diff --git a/docs/modules/ROOT/pages/getting-started/adding-to-your-project.adoc b/website/docs/getting-started/adding-to-your-project.adoc similarity index 100% rename from docs/modules/ROOT/pages/getting-started/adding-to-your-project.adoc rename to website/docs/getting-started/adding-to-your-project.adoc diff --git a/docs/modules/ROOT/pages/getting-started/extend-an-example.adoc b/website/docs/getting-started/extend-an-example.adoc similarity index 100% rename from docs/modules/ROOT/pages/getting-started/extend-an-example.adoc rename to website/docs/getting-started/extend-an-example.adoc diff --git a/docs/modules/ROOT/pages/getting-started/index.adoc b/website/docs/getting-started/index.adoc similarity index 100% rename from docs/modules/ROOT/pages/getting-started/index.adoc rename to website/docs/getting-started/index.adoc diff --git a/website/docs/getting-started/index.html b/website/docs/getting-started/index.html new file mode 100644 index 00000000..a57aa97f --- /dev/null +++ b/website/docs/getting-started/index.html @@ -0,0 +1,122 @@ +
+
+
+

Shpadoinkle is built with Nix, but you can build however you like. stack and cabal are supported with or without Nix.

+
+
+
+
+

⛄ Snowman

+
+
+

The fastest way to start a new Shpadoinkle project is to build a +{gitlab}snowman/[Snowman]. +The following command will setup a new "hello world" project for you.

+
+
+
+
bash <( curl https://gitlab.com/platonic/shpadoinkle/-/raw/master/snowman/generate.sh )
+
+
+
+

Once you have your new project you can get live reloads with the following command:

+
+
+
+
nix-shell --command "ghcid --command 'cabal repl' -W -T Main.dev"
+
+
+
+

This will use nix-shell to setup a development environment for you, then execute ghcid inside the environment to watch your files, and execute Main.dev inside a repl. You can see results at http://localhost:8080. Of course you must have Nix installed for nix-shell to work.

+
+
+ + + + + +
+
Caution
+
+Building from source can take a long time. If you run with nix-build it’s highly recommended you use Cachix. +
+
+
+ + + + + +
+
Note
+
+
chan
+If you are seeing version errors in your build, or getting something like "attribute X not found", you may need to update your channel. You can manually set the field chan (line 1) in default.nix to either a specific nix-channel version, or a specific hash (the latest hash building Shpadoinkle can be found here). Furthermore, you can control the channel for a build environment with the --argstr chan argument, for instance nix-build --argstr chan "20.03". +
+
+
+
+
+

Nix

+
+
+

Nix works on all Linux distributions, as well as Darwin-based operating systems such as MacOS. To install Nix run:

+
+
+
+
curl -L https://nixos.org/nix/install | sh
+
+
+
+ + + + + +
+
Warning
+
+
👏 Follow 👏 Instructions
+The install script outputs important instructions you need to follow. Read the output and follow instructions. +
+
+
+

Success is indicated by the presence of the Nix toolchain in your terminal…​

+
+
+
+
nix --version
+
+
+
+ + + + + +
+
Note
+
+
MacOS
+Installing Nix on MacOS should work fine, but if you end up in a sticky situation, this article may help. You can also refer to the official manual for more information about Nix. +
+
+
+
+
+

Cachix

+
+
+

Some of the work done with Nix includes deviations from the official Nix package set. As such, some dependencies are not cached on https://cache.nixos.org. Building these dependencies from source can be very slow. If you would like to avoid this wait, you can pull from our Cachix cache of pre-built dependencies by executing the following:

+
+
+
+
nix-env -iA cachix -f https://cachix.org/api/v1/install
+cachix use shpadoinkle
+
+
+
+

After executing the above, subsequent builds will use cached dependencies from shpadoinkle.cachix.org, which are kept up to date with master using GitLab CI.

+
+
+
\ No newline at end of file diff --git a/docs/modules/ROOT/pages/index.adoc b/website/docs/index.adoc similarity index 100% rename from docs/modules/ROOT/pages/index.adoc rename to website/docs/index.adoc diff --git a/website/docs/index.html b/website/docs/index.html new file mode 100644 index 00000000..3760db7c --- /dev/null +++ b/website/docs/index.html @@ -0,0 +1,490 @@ + + + + + + + +Shpadoinkle + + + + + +
+ +
+

Shpadoinkle is a new way to +program user interfaces with Haskell.

+
+
+

To begin using it, go to +Getting Started.

+
+
+
+
Fast
+
+

Shpadoinkle is fast because it does little work. The renderer is modular, so +you can always benefit from the latest advances in virtual DOM rendering.

+
+
Declarative
+
+

Your Shpadoinkle code is high-level. You need not worry about low-level +details, causality, or when DOM nodes get replaced.

+
+
Composable
+
+

With Shpadoinkle, there is no need to associate model update code with DOM +locations. This avoids elaborate passing of messages and payloads. Components +are highly composable.

+
+
Reliable
+
+

Shpadoinkle UIs are composed of components with no side-effects. So, runtime +errors are exceedingly rare. Code is easy to test because model updates are +pure functions.

+
+
+
+
+ + + \ No newline at end of file diff --git a/docs/modules/ROOT/nav.adoc b/website/docs/nav.adoc similarity index 100% rename from docs/modules/ROOT/nav.adoc rename to website/docs/nav.adoc diff --git a/docs/modules/ROOT/pages/packages/backends.adoc b/website/docs/packages/backends.adoc similarity index 100% rename from docs/modules/ROOT/pages/packages/backends.adoc rename to website/docs/packages/backends.adoc diff --git a/docs/modules/ROOT/pages/packages/console.adoc b/website/docs/packages/console.adoc similarity index 100% rename from docs/modules/ROOT/pages/packages/console.adoc rename to website/docs/packages/console.adoc diff --git a/docs/modules/ROOT/pages/packages/core.adoc b/website/docs/packages/core.adoc similarity index 100% rename from docs/modules/ROOT/pages/packages/core.adoc rename to website/docs/packages/core.adoc diff --git a/docs/modules/ROOT/pages/packages/html.adoc b/website/docs/packages/html.adoc similarity index 100% rename from docs/modules/ROOT/pages/packages/html.adoc rename to website/docs/packages/html.adoc diff --git a/docs/modules/ROOT/pages/packages/index.adoc b/website/docs/packages/index.adoc similarity index 100% rename from docs/modules/ROOT/pages/packages/index.adoc rename to website/docs/packages/index.adoc diff --git a/docs/modules/ROOT/pages/packages/lens.adoc b/website/docs/packages/lens.adoc similarity index 100% rename from docs/modules/ROOT/pages/packages/lens.adoc rename to website/docs/packages/lens.adoc diff --git a/docs/modules/ROOT/pages/packages/router.adoc b/website/docs/packages/router.adoc similarity index 100% rename from docs/modules/ROOT/pages/packages/router.adoc rename to website/docs/packages/router.adoc diff --git a/docs/modules/ROOT/pages/packages/widgets.adoc b/website/docs/packages/widgets.adoc similarity index 100% rename from docs/modules/ROOT/pages/packages/widgets.adoc rename to website/docs/packages/widgets.adoc diff --git a/docs/modules/ROOT/pages/tutorial/calculator.adoc b/website/docs/tutorial/calculator.adoc similarity index 100% rename from docs/modules/ROOT/pages/tutorial/calculator.adoc rename to website/docs/tutorial/calculator.adoc diff --git a/docs/modules/ROOT/pages/tutorial/composing.adoc b/website/docs/tutorial/composing.adoc similarity index 100% rename from docs/modules/ROOT/pages/tutorial/composing.adoc rename to website/docs/tutorial/composing.adoc diff --git a/docs/modules/ROOT/pages/tutorial/immediate-execution.adoc b/website/docs/tutorial/immediate-execution.adoc similarity index 100% rename from docs/modules/ROOT/pages/tutorial/immediate-execution.adoc rename to website/docs/tutorial/immediate-execution.adoc diff --git a/docs/modules/ROOT/pages/tutorial/index.adoc b/website/docs/tutorial/index.adoc similarity index 100% rename from docs/modules/ROOT/pages/tutorial/index.adoc rename to website/docs/tutorial/index.adoc diff --git a/marketing/example.template.bottom b/website/example.template.bottom similarity index 100% rename from marketing/example.template.bottom rename to website/example.template.bottom diff --git a/marketing/example.template.top b/website/example.template.top similarity index 100% rename from marketing/example.template.top rename to website/example.template.top diff --git a/marketing/hello-world.example b/website/hello-world.example similarity index 100% rename from marketing/hello-world.example rename to website/hello-world.example diff --git a/marketing/package.yaml b/website/package.yaml similarity index 71% rename from marketing/package.yaml rename to website/package.yaml index 9ef8e096..33f73f3f 100644 --- a/marketing/package.yaml +++ b/website/package.yaml @@ -1,15 +1,14 @@ -name: Shpadoinkle-marketing -license: BSD3 +name: Shpadoinkle-website +license: GPL-3 license-file: LICENSE author: Isaac Shapira version: 0.0.0.1 maintainer: fresheyeball@protonmail.com category: Web build-type: Simple -synopsis: Shpadoinkle splash page +synopsis: Shpadoinkle website description: - This is the splash page to be hosted at shapadoinkle.org, - making the case for adoption of the Shpadoinkle programming model. + Website for Shpadoinkle, includeing splash pages, sandboxes, and documentation. ghc-options: @@ -69,13 +68,13 @@ dependencies: executables: run: - main: Shpadoinkle/Marketing/Run.hs + main: Shpadoinkle/Website/Run.hs other-modules: - - Shpadoinkle.Marketing.Tailwind - - Shpadoinkle.Marketing.Types - - Shpadoinkle.Marketing.Types.Hoogle - - Shpadoinkle.Marketing.Style - - Shpadoinkle.Marketing.View + - Shpadoinkle.Website.Tailwind + - Shpadoinkle.Website.Types + - Shpadoinkle.Website.Types.Hoogle + - Shpadoinkle.Website.Style + - Shpadoinkle.Website.View source-dirs: . dependencies: - Shpadoinkle-backend-snabbdom @@ -98,13 +97,13 @@ executables: buildable: False else: buildable: True - main: Shpadoinkle/Marketing/Disembodied.hs + main: Shpadoinkle/Website/Disembodied.hs other-modules: - - Shpadoinkle.Marketing.Tailwind - - Shpadoinkle.Marketing.Types - - Shpadoinkle.Marketing.Types.Hoogle - - Shpadoinkle.Marketing.Style - - Shpadoinkle.Marketing.View + - Shpadoinkle.Website.Tailwind + - Shpadoinkle.Website.Types + - Shpadoinkle.Website.Types.Hoogle + - Shpadoinkle.Website.Style + - Shpadoinkle.Website.View source-dirs: . dependencies: - Shpadoinkle-disembodied diff --git a/marketing/static/logo.png b/website/static/logo.png similarity index 100% rename from marketing/static/logo.png rename to website/static/logo.png diff --git a/marketing/static/style.css b/website/static/style.css similarity index 100% rename from marketing/static/style.css rename to website/static/style.css diff --git a/marketing/static/tailwind.min.css b/website/static/tailwind.min.css similarity index 100% rename from marketing/static/tailwind.min.css rename to website/static/tailwind.min.css -- GitLab From a910faef5a05268189d80fd5313f37269d81fe92 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Fri, 26 Mar 2021 12:08:25 -0600 Subject: [PATCH 008/116] yml --- .gitlab-ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 00f87cdd..6554faa1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -134,8 +134,7 @@ Isreal: pages: stage: Push Artifacts needs: - - Antora - - Marketing + - Website - Packages GHCJS 8.6 on Linux script: - ./nix/build-pages.sh -- GitLab From 4f244bd9568c39b31f7ba8911fe18b6e5027e2b0 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Fri, 26 Mar 2021 16:17:35 -0600 Subject: [PATCH 009/116] sorta --- website/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/default.nix b/website/default.nix index 82ddfc0e..f28cef7b 100644 --- a/website/default.nix +++ b/website/default.nix @@ -14,7 +14,7 @@ let src = ./docs; buildInputs = [ pkgs.asciidoctor ]; } '' - find . -name "*.adoc" + find . -name "*.adoc" -print0 | xargs -0 asciidoctor -s -D $out ''; in pkgs.runCommand "website" {} '' -- GitLab From 9bc10b8e1b6f43045d7a6f6975f9d1288ee65fff Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Fri, 26 Mar 2021 21:34:28 -0600 Subject: [PATCH 010/116] process with asciidoctor --- .gitignore | 2 + nix/overlay-shpadoinkle.nix | 63 +-- website/docs/default.nix | 15 + website/docs/getting-started/index.html | 122 ------ website/docs/index.html | 490 ------------------------ 5 files changed, 50 insertions(+), 642 deletions(-) create mode 100644 website/docs/default.nix delete mode 100644 website/docs/getting-started/index.html delete mode 100644 website/docs/index.html diff --git a/.gitignore b/.gitignore index ec869da0..a849ebee 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ client roster.db .vscode *.tar.gz +./website/docs/*.html +./website/docs/**/*.html diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index c88536a6..d75eb35c 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -88,19 +88,20 @@ gitignore = util.gitignore - [ ".git" - "*.ghc*" - "*.cabal" - "*result*" - "*dist*" - "*.nix" - "*.md" - ".*.yml" - ]; + [ ".git" + "*.ghc*" + "*.cabal" + "*result*" + "*dist*" + "*.nix" + "*.md" + ".*.yml" + "*docs*" + ]; addFlags = x: super.haskell.lib.overrideCabal (super.haskell.lib.appendConfigureFlags x - ["--ghc-option=-Werror"]) (drv: { + [ "--ghc-option=-Werror" ]) (drv: { haddockFlags = ["--css=${../etc/linuwial.css}"]; }); @@ -146,28 +147,30 @@ in { ''; }); + websiteOverride = x: x.overrideAttrs (old: { + preBuild = '' + ln -s ${import ../website/docs {}} docs + ''; + }); + hpkgs = { - Shpadoinkle = call "Shpadoinkle" ../core; - - Shpadoinkle-backend-snabbdom = call "Shpadoinkle-backend-snabbdom" ../backends/snabbdom; - Shpadoinkle-backend-static = call "Shpadoinkle-backend-static" ../backends/static; - Shpadoinkle-backend-pardiff = call "Shpadoinkle-backend-pardiff" ../backends/pardiff; - Shpadoinkle-console = call "Shpadoinkle-console" ../console; - Shpadoinkle-developer-tools = addDev (call "Shpadoinkle-developer-tools" ../developer-tools); - Shpadoinkle-disembodied = call "Shpadoinkle-disembodied" ../disembodied; - Shpadoinkle-lens = call "Shpadoinkle-lens" ../lens; - Shpadoinkle-website = call "Shpadoinkle-website" ../website; - Shpadoinkle-html = call "Shpadoinkle-html" ../html; - Shpadoinkle-router = call "Shpadoinkle-router" ../router; - Shpadoinkle-streaming = call "Shpadoinkle-streaming" ../streaming; - Shpadoinkle-widgets = addTest (call "Shpadoinkle-widgets" ../widgets) hpkgs; - Shpadoinkle-template = call "Shpadoinkle-template" ../template; - - Shpadoinkle-tests = super.haskell.packages.${compiler}.callCabal2nix "tests" (gitignore ../tests) {}; - Shpadoinkle-examples = call "Shpadoinkle-examples" ../examples; - - Shpadoinkle-isreal = call "Shpadoinkle-isreal" ../isreal; + Shpadoinkle = call "Shpadoinkle" ../core; + Shpadoinkle-backend-snabbdom = call "Shpadoinkle-backend-snabbdom" ../backends/snabbdom; + Shpadoinkle-backend-static = call "Shpadoinkle-backend-static" ../backends/static; + Shpadoinkle-backend-pardiff = call "Shpadoinkle-backend-pardiff" ../backends/pardiff; + Shpadoinkle-console = call "Shpadoinkle-console" ../console; + Shpadoinkle-developer-tools = addDev (call "Shpadoinkle-developer-tools" ../developer-tools); + Shpadoinkle-disembodied = call "Shpadoinkle-disembodied" ../disembodied; + Shpadoinkle-lens = call "Shpadoinkle-lens" ../lens; + Shpadoinkle-website = websiteOverride (call "Shpadoinkle-website" ../website); + Shpadoinkle-html = call "Shpadoinkle-html" ../html; + Shpadoinkle-router = call "Shpadoinkle-router" ../router; + Shpadoinkle-streaming = call "Shpadoinkle-streaming" ../streaming; + Shpadoinkle-widgets = addTest (call "Shpadoinkle-widgets" ../widgets) hpkgs; + Shpadoinkle-template = call "Shpadoinkle-template" ../template; + Shpadoinkle-examples = call "Shpadoinkle-examples" ../examples; + Shpadoinkle-isreal = call "Shpadoinkle-isreal" ../isreal; ease = hself.callCabal2nix "ease" ease {}; ghcjs-base-stub = hself.callCabal2nix "ghcjs-base-stub" ghcjs-base-stub-src {}; diff --git a/website/docs/default.nix b/website/docs/default.nix new file mode 100644 index 00000000..25c10eb7 --- /dev/null +++ b/website/docs/default.nix @@ -0,0 +1,15 @@ +{ chan ? (import ../../nix/chan.nix) }: +let + pkgs = import ../../nix/pkgs.nix { inherit chan; }; +in + pkgs.stdenv.mkDerivation { + name = "docs"; + src = ./.; + buildInputs = [ pkgs.asciidoctor ]; + installPhase = '' + echo "Processing asciidoctor..." + find . -name "*.adoc" -print0 | xargs -0 asciidoctor -s + rm **/*.adoc + cp -r . $out + ''; + } diff --git a/website/docs/getting-started/index.html b/website/docs/getting-started/index.html deleted file mode 100644 index a57aa97f..00000000 --- a/website/docs/getting-started/index.html +++ /dev/null @@ -1,122 +0,0 @@ -
-
-
-

Shpadoinkle is built with Nix, but you can build however you like. stack and cabal are supported with or without Nix.

-
-
-
-
-

⛄ Snowman

-
-
-

The fastest way to start a new Shpadoinkle project is to build a -{gitlab}snowman/[Snowman]. -The following command will setup a new "hello world" project for you.

-
-
-
-
bash <( curl https://gitlab.com/platonic/shpadoinkle/-/raw/master/snowman/generate.sh )
-
-
-
-

Once you have your new project you can get live reloads with the following command:

-
-
-
-
nix-shell --command "ghcid --command 'cabal repl' -W -T Main.dev"
-
-
-
-

This will use nix-shell to setup a development environment for you, then execute ghcid inside the environment to watch your files, and execute Main.dev inside a repl. You can see results at http://localhost:8080. Of course you must have Nix installed for nix-shell to work.

-
-
- - - - - -
-
Caution
-
-Building from source can take a long time. If you run with nix-build it’s highly recommended you use Cachix. -
-
-
- - - - - -
-
Note
-
-
chan
-If you are seeing version errors in your build, or getting something like "attribute X not found", you may need to update your channel. You can manually set the field chan (line 1) in default.nix to either a specific nix-channel version, or a specific hash (the latest hash building Shpadoinkle can be found here). Furthermore, you can control the channel for a build environment with the --argstr chan argument, for instance nix-build --argstr chan "20.03". -
-
-
-
-
-

Nix

-
-
-

Nix works on all Linux distributions, as well as Darwin-based operating systems such as MacOS. To install Nix run:

-
-
-
-
curl -L https://nixos.org/nix/install | sh
-
-
-
- - - - - -
-
Warning
-
-
👏 Follow 👏 Instructions
-The install script outputs important instructions you need to follow. Read the output and follow instructions. -
-
-
-

Success is indicated by the presence of the Nix toolchain in your terminal…​

-
-
-
-
nix --version
-
-
-
- - - - - -
-
Note
-
-
MacOS
-Installing Nix on MacOS should work fine, but if you end up in a sticky situation, this article may help. You can also refer to the official manual for more information about Nix. -
-
-
-
-
-

Cachix

-
-
-

Some of the work done with Nix includes deviations from the official Nix package set. As such, some dependencies are not cached on https://cache.nixos.org. Building these dependencies from source can be very slow. If you would like to avoid this wait, you can pull from our Cachix cache of pre-built dependencies by executing the following:

-
-
-
-
nix-env -iA cachix -f https://cachix.org/api/v1/install
-cachix use shpadoinkle
-
-
-
-

After executing the above, subsequent builds will use cached dependencies from shpadoinkle.cachix.org, which are kept up to date with master using GitLab CI.

-
-
-
\ No newline at end of file diff --git a/website/docs/index.html b/website/docs/index.html deleted file mode 100644 index 3760db7c..00000000 --- a/website/docs/index.html +++ /dev/null @@ -1,490 +0,0 @@ - - - - - - - -Shpadoinkle - - - - - -
- -
-

Shpadoinkle is a new way to -program user interfaces with Haskell.

-
-
-

To begin using it, go to -Getting Started.

-
-
-
-
Fast
-
-

Shpadoinkle is fast because it does little work. The renderer is modular, so -you can always benefit from the latest advances in virtual DOM rendering.

-
-
Declarative
-
-

Your Shpadoinkle code is high-level. You need not worry about low-level -details, causality, or when DOM nodes get replaced.

-
-
Composable
-
-

With Shpadoinkle, there is no need to associate model update code with DOM -locations. This avoids elaborate passing of messages and payloads. Components -are highly composable.

-
-
Reliable
-
-

Shpadoinkle UIs are composed of components with no side-effects. So, runtime -errors are exceedingly rare. Code is easy to test because model updates are -pure functions.

-
-
-
-
- - - \ No newline at end of file -- GitLab From 96f6dd0e9ea200a0d286112938667eb53daa747d Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Fri, 26 Mar 2021 21:35:26 -0600 Subject: [PATCH 011/116] no html in docs --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index a849ebee..d903a6dc 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,4 @@ client roster.db .vscode *.tar.gz -./website/docs/*.html -./website/docs/**/*.html +*/docs/**/*.html -- GitLab From 01dbb352841d3170d1e2cb1e733990c02284d4de Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Fri, 26 Mar 2021 21:37:45 -0600 Subject: [PATCH 012/116] ignore html in build --- website/docs/default.nix | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/website/docs/default.nix b/website/docs/default.nix index 25c10eb7..71ddd9d9 100644 --- a/website/docs/default.nix +++ b/website/docs/default.nix @@ -1,11 +1,13 @@ { chan ? (import ../../nix/chan.nix) }: -let - pkgs = import ../../nix/pkgs.nix { inherit chan; }; +let pkgs = import ../../nix/pkgs.nix { inherit chan; }; + util = import ./util.nix { inherit pkgs; }; + gitignore = util.gitignore + [ "*.html" ]; in pkgs.stdenv.mkDerivation { name = "docs"; - src = ./.; - buildInputs = [ pkgs.asciidoctor ]; + src = gitignore ./.; + buildInputs = [ pkgs.asciidoctor ]; installPhase = '' echo "Processing asciidoctor..." find . -name "*.adoc" -print0 | xargs -0 asciidoctor -s -- GitLab From 318ca6fe3a7975d7f408531a66fbcb37ef9e5db1 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Fri, 26 Mar 2021 23:14:47 -0600 Subject: [PATCH 013/116] it builds with asciidoctor --- cabal.project | 2 +- nix/asciidoctor.sh | 2 + nix/base.nix | 7 +- nix/hpackall.sh | 2 +- nix/sandblast.sh | 1 + website/Shpadoinkle/Website/Disembodied.hs | 12 +- website/Shpadoinkle/Website/Run.hs | 4 +- website/Shpadoinkle/Website/Types.hs | 148 ++++++++++++++------- website/Shpadoinkle/Website/View.hs | 72 +++++++--- website/default.nix | 10 +- website/docs/default.nix | 10 +- website/package.yaml | 1 + 12 files changed, 180 insertions(+), 91 deletions(-) create mode 100755 nix/asciidoctor.sh diff --git a/cabal.project b/cabal.project index c9e9d5b2..3103dcf8 100644 --- a/cabal.project +++ b/cabal.project @@ -7,7 +7,7 @@ packages: core , developer-tools , disembodied , lens - , marketing + , website , html , router , streaming diff --git a/nix/asciidoctor.sh b/nix/asciidoctor.sh new file mode 100755 index 00000000..72f47099 --- /dev/null +++ b/nix/asciidoctor.sh @@ -0,0 +1,2 @@ +echo "Processing asciidoctor..." +find . -name "*.adoc" -print0 | xargs -0 asciidoctor -s diff --git a/nix/base.nix b/nix/base.nix index 7132fb83..e20affd0 100644 --- a/nix/base.nix +++ b/nix/base.nix @@ -61,9 +61,8 @@ let Shpadoinkle-streaming Shpadoinkle-widgets Shpadoinkle-isreal - Shpadoinkle-template + Shpadoinkle-template; - Shpadoinkle-tests; Shpadoinkle-examples = cannibal haskell.packages.${util.compilerjs}.Shpadoinkle-examples; Shpadoinkle-website = cannibal haskell.packages.${util.compilerjs}.Shpadoinkle-website; @@ -89,13 +88,11 @@ let }); - - shellBase = { inherit withHoogle; packages = _: if pack == "all" then attrValues packages else [ packages.${pack} ]; COMPILER = util.compilerjs; - buildInputs = ghcTools ++ [ ack util.cannibalize nixops ]; + buildInputs = ghcTools ++ [ asciidoctor ack util.cannibalize nixops ]; shellHook = '' cat ${../etc/figlet} ./nix/hpackall.sh | grep generated diff --git a/nix/hpackall.sh b/nix/hpackall.sh index 17eac857..c328f1a3 100755 --- a/nix/hpackall.sh +++ b/nix/hpackall.sh @@ -11,7 +11,7 @@ p disembodied p html p router p lens -p marketing +p website p widgets p examples p tests diff --git a/nix/sandblast.sh b/nix/sandblast.sh index 004d1dbe..af7901b3 100755 --- a/nix/sandblast.sh +++ b/nix/sandblast.sh @@ -7,3 +7,4 @@ rm -r *dist* & rm -- **/*dist* & rm roster.db & rm -- **/*.cabal & +rm -- */docs/**/*.html & diff --git a/website/Shpadoinkle/Website/Disembodied.hs b/website/Shpadoinkle/Website/Disembodied.hs index bfa329f8..8d49598c 100644 --- a/website/Shpadoinkle/Website/Disembodied.hs +++ b/website/Shpadoinkle/Website/Disembodied.hs @@ -28,8 +28,7 @@ import Shpadoinkle.Lens (onSum) import Shpadoinkle.Run (Env (Prod)) import Shpadoinkle.Website.Types -import Shpadoinkle.Website.View (comparisons, fourOhFour, - genExampleTokens, home, template) +import Shpadoinkle.Website.View newtype Noop a = Noop (JSM a) deriving newtype (Functor, Applicative, Monad, MonadJSM, MonadIO) @@ -41,10 +40,13 @@ wrap l v x = const $ template Prod (x ^. re l) (l `onSum` v x) site :: Hooglable m => ExampleEffects m => MonadJSM m => Examples SnowToken -> SiteSpec () (SPA m) -site token = wrap #_HomeM home (emptyHome token) - :<|> wrap #_ComparisonM comparisons . (`Comparison` Nothing) +site token = wrap #_MHome home (emptyHome token) + :<|> const concepts + :<|> (const gsIndex :<|> const gsAddingToYourProject :<|> const gsExtendAnExample) + :<|> (const rpIndex :<|> const rpCore :<|> const rpConsole :<|> const rpBackends :<|> const rpHtml :<|> const rpLens :<|> const rpRouter :<|> const rpWidgets) + :<|> (const rtIndex :<|> const rtCalculator :<|> const rtImmediateExecution :<|> const rtComposing) :<|> const fourOhFour - :<|> wrap #_HomeM home (emptyHome token) + :<|> wrap #_MHome home (emptyHome token) main :: IO () diff --git a/website/Shpadoinkle/Website/Run.hs b/website/Shpadoinkle/Website/Run.hs index 779b8533..5b613ba6 100644 --- a/website/Shpadoinkle/Website/Run.hs +++ b/website/Shpadoinkle/Website/Run.hs @@ -52,7 +52,7 @@ import Shpadoinkle.Run (runJSorWarp) import Shpadoinkle.DeveloperTools import Shpadoinkle.Website.Types (Frontend, Hooglable (..), HoogleAPI, - Route (FourOhFourR), SPA, + Route (RFourOhFour), SPA, Swan (..), routes) import Shpadoinkle.Website.Types.Hoogle (Target) #ifndef ghcjs_HOST_OS @@ -102,7 +102,7 @@ findTargetsM (Search s) = client (Proxy @ HoogleAPI) (Just "json") (Just s) (Jus app :: JSM () app = do mutex <- newTVarIO Nothing - model :: TVar Frontend <- newTVarIO =<< runReaderT (runApp (start FourOhFourR)) mutex + model :: TVar Frontend <- newTVarIO =<< runReaderT (runApp (start RFourOhFour)) mutex withDeveloperTools model fullPageSPA' @(SPA JSM) (flip runReaderT mutex . runApp) runSnabbdom model (withHydration startJS) view stage (fmap constUpdate . startJS) routes diff --git a/website/Shpadoinkle/Website/Types.hs b/website/Shpadoinkle/Website/Types.hs index 28b294a5..3570536b 100644 --- a/website/Shpadoinkle/Website/Types.hs +++ b/website/Shpadoinkle/Website/Types.hs @@ -27,10 +27,7 @@ import Data.Monoid.Generic (GenericMonoid (..), import Data.Text (Text, splitOn) import Data.Text.Encoding (decodeUtf8) import GHC.Generics (Generic) -import Servant.API (Capture, - FromHttpApiData (parseUrlPiece), - Get, JSON, QueryParam, - ToHttpApiData (toUrlPiece), +import Servant.API (Get, JSON, QueryParam, type (:<|>) (..), type (:>)) import Shpadoinkle (NFData, TVar) import Shpadoinkle.Isreal.Types (Code (..), CompileError, @@ -111,59 +108,128 @@ data Hoogle = Hoogle deriving stock (Eq, Ord, Show, Read, Generic) deriving anyclass (FromJSON, ToJSON, NFData) deriving Semigroup via GenericSemigroup Hoogle - deriving Monoid via GenericMonoid Hoogle + deriving Monoid via GenericMonoid Hoogle -data Comparison = Comparison - { framework :: Framework - , tokenMay :: Maybe SnowToken - } +data RGettingStarted + = RGSIndex + | RGSAddingToYourProject + | RGSExtendAnExample deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) -data Route - = HomeR - | ComparisonR Framework - | FourOhFourR +type SPAGettingStarted m + = View m Frontend + :<|> "adding-to-your-project" :> View m Frontend + :<|> "extend-an-example" :> View m Frontend -data Frontend - = HomeM Home - | ComparisonM Comparison - | FourOhFourM +gettingStartedRoutes :: SPAGettingStarted m :>> Route +gettingStartedRoutes + = RGettingStarted RGSIndex + :<|> RGettingStarted RGSAddingToYourProject + :<|> RGettingStarted RGSExtendAnExample + + +data RPackages + = RPIndex + | RPCore + | RPConsole + | RPBackends + | RPHtml + | RPLens + | RPRouter + | RPWidgets deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) -data Framework = React | Vue | Elm | Halogen | Reflex - deriving (Eq, Ord, Read, Show, Bounded, Enum, Generic, FromJSON, ToJSON, NFData) +type SPAPackages m + = View m Frontend + :<|> "core" :> View m Frontend + :<|> "console" :> View m Frontend + :<|> "backends" :> View m Frontend + :<|> "html" :> View m Frontend + :<|> "lens" :> View m Frontend + :<|> "router" :> View m Frontend + :<|> "widgets" :> View m Frontend + + +packagesRoutes :: SPAPackages m :>> Route +packagesRoutes + = RPackages RPIndex + :<|> RPackages RPCore + :<|> RPackages RPConsole + :<|> RPackages RPBackends + :<|> RPackages RPHtml + :<|> RPackages RPLens + :<|> RPackages RPRouter + :<|> RPackages RPWidgets + + +data RTutorial + = RTIndex + | RTCalculator + | RTImmediateExecution + | RTComposing + deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) -instance FromHttpApiData Framework where - parseUrlPiece = \case - "react" -> pure React - "vue" -> pure Vue - "elm" -> pure Elm - "halogen" -> pure Halogen - "reflex" -> pure Reflex - x -> Left $ x <> " is not a valid framework" +type SPATutorial m + = View m Frontend + :<|> "calculator" :> View m Frontend + :<|> "immediate-execution" :> View m Frontend + :<|> "composing" :> View m Frontend -instance ToHttpApiData Framework where - toUrlPiece = \case - React -> "react" - Vue -> "vue" - Elm -> "elm" - Halogen -> "halogen" - Reflex -> "reflex" +tutorialRoutes :: SPATutorial m :>> Route +tutorialRoutes + = RTutorial RTIndex + :<|> RTutorial RTCalculator + :<|> RTutorial RTImmediateExecution + :<|> RTutorial RTComposing + + +data Route + = RHome + | RConcepts + | RGettingStarted RGettingStarted + | RPackages RPackages + | RTutorial RTutorial + | RFourOhFour + deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) type SPA m - = "home" :> View m Frontend - :<|> "comparisons" :> Capture "framework" Framework :> View m Frontend - :<|> "404" :> View m Frontend + = "home" :> View m Frontend + :<|> "concepts" :> View m Frontend + :<|> "getting-started" :> SPAGettingStarted m + :<|> "packages" :> SPAPackages m + :<|> "tutorial" :> SPATutorial m + :<|> "404" :> View m Frontend :<|> View m Frontend +routes :: SPA m :>> Route +routes + = RHome + :<|> RConcepts + :<|> gettingStartedRoutes + :<|> packagesRoutes + :<|> tutorialRoutes + :<|> RFourOhFour + :<|> RHome + + +data Frontend + = MHome Home + | MConcepts + | MGettingStarted RGettingStarted + | MPackages RPackages + | MTutorial RTutorial + | MFourOhFour + deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) + + type HoogleAPI = QueryParam "mode" Text :> QueryParam "hoogle" Text :> QueryParam "start" Int @@ -190,11 +256,3 @@ class Hooglable m where instance (MonadTrans t, Monad m, Hooglable m) => Hooglable (t m) where findTargets = lift . findTargets - - -routes :: SPA m :>> Route -routes - = HomeR - :<|> ComparisonR - :<|> FourOhFourR - :<|> HomeR diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index 6bf1503c..43209273 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -5,6 +5,7 @@ {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-} @@ -12,8 +13,7 @@ module Shpadoinkle.Website.View where -import Control.Lens (to, (%~), (.~), (<>~), - (^.)) +import Control.Lens ((%~), (.~), (<>~), (^.)) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Reader import Data.Generics.Labels () @@ -38,6 +38,7 @@ import Shpadoinkle.Lens (onRecord, onRecordEndo, onSum) import Shpadoinkle.Router (toHydration) import Shpadoinkle.Run (Env, entrypoint) +import Shpadoinkle.Template.TH import qualified Shpadoinkle.Website.Tailwind as T import Shpadoinkle.Website.Types @@ -191,7 +192,6 @@ compileExample = kleisli $ \(Example cc token nonce _) -> do Right _ -> #state .~ EReady - errorMessages :: CompileError -> Html m a errorMessages = div "errors" . fmap singleError . breakError . replace bunkWarning "" . unCompileError where @@ -205,7 +205,6 @@ errorMessages = div "errors" . fmap singleError . breakError . replace bunkWarni bunkWarning = "Warning: don't know how to find change monitoring files for the installed\npackage databases for ghcjs\n" - hoogleWidget :: forall m. Hooglable m => MonadJSM m => Hoogle -> Html m Hoogle hoogleWidget hoo = div @@ -240,19 +239,53 @@ home home' = section_ ] -comparisons :: Comparison -> Html m Comparison -comparisons x = h1_ [ text $ "Comparison with, " <> x ^. #framework . to (pack . show) ] - - -fourOhFour :: Html m a -fourOhFour = h2_ [ "404" ] +concepts + , gsIndex, gsAddingToYourProject, gsExtendAnExample + , rpIndex, rpCore, rpConsole, rpBackends, rpHtml, rpLens, rpRouter, rpWidgets + , rtIndex, rtCalculator, rtImmediateExecution, rtComposing + , fourOhFour :: Html m a +concepts = div_ $(embedHtml "./docs/concepts.html") +gsIndex = div_ $(embedHtml "./docs/getting-started/index.html") +gsAddingToYourProject = div_ $(embedHtml "./docs/getting-started/adding-to-your-project.html") +gsExtendAnExample = div_ $(embedHtml "./docs/getting-started/extend-an-example.html") +rpIndex = div_ $(embedHtml "./docs/packages/index.html") +rpCore = div_ $(embedHtml "./docs/packages/core.html") +rpConsole = div_ $(embedHtml "./docs/packages/console.html") +rpBackends = div_ $(embedHtml "./docs/packages/backends.html") +rpHtml = div_ $(embedHtml "./docs/packages/html.html") +rpLens = div_ $(embedHtml "./docs/packages/lens.html") +rpRouter = div_ $(embedHtml "./docs/packages/router.html") +rpWidgets = div_ $(embedHtml "./docs/packages/widgets.html") +rtIndex = div_ $(embedHtml "./docs/tutorial/index.html") +rtCalculator = div_ $(embedHtml "./docs/tutorial/calculator.html") +rtImmediateExecution = div_ $(embedHtml "./docs/tutorial/immediate-execution.html") +rtComposing = div_ $(embedHtml "./docs/tutorial/composing.html") +fourOhFour = h2_ [ "404" ] view :: Hooglable m => ExampleEffects m => MonadJSM m => Frontend -> Html m Frontend view = \case - HomeM x -> #_HomeM `onSum` home x - ComparisonM x -> #_ComparisonM `onSum` comparisons x - FourOhFourM -> fourOhFour + MHome x -> #_MHome `onSum` home x + MConcepts -> concepts + MGettingStarted gsr -> case gsr of + RGSIndex -> gsIndex + RGSAddingToYourProject -> gsAddingToYourProject + RGSExtendAnExample -> gsExtendAnExample + MPackages pr -> case pr of + RPIndex -> rpIndex + RPCore -> rpCore + RPConsole -> rpConsole + RPBackends -> rpBackends + RPHtml -> rpHtml + RPLens -> rpLens + RPRouter -> rpRouter + RPWidgets -> rpWidgets + MTutorial tutr -> case tutr of + RTIndex -> rtIndex + RTCalculator -> rtCalculator + RTImmediateExecution -> rtImmediateExecution + RTComposing -> rtComposing + MFourOhFour -> fourOhFour template :: Env -> Frontend -> Html m Frontend -> Html m Frontend @@ -295,16 +328,17 @@ genExampleTokens = liftIO $ Examples <$> genSnowToken <*> genSnowToken start :: MonadIO m => Route -> m Frontend start = \case - HomeR -> HomeM . emptyHome <$> genExampleTokens - ComparisonR f -> pure . ComparisonM $ Comparison f Nothing - FourOhFourR -> pure FourOhFourM + RHome -> MHome . emptyHome <$> genExampleTokens + RConcepts -> pure $ MConcepts + RGettingStarted x -> pure $ MGettingStarted x + RPackages x -> pure $ MPackages x + RTutorial x -> pure $ MTutorial x + RFourOhFour -> pure MFourOhFour startJS :: MonadJSM m => ExampleEffects m => Route -> m Frontend startJS r = do fe <- start r case fe of - HomeM home' -> HomeM <$> compileExamples home' + MHome home' -> MHome <$> compileExamples home' x -> pure x - - diff --git a/website/default.nix b/website/default.nix index f28cef7b..4ae02b31 100644 --- a/website/default.nix +++ b/website/default.nix @@ -10,14 +10,10 @@ let util = import ../nix/util.nix { inherit pkgs compiler; isJS = true; }; opti = if optimize then util.doCannibalize else (x: x); file = if optimize then "all.min.js" else "all.js"; - docs = pkgs.runCommand "docs" { - src = ./docs; - buildInputs = [ pkgs.asciidoctor ]; - } '' - find . -name "*.adoc" -print0 | xargs -0 asciidoctor -s -D $out - ''; in - pkgs.runCommand "website" {} '' + pkgs.runCommand "website" { + LANG = "C.UTF-8"; + } '' mkdir $out ln -s ${./static} $out/static ln -s ${opti pkgsJS.haskell.packages.${util.compilerjs}.Shpadoinkle-website}/bin/run.jsexe/${file} $out/all.min.js diff --git a/website/docs/default.nix b/website/docs/default.nix index 71ddd9d9..29a7665e 100644 --- a/website/docs/default.nix +++ b/website/docs/default.nix @@ -1,16 +1,14 @@ { chan ? (import ../../nix/chan.nix) }: -let pkgs = import ../../nix/pkgs.nix { inherit chan; }; - util = import ./util.nix { inherit pkgs; }; - gitignore = util.gitignore - [ "*.html" ]; +let pkgs = import ../../nix/pkgs.nix { inherit chan; }; + util = import ../../nix/util.nix { inherit pkgs; }; + gitignore = util.gitignore [ "*.html" ]; in pkgs.stdenv.mkDerivation { name = "docs"; src = gitignore ./.; buildInputs = [ pkgs.asciidoctor ]; installPhase = '' - echo "Processing asciidoctor..." - find . -name "*.adoc" -print0 | xargs -0 asciidoctor -s + ${builtins.readFile ../../nix/asciidoctor.sh} rm **/*.adoc cp -r . $out ''; diff --git a/website/package.yaml b/website/package.yaml index 33f73f3f..08d6e18f 100644 --- a/website/package.yaml +++ b/website/package.yaml @@ -64,6 +64,7 @@ dependencies: - Shpadoinkle-lens - Shpadoinkle-widgets - Shpadoinkle-console + - Shpadoinkle-template executables: -- GitLab From 7b094022bc79081fa8d1d7083e0e4cd2eb5b4d95 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Fri, 26 Mar 2021 23:24:53 -0600 Subject: [PATCH 014/116] lint --- website/Shpadoinkle/Website/View.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index 43209273..91420ef5 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -329,7 +329,7 @@ genExampleTokens = liftIO $ Examples <$> genSnowToken <*> genSnowToken start :: MonadIO m => Route -> m Frontend start = \case RHome -> MHome . emptyHome <$> genExampleTokens - RConcepts -> pure $ MConcepts + RConcepts -> pure MConcepts RGettingStarted x -> pure $ MGettingStarted x RPackages x -> pure $ MPackages x RTutorial x -> pure $ MTutorial x -- GitLab From d27e9c5394331255f675d9806166bd91d09a97b0 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Sat, 27 Mar 2021 09:52:01 -0600 Subject: [PATCH 015/116] load both at start --- website/Shpadoinkle/Website/View.hs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index 91420ef5..cafa63d4 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -13,7 +13,8 @@ module Shpadoinkle.Website.View where -import Control.Lens ((%~), (.~), (<>~), (^.)) +import Control.Lens ((%~), (&), (.~), (<>~), + (^.)) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Reader import Data.Generics.Labels () @@ -318,8 +319,11 @@ template ev fe v = H.html_ compileExamples :: MonadJSM m => ExampleEffects m => Home -> m Home compileExamples home' = do - f <- runContinuation compileExample $ home' ^. #examples . #helloWorld - return $ (#examples . #helloWorld %~ f) home' + hw <- runContinuation compileExample $ home' ^. #examples . #helloWorld + c <- runContinuation compileExample $ home' ^. #examples . #counter + return $ home' + & #examples . #helloWorld %~ hw + & #examples . #counter %~ c genExampleTokens :: MonadIO m => m (Examples SnowToken) -- GitLab From 185ea07ef48454bf7ca698bbeff644f758a4c6c3 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Sat, 27 Mar 2021 21:04:35 -0600 Subject: [PATCH 016/116] add an emoji for utf8 test --- template/Test.hs | 2 ++ template/test.html | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/template/Test.hs b/template/Test.hs index e261e447..840883e7 100644 --- a/template/Test.hs +++ b/template/Test.hs @@ -21,6 +21,7 @@ testHtmlIngestion = in if x == y then pure () else error $ "test.html did not parse correctly. Got: " ++ unpack x + testTemplate :: IO () testTemplate = let x = renderStatic $ template (replace "{{x}}" "yoddle") $ @@ -31,6 +32,7 @@ testTemplate = in if x == y then pure () else error $ "template did not interpolate. Got: " ++ unpack x + main :: IO () main = do testHtmlIngestion diff --git a/template/test.html b/template/test.html index 1dc03d96..b43aefba 100644 --- a/template/test.html +++ b/template/test.html @@ -1,5 +1,5 @@
- Shocka zooloo + Shocka zooloo🤠

Wak!

-- GitLab From 0ed01038bff65cf62ffec5048bee310a17635bb5 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 1 Apr 2021 18:17:28 -0600 Subject: [PATCH 017/116] snowman update --- nix/overlay-shpadoinkle.nix | 7 +++++-- snowman/template/snowman.cabal | 28 +++++++++++++++++++++++++++- snowman/template/src/Main.hs | 8 ++++---- template/Test.hs | 12 ++++++++---- template/package.yaml | 3 +++ 5 files changed, 47 insertions(+), 11 deletions(-) diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index d75eb35c..4249712a 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -100,8 +100,11 @@ ]; - addFlags = x: super.haskell.lib.overrideCabal (super.haskell.lib.appendConfigureFlags x - [ "--ghc-option=-Werror" ]) (drv: { + addFlags = x: with super.haskell.lib; overrideCabal + (appendConfigureFlags (appendBuildFlags x (if isJS then + [ "--ghcjs-option=-dedupe" ] else [])) + [ "--ghc-option=-Werror" ]) + (drv: { haddockFlags = ["--css=${../etc/linuwial.css}"]; }); diff --git a/snowman/template/snowman.cabal b/snowman/template/snowman.cabal index 01b8590b..38a7c682 100644 --- a/snowman/template/snowman.cabal +++ b/snowman/template/snowman.cabal @@ -6,8 +6,34 @@ executable snowman main-is: Main.hs build-depends: base ^>=4.12.0.0 , Shpadoinkle - , Shpadoinkle-backend-pardiff + , Shpadoinkle-backend-snabbdom , Shpadoinkle-html , text + hs-source-dirs: src default-language: Haskell2010 + + ghc-options: + -Wall + -Werror + -Wcompat + -fwarn-redundant-constraints + -fwarn-incomplete-uni-patterns + -fwarn-tabs + -fwarn-incomplete-record-updates + -fwarn-identities + + ghcjs-options: + -Wall + -Werror + -Wcompat + -fno-warn-missing-home-modules + -fwarn-redundant-constraints + -fwarn-incomplete-uni-patterns + -fwarn-tabs + -fwarn-incomplete-record-updates + -fwarn-identities + -dedupe + -O2 + + diff --git a/snowman/template/src/Main.hs b/snowman/template/src/Main.hs index 16e0992d..05c76550 100644 --- a/snowman/template/src/Main.hs +++ b/snowman/template/src/Main.hs @@ -4,10 +4,10 @@ module Main where -import Shpadoinkle (Html, JSM) -import Shpadoinkle.Backend.ParDiff (runParDiff) +import Shpadoinkle (Html, JSM) +import Shpadoinkle.Backend.Snabbdom (runSnabbdom) import Shpadoinkle.Html -import Shpadoinkle.Run (live, runJSorWarp, simple) +import Shpadoinkle.Run (live, runJSorWarp, simple) view :: () -> Html m () @@ -15,7 +15,7 @@ view _ = "hello world" app :: JSM () -app = simple runParDiff () view getBody +app = simple runSnabbdom () view getBody dev :: IO () diff --git a/template/Test.hs b/template/Test.hs index 840883e7..1300c0a3 100644 --- a/template/Test.hs +++ b/template/Test.hs @@ -18,8 +18,10 @@ testHtmlIngestion :: IO () testHtmlIngestion = let x = mconcat $ renderStatic <$> $(embedHtml "./test.html") y = decodeUtf8 $(embedFile "./test.html") - in if x == y then pure () else - error $ "test.html did not parse correctly. Got: " ++ unpack x + in if x == y then pure () else do + print x + print y + error $ "test.html did not parse correctly. Got: " testTemplate :: IO () @@ -29,8 +31,10 @@ testTemplate = [ h "span" [] [ "Hi {{x}}" ] ] y = "Hi yoddle" - in if x == y then pure () else - error $ "template did not interpolate. Got: " ++ unpack x + in if x == y then pure () else do + print x + print y + error $ "template did not interpolate." main :: IO () diff --git a/template/package.yaml b/template/package.yaml index 1b83c603..f5ee7bb1 100644 --- a/template/package.yaml +++ b/template/package.yaml @@ -49,6 +49,9 @@ tests: sample: main: Test.hs source-dirs: . + other-modules: + - Shpadoinkle.Template.TH + - Shpadoinkle.Template dependencies: - Shpadoinkle-template - file-embed -- GitLab From b8eba2afa185616e755fa818400bd8e0b649f53b Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 1 Apr 2021 19:28:41 -0600 Subject: [PATCH 018/116] bad fix to unicode problem --- template/Shpadoinkle/Template/TH.hs | 10 ++++++---- template/test.html | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/template/Shpadoinkle/Template/TH.hs b/template/Shpadoinkle/Template/TH.hs index a60d0a47..15d52c0c 100644 --- a/template/Shpadoinkle/Template/TH.hs +++ b/template/Shpadoinkle/Template/TH.hs @@ -1,4 +1,5 @@ -{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} module Shpadoinkle.Template.TH where @@ -13,7 +14,7 @@ import Text.HTML.Parser (Attr (..), Token (..), parseTokens) embedHtml :: FilePath -> Q Exp embedHtml path = do - ts <- runIO $ parseTokens <$> readFile path + ts <- runIO $ parseTokens <$> readFile path pure . ListE $ tokenToExp ts @@ -32,8 +33,9 @@ tokenToExp = in AppE (AppE (AppE h name) attrs') (ListE []) : tokenToExp ts TagClose _:ts -> tokenToExp ts ContentText content:ts -> - let content' = asText content - in AppE text content' : tokenToExp ts + if content == "\56608" + then tokenToExp ts else let content' = asText content + in AppE text content' : tokenToExp ts ContentChar char:ts -> let char' = asText $ cons char mempty in AppE text char' : tokenToExp ts diff --git a/template/test.html b/template/test.html index b43aefba..67aacf9f 100644 --- a/template/test.html +++ b/template/test.html @@ -1,5 +1,5 @@
- Shocka zooloo🤠 + Shockazooloo🤠

Wak!

-- GitLab From 80a2a429f9e9b1f677d79f3c4ba038f74c81fb73 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Tue, 6 Apr 2021 17:55:46 -0600 Subject: [PATCH 019/116] partail --- hie.yaml | 8 +- .../Shpadoinkle/Website/Partials/Template.hs | 279 ++++++++++++++++++ website/Shpadoinkle/Website/View.hs | 52 ++-- 3 files changed, 310 insertions(+), 29 deletions(-) create mode 100644 website/Shpadoinkle/Website/Partials/Template.hs diff --git a/hie.yaml b/hie.yaml index 4ab633ab..971db8fc 100644 --- a/hie.yaml +++ b/hie.yaml @@ -55,17 +55,17 @@ cradle: cabal: component: "lens" - - path: "./marketing/Shpadoinkle/Marketing/Disembodied.hs" + - path: "./website/Shpadoinkle/Website/Disembodied.hs" config: cradle: cabal: - component: "marketing:disembodied" + component: "website:disembodied" - - path: "./marketing/" + - path: "./website/" config: cradle: cabal: - component: "marketing:run" + component: "website:run" - path: "./html/" config: diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs new file mode 100644 index 00000000..a1f7b596 --- /dev/null +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -0,0 +1,279 @@ +{-# LANGUAGE OverloadedStrings #-} +module Shpadoinkle.Website.Partials.Template where + + +import Shpadoinkle.Html + + +headView :: Html m a +headView = head_ + [ meta' [ charset "UTF-8" ] + , link' [ href "favicon.svg", rel "icon", type_ "image/svg+xml" ] + , meta' [ content "width=device-width, initial-scale=1.0", name "viewport" ] + , title_ [ "Shpadoinkle" ] + ] + + +navView :: [Html m a] +navView + [ nav "nav" + [ a [ href "/" ] + [ img' [ alt "shpadoinkle logo", className "nav__logo", src "assets/landing_logo.svg" ] + ] + , img' [ alt "menu toggle icon", className "nav__menu-icon", src "assets/menu_icon.svg" ] + , img' [ alt "menu toggle icon", className "nav__menu-icon", src "assets/menu_close_icon.svg" ] + , ul "nav__links" + [ li "nav__link" + [ a [ href "/" ] + [ "Home" ] + , svg [ className "nav__link-underline", viewBox "0 0 129 10", ("xmlns", "http://www.w3.org/2000/svg") ] + [ path' [ d "M.365 10c-.59-1.096-.508-1.247.562-1.814.438-.233.775-.565 1.197-.816.423-.253.855-.537 1.351-.678C5.002 6.258 6.18 5.496 7.313 4.68c.077-.056.087-.16.154-.294l-1.152.113c.235-.713.713-1.174 1.556-1.372 1.976-.466 3.94-.987 5.965-1.303 1.292-.2 2.682-.122 4.023-.078 1.23.04 2.45.22 3.68.308 2.194.158 4.399.256 6.587.448 3.387.296 6.753.77 10.15.941 3.217.161 6.462.02 9.695.025 3.515.004 7.039.143 10.542.004 4.409-.174 8.602.52 12.83 1.219 1.291.213 2.65.25 3.983.285 1.983.05 3.976.1 5.953.015 2.823-.121 5.642-.312 8.388-.876 2.096-.429 4.259-.707 6.31-1.222 2.662-.667 5.367-.357 8.057-.41.559-.01 1.12.24 1.693.305 2.774.318 5.45-.08 8.109-.611 1.04-.208 1.921-.6 2.857-.98 1.357-.551 2.996-.738 4.516-1.08 1.546-.35 2.96.181 4.434.331.749.077 1.524.01 2.287.01.674 0 .983.233 1.06.732.093.597-.52.602-.99.675-2.23.345-4.5.575-6.691 1.02-1.349.272-2.634.787-3.85 1.313-2.482 1.073-5.294 1.444-7.984 2.048-1.409.317-2.973.25-4.454.419-3.277.37-6.508-.214-9.761-.187-1.237.01-2.479.271-3.702.473-2.37.392-4.726.821-7.088 1.238-.252.045-.51.092-.743.172-2.847.985-5.879 1.109-8.907.802-4.293-.434-8.586-.926-12.818-1.594-2.513-.396-4.913-.103-7.364.029-1.1.059-2.27-.032-3.285.214-2.615.634-5.281.357-7.927.424-.465.011-.935-.17-1.41-.23-.83-.103-1.667-.174-2.498-.272-.417-.05-.849-.089-1.235-.202-2.997-.88-6.041-1.432-9.322-1.028-.91.112-1.891-.113-2.823-.05-1.973.133-3.934.366-5.904.53-.477.04-1.044.071-1.444-.074-1.037-.377-2.022-.166-3.056-.063-1.992.2-4 .32-5.998.491-2.51.217-4.893.67-6.968 1.814-.676.373-1.551.556-2.403.848", fill "#D9CE5F" ] + ] + ] + , li "nav__link" + [ a [ href "getstarted.html" ] + [ text "Get Started" ] + ] + , li "nav__link" + [ a [ href "/" ] + [ "Tutorial" ] + ] + , li [ "nav__link" ] + [ a [ href "/" ] + [ "Sandbox" ] + ] + ] + ] + , nav [ className "nav-mobile", attribute "x-show" "isOpen" ] + [ div "nav-mobile__wrapper" + [ a [ className "nav-mobile__link", href "#" ] + [ "Home" ] + , a [ className "nav-mobile__link", href "getstarted.html" ] + [ "Get Started" ] + , a [ className "nav-mobile__link", href "#" ] + [ "Tutorial" ] + , a [ className "nav-mobile__link", href "#" ] + [ "Sandbox" ] + ] + ] + ] + + +hero :: Html m a +hero = div_ + [ div "hero" + [ div "hero-title-wrapper" + [ p "hero-title" + [ "A new Haskell UI" ] + , p "hero-title" + [ "programming" ] + , p "hero-title" + [ "paradigm" ] + ] + , div "hero-button" + [ div "split-button hero-button__content" + [ button [ className "split-button__button", type' "button" ] + [ span "split-button__split1 hero-button__split" + [ "Get Started" ] + , span "split-button__split2 hero-button__split" + [ img' [ alt "Right arrow", src "assets/right-arrow-white.svg" ] + ] + ] + ] + ] + ] + , img' [ alt "desert cactus", className "hero-bg", src "/assets/hero_image.svg" ] + , img' [ alt "desert cactus", className "hero-bg-mobile", src "/assets/mobile/hero_image.svg" ] + ] + + +footerView :: Html m a +footerView = + footer_ + [ div "footer__wrapper" + [ a [ href "/" ] + [ img' [ alt "shpadoinkle logo", src "assets/try_shpadoinkle_footer_logo.svg" ] + ] + , div "flex flex-col justify-between w-full gap-8 pt-12 leading-8 md:flex-row md:w-2/5 md:gap-0" + [ div_ + [ p "footer__nav__category" + [ text "Quick Links" ] + , nav_ + [ ul "footer__nav__links" + [ li_ + [ a [ href "/" ] + [ "Sandbox" ] + ] + , li_ + [ a [ href "/" ] + [ "Get Started" ] + ] + , li_ + [ a [ href "/" ] + [ "Tutorial" ] + ] + , li_ + [ a [ href "/" ] + [ "Sandbox" ] + ] + ] + ] + ] + , div_ + [ p "footer__nav__category" + [ "Community" ] + , nav_ + [ ul "footer__nav__links" + [ li_ + [ a [ href "/" ] + [ "Reddit" ] + ] + , li_ + [ a [ href "/" ] + [ "Twitter" ] + ] + , li_ + [ a [ href "/" ] + [ "Hackage" ] + ] + ] + ] + ] + ] + , div "footer_copyright" + [ "© 2020-2021 Platonic Systems" ] + ] + ] + + +featureSection :: Html m a +featureSection = div "feature__section" + [ div "feature__wrapper" + [ p "feature__title" + [ "A programming model for declarative, high performance user interface." ] + , div "feature__cards" + [ div "feature__card" + [ div "feature__card__content" + [ div "feature__card__heading" + [ img [ alt "rabbit", src "assets/fast_icon.svg" ] + [] + , p "feature__card__title" + [ "Fast" ] + ] + , p "feature__card__description" + [ "Shpadoinkle is fast because it does little work. The renderer is modular, so you can always benefit from the latest advances in virtual DOM rendering. " ] + ] + ] + , div "feature__card" + [ div "feature__card__content" + [ div "feature__card__heading" + [ img' [ alt "playing cards", src "assets/declarative_icon.svg" ] + , p "feature__card__title" + [ "Declarative" ] + ] + , p "feature__card__description" + [ "Your Shpadoinkle code is high-level. You need not worry about low-level details, causality, or when DOM nodes get replaced. " ] + ] + ] + , div "feature__card" + [ div "feature__card__content" + [ div "feature__card__heading" + [ img' [ alt "puzzle pieces", src "assets/composable_icon.svg" ] + , p "feature__card__title" + [ "Composable" ] + ] + , p "feature__card__description" + [ "With Shpadoinkle, there is no need to associate model update code with DOM locations. This avoids elaborate passing of messages and payloads. Components are highly composable. " ] + ] + ] + , div "feature__card" + [ div "feature__card__content" + [ div "feature__card__heading" + [ img' [ alt "revolver gun", src "assets/reliable_icon.svg" ] + , p "feature__card__title" + [ "Reliable" ] + ] + , p "feature__card__description" + [ "Shpadoinkle UIs are composed of components with no side-effects. So, runtime errors are exceedingly rare. Code is easy to test because model updates are pure functions. " ] + ] + ] + , div "feature__card" + [ div "feature__card__content" + [ div "feature__card__heading" + [ img' [ alt "whip", src "assets/lorem_ipsum_logo.svg" ] + , p "feature__card__title" + [ "Lorem Ipsum" ] + ] + , p "feature__card__description" + [ "Shpadoinkle UIs are composed of components with no side-effects. So, runtime errors are exceedingly rare. Code is easy to test because model updates are pure functions. " ] + ] + ] + ] + ] + , div "feature-button" + [ div "split-button feature-button__content" + [ button [ className "split-button__button", type' "button" ] + [ span "split-button__split1 feature-button__split" + [ "Checkout the getting started guide" ] + , span "split-button__split2 feature-button__split" + [ img' [ alt "Right arrow", src "assets/right-arrow.svg" ] + ] + ] + ] + ] + , img' [ alt "background transition", className "transition", src "assets/orange_purple_transition.svg" ] + ] + + +componentsSection :: Html m a +componentsSection = div "components__section" + [ div "component" + [ div "component__info" + [ h2 "component__info__title" + [ "A Component" ] + , p "component__info__description" + [ "A Shpadoinkle component ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. " ] + ] + , div "component__example" + [ div' "component__example__code" + ] + ] + , div "component" + [ div "component__info" + [ h2 "component__info__title" + [ "A Component" ] + , p "component__info__description" + [ text "A Shpadoinkle component ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. " ] + ] + , div "component__example" + [ div' "component__example__code" + ] + ] + , div "component" + [ div "component__info" + [ h2 "component__info__title" + [ "A Component" ] + , p "component__info__description" + [ "A Shpadoinkle component ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. " ] + ] + , div "component__example" + [ div' "component__example__code" + ] + ] + , img' [ alt "background transition", className "transition", src "assets/purple_black_transition.svg" ] + ] + + +template :: Html m a -> Html m a +template content = + html [ lang "en" ] + [ headView + , body "top-border" $ navView <> + [ main_ + [ hero + , featureSection + , componentsSection + ] + , footerView + ] + ] diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index cafa63d4..b6c38029 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -13,41 +13,43 @@ module Shpadoinkle.Website.View where -import Control.Lens ((%~), (&), (.~), (<>~), - (^.)) -import Control.Monad.IO.Class (MonadIO, liftIO) +import Control.Lens ((%~), (&), (.~), (<>~), + (^.)) +import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Reader -import Data.Generics.Labels () -import Data.List (intersperse) +import Data.Generics.Labels () +import Data.List (intersperse) import Data.String -import Data.Text as T (Text, drop, length, - null, pack, replace, - replicate, splitOn, - strip, unpack) -import Data.Text.Lazy (fromStrict, toStrict) -import Data.Text.Lazy.Encoding (decodeUtf8, encodeUtf8) +import Data.Text as T (Text, drop, length, + null, pack, + replace, replicate, + splitOn, strip, + unpack) +import Data.Text.Lazy (fromStrict, toStrict) +import Data.Text.Lazy.Encoding (decodeUtf8, encodeUtf8) import GHCJS.DOM import GHCJS.DOM.Document -import GHCJS.DOM.WindowOrWorkerGlobalScope (setTimeout) -import Language.Javascript.JSaddle hiding (JSM, MonadJSM) -import Prelude hiding (div) -import Servant.API (toUrlPiece) +import GHCJS.DOM.WindowOrWorkerGlobalScope (setTimeout) +import Language.Javascript.JSaddle hiding (JSM, MonadJSM) +import Prelude hiding (div) +import Servant.API (toUrlPiece) import Shpadoinkle -import Shpadoinkle.Html as H -import Shpadoinkle.Isreal.Types as Swan -import Shpadoinkle.Lens (onRecord, onRecordEndo, - onSum) -import Shpadoinkle.Router (toHydration) -import Shpadoinkle.Run (Env, entrypoint) +import Shpadoinkle.Html as H +import Shpadoinkle.Isreal.Types as Swan +import Shpadoinkle.Lens (onRecord, onRecordEndo, + onSum) +import Shpadoinkle.Router (toHydration) +import Shpadoinkle.Run (Env, entrypoint) import Shpadoinkle.Template.TH -import qualified Shpadoinkle.Website.Tailwind as T +import Shpadoinkle.Website.Partials.Template +import qualified Shpadoinkle.Website.Tailwind as T import Shpadoinkle.Website.Types import Shpadoinkle.Website.Types.Hoogle import Shpadoinkle.Widgets.Form.Dropdown -import qualified Shpadoinkle.Widgets.Form.Input as I -import Shpadoinkle.Widgets.Types (Pick (One), Search (..), - withOptions) +import qualified Shpadoinkle.Widgets.Form.Input as I +import Shpadoinkle.Widgets.Types (Pick (One), Search (..), + withOptions) default (Text) -- GitLab From 838e0893cd08c43380120b5ab44b2984d307bd60 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Tue, 6 Apr 2021 19:27:09 -0600 Subject: [PATCH 020/116] mass build out in progress --- .../Shpadoinkle/Website/Component/Hoogle.hs | 25 +++ .../Website/Component/LiveExample.hs | 86 ++++++++ website/Shpadoinkle/Website/Page/Home.hs | 157 ++++++++++++++ .../Shpadoinkle/Website/Partials/Template.hs | 185 +++------------- website/Shpadoinkle/Website/View.hs | 201 ------------------ 5 files changed, 295 insertions(+), 359 deletions(-) create mode 100644 website/Shpadoinkle/Website/Component/Hoogle.hs create mode 100644 website/Shpadoinkle/Website/Component/LiveExample.hs create mode 100644 website/Shpadoinkle/Website/Page/Home.hs diff --git a/website/Shpadoinkle/Website/Component/Hoogle.hs b/website/Shpadoinkle/Website/Component/Hoogle.hs new file mode 100644 index 00000000..fe0d30f9 --- /dev/null +++ b/website/Shpadoinkle/Website/Component/Hoogle.hs @@ -0,0 +1,25 @@ +module Shpadoinkle.Website.Component.Hoogle where + + +import Shpadoinkle.Website.Types +import Shpadoinkle.Html + + +hoogleWidget :: forall m. Hooglable m => MonadJSM m => Hoogle -> Html m Hoogle +hoogleWidget hoo = + div + [ onInputM (query . Search) ] + [ onRecord #search $ I.search [] (search hoo) + , onRecord #targets $ div [ class' T.p_2 ] [ dropdown theme defConfig $ targets hoo ] + ] + + where + + query :: Search -> m (Hoogle -> Hoogle) + query ss = do + ts <- findTargets ss + return $ #targets <>~ Nothing `withOptions` ts + + +targetWidget :: Target -> Html m a +targetWidget = div' . pure . innerHTML . pack . targetItem diff --git a/website/Shpadoinkle/Website/Component/LiveExample.hs b/website/Shpadoinkle/Website/Component/LiveExample.hs new file mode 100644 index 00000000..5097aa48 --- /dev/null +++ b/website/Shpadoinkle/Website/Component/LiveExample.hs @@ -0,0 +1,86 @@ +module Shpadoinkle.Website.Component.LiveExample where + + +example :: MonadJSM m => ExampleEffects m => Example -> Html m Example +example (Example cc token nonce state') = div "example" + [ div [ className "mirror-wrap" + , onInput $ const $ #state .~ ELoading + ] + [ mapC (mappend compileExample . onRecord #inputHaskell) $ mirror cc ] + , case state' of + EReady -> iframe + [ src $ "https://isreal.shpadoinkle.org/" + <> toUrlPiece (Swan.serve token) + <> "/index.html?nonce=" + <> pack (show $ nonce - 1) + ] [] + EError e -> errorMessages e + ELoading -> text "Loading..." + ] + + +compileExample :: MonadJSM m => ExampleEffects m => Continuation m Example +compileExample = kleisli $ \(Example cc token nonce _) -> do + mutex <- ask + cur <- readTVarIO mutex + atomically $ writeTVar mutex $ Just cc + case cur of + Just _ -> return done + Nothing -> do + res <- compile token nonce $ exampleTemplate cc + cur' <- readTVarIO mutex + case cur' of + Nothing -> ret res 1 + Just cc' | cc' == cc -> do + atomically $ writeTVar mutex Nothing + ret res 1 + Just cc' -> do + atomically $ writeTVar mutex Nothing + res' <- compile token (nonce + 1) $ exampleTemplate cc' + ret res' 2 + where + ret res n = + return . pur $ (#snowNonce %~ (+ n)) . case res of + Left e -> #state .~ EError e + Right _ -> #state .~ EReady + + +errorMessages :: CompileError -> Html m a +errorMessages = div "errors" . fmap singleError . breakError . replace bunkWarning "" . unCompileError + where + singleError e = + let offLineNumber = Prelude.head . splitOn ":" $ T.drop 1 e + lineNumber = pack . show . subtract topOffset . read $ unpack offLineNumber + pad = let padSize = T.length offLineNumber - T.length lineNumber in if padSize > 0 then T.replicate padSize " " else "" + replaceLineNumber = replace (offLineNumber <> ":") (lineNumber <> ":") . replace (offLineNumber <> " |") (pad <> lineNumber <> " |") + in div "error" . intersperse br'_ . fmap (text . replaceLineNumber) . splitOn "\n" $ T.drop 1 e + breakError = filter (not . T.null . strip) . splitOn "Main.hs" + bunkWarning = "Warning: don't know how to find change monitoring files for the installed\npackage databases for ghcjs\n" + + +mirrorCfg :: Code -> JSM Object +mirrorCfg (Code cc) = do + cfg <- obj + (cfg <# "mode") "haskell" + (cfg <# "value") $ toStrict $ decodeUtf8 cc + (cfg <# "indentUnit") (4 :: Int) + (cfg <# "matchBrackets") True + (cfg <# "lineNumbers") True + return cfg + + +mirror :: Code -> Html m Code +mirror cc = baked $ do + (notify, stream) <- mkGlobalMailboxAfforded constUpdate + doc <- currentDocumentUnchecked + container <- toJSVal =<< createElement doc "div" + cfg <- mirrorCfg cc + cm <- jsg2 "CodeMirror" container cfg + _ <- cm ^. js2 "on" "change" (fun $ \_ _ _ -> do + jsv <- cm ^. js0 "getValue" + raw :: Maybe Text <- fromJSVal jsv + maybe (pure ()) (notify . Code . encodeUtf8 . fromStrict) raw + ) + window <- currentWindowUnchecked + _ <- setTimeout window (fun $ \_ _ _ -> () <$ cm ^. js0 "refresh") (Just 33) + return (RawNode container, stream) diff --git a/website/Shpadoinkle/Website/Page/Home.hs b/website/Shpadoinkle/Website/Page/Home.hs new file mode 100644 index 00000000..9f3d024f --- /dev/null +++ b/website/Shpadoinkle/Website/Page/Home.hs @@ -0,0 +1,157 @@ +{-# LANGUAGE OverloadedStrings #-} + + +module Shpadoinkle.Website.Page.Home where + + +import Shpadoinkle.Html + + +view :: [Html m a] +view = [hero, featureSection, componentsSection] + + +hero :: Html m a +hero = div_ + [ div "hero" + [ div "hero-title-wrapper" + [ p "hero-title" + [ "A new Haskell UI" ] + , p "hero-title" + [ "programming" ] + , p "hero-title" + [ "paradigm" ] + ] + , div "hero-button" + [ div "split-button hero-button__content" + [ button [ className "split-button__button", type' "button" ] + [ span "split-button__split1 hero-button__split" + [ "Get Started" ] + , span "split-button__split2 hero-button__split" + [ img' [ alt "Right arrow", src "assets/right-arrow-white.svg" ] + ] + ] + ] + ] + ] + , img' [ alt "desert cactus", className "hero-bg", src "/assets/hero_image.svg" ] + , img' [ alt "desert cactus", className "hero-bg-mobile", src "/assets/mobile/hero_image.svg" ] + ] + + +featureSection :: Html m a +featureSection = div "feature__section" + [ div "feature__wrapper" + [ p "feature__title" + [ "A programming model for declarative, high performance user interface." ] + , div "feature__cards" + [ div "feature__card" + [ div "feature__card__content" + [ div "feature__card__heading" + [ img [ alt "rabbit", src "assets/fast_icon.svg" ] + [] + , p "feature__card__title" + [ "Fast" ] + ] + , p "feature__card__description" + [ "Shpadoinkle is fast because it does little work. The renderer is modular, so you can always benefit from the latest advances in virtual DOM rendering. " ] + ] + ] + , div "feature__card" + [ div "feature__card__content" + [ div "feature__card__heading" + [ img' [ alt "playing cards", src "assets/declarative_icon.svg" ] + , p "feature__card__title" + [ "Declarative" ] + ] + , p "feature__card__description" + [ "Your Shpadoinkle code is high-level. You need not worry about low-level details, causality, or when DOM nodes get replaced. " ] + ] + ] + , div "feature__card" + [ div "feature__card__content" + [ div "feature__card__heading" + [ img' [ alt "puzzle pieces", src "assets/composable_icon.svg" ] + , p "feature__card__title" + [ "Composable" ] + ] + , p "feature__card__description" + [ "With Shpadoinkle, there is no need to associate model update code with DOM locations. This avoids elaborate passing of messages and payloads. Components are highly composable. " ] + ] + ] + , div "feature__card" + [ div "feature__card__content" + [ div "feature__card__heading" + [ img' [ alt "revolver gun", src "assets/reliable_icon.svg" ] + , p "feature__card__title" + [ "Reliable" ] + ] + , p "feature__card__description" + [ "Shpadoinkle UIs are composed of components with no side-effects. So, runtime errors are exceedingly rare. Code is easy to test because model updates are pure functions. " ] + ] + ] + , div "feature__card" + [ div "feature__card__content" + [ div "feature__card__heading" + [ img' [ alt "whip", src "assets/lorem_ipsum_logo.svg" ] + , p "feature__card__title" + [ "Lorem Ipsum" ] + ] + , p "feature__card__description" + [ "Shpadoinkle UIs are composed of components with no side-effects. So, runtime errors are exceedingly rare. Code is easy to test because model updates are pure functions. " ] + ] + ] + ] + ] + , div "feature-button" + [ div "split-button feature-button__content" + [ button [ className "split-button__button", type' "button" ] + [ span "split-button__split1 feature-button__split" + [ "Checkout the getting started guide" ] + , span "split-button__split2 feature-button__split" + [ img' [ alt "Right arrow", src "assets/right-arrow.svg" ] + ] + ] + ] + ] + , img' [ alt "background transition", className "transition", src "assets/orange_purple_transition.svg" ] + ] + + +componentsSection :: Html m a +componentsSection = div "components__section" + [ div "component" + [ div "component__info" + [ h2 "component__info__title" + [ "A Component" ] + , p "component__info__description" + [ "A Shpadoinkle component ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. " ] + ] + , div "component__example" + [ div' "component__example__code" + ] + ] + , div "component" + [ div "component__info" + [ h2 "component__info__title" + [ "A Component" ] + , p "component__info__description" + [ text "A Shpadoinkle component ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. " ] + ] + , div "component__example" + [ div' "component__example__code" + ] + ] + , div "component" + [ div "component__info" + [ h2 "component__info__title" + [ "A Component" ] + , p "component__info__description" + [ "A Shpadoinkle component ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. " ] + ] + , div "component__example" + [ div' "component__example__code" + ] + ] + , img' [ alt "background transition", className "transition", src "assets/purple_black_transition.svg" ] + ] diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index a1f7b596..5b90fbcd 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -1,16 +1,32 @@ {-# LANGUAGE OverloadedStrings #-} + + module Shpadoinkle.Website.Partials.Template where +import Data.Text + import Shpadoinkle.Html -headView :: Html m a -headView = head_ +codemirrorCDN :: Text -> Text +codemirrorCDN = mappend "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.59.4/" + + +headView :: Env -> Frontend -> Html m a +headView ev fe = head_ [ meta' [ charset "UTF-8" ] , link' [ href "favicon.svg", rel "icon", type_ "image/svg+xml" ] , meta' [ content "width=device-width, initial-scale=1.0", name "viewport" ] + , link' + [ rel "stylesheet" + , href $ codemirrorCDN "codemirror.min.css" + ] + , toHydration fe , title_ [ "Shpadoinkle" ] + , script [ src $ entrypoint ev ] [] + , script [ src $ codemirrorCDN "codemirror.min.js" ] [] + , script [ src $ codemirrorCDN "mode/haskell/haskell.min.js" ] [] ] @@ -59,34 +75,6 @@ navView ] -hero :: Html m a -hero = div_ - [ div "hero" - [ div "hero-title-wrapper" - [ p "hero-title" - [ "A new Haskell UI" ] - , p "hero-title" - [ "programming" ] - , p "hero-title" - [ "paradigm" ] - ] - , div "hero-button" - [ div "split-button hero-button__content" - [ button [ className "split-button__button", type' "button" ] - [ span "split-button__split1 hero-button__split" - [ "Get Started" ] - , span "split-button__split2 hero-button__split" - [ img' [ alt "Right arrow", src "assets/right-arrow-white.svg" ] - ] - ] - ] - ] - ] - , img' [ alt "desert cactus", className "hero-bg", src "/assets/hero_image.svg" ] - , img' [ alt "desert cactus", className "hero-bg-mobile", src "/assets/mobile/hero_image.svg" ] - ] - - footerView :: Html m a footerView = footer_ @@ -146,134 +134,15 @@ footerView = ] -featureSection :: Html m a -featureSection = div "feature__section" - [ div "feature__wrapper" - [ p "feature__title" - [ "A programming model for declarative, high performance user interface." ] - , div "feature__cards" - [ div "feature__card" - [ div "feature__card__content" - [ div "feature__card__heading" - [ img [ alt "rabbit", src "assets/fast_icon.svg" ] - [] - , p "feature__card__title" - [ "Fast" ] - ] - , p "feature__card__description" - [ "Shpadoinkle is fast because it does little work. The renderer is modular, so you can always benefit from the latest advances in virtual DOM rendering. " ] - ] - ] - , div "feature__card" - [ div "feature__card__content" - [ div "feature__card__heading" - [ img' [ alt "playing cards", src "assets/declarative_icon.svg" ] - , p "feature__card__title" - [ "Declarative" ] - ] - , p "feature__card__description" - [ "Your Shpadoinkle code is high-level. You need not worry about low-level details, causality, or when DOM nodes get replaced. " ] - ] - ] - , div "feature__card" - [ div "feature__card__content" - [ div "feature__card__heading" - [ img' [ alt "puzzle pieces", src "assets/composable_icon.svg" ] - , p "feature__card__title" - [ "Composable" ] - ] - , p "feature__card__description" - [ "With Shpadoinkle, there is no need to associate model update code with DOM locations. This avoids elaborate passing of messages and payloads. Components are highly composable. " ] - ] - ] - , div "feature__card" - [ div "feature__card__content" - [ div "feature__card__heading" - [ img' [ alt "revolver gun", src "assets/reliable_icon.svg" ] - , p "feature__card__title" - [ "Reliable" ] - ] - , p "feature__card__description" - [ "Shpadoinkle UIs are composed of components with no side-effects. So, runtime errors are exceedingly rare. Code is easy to test because model updates are pure functions. " ] - ] - ] - , div "feature__card" - [ div "feature__card__content" - [ div "feature__card__heading" - [ img' [ alt "whip", src "assets/lorem_ipsum_logo.svg" ] - , p "feature__card__title" - [ "Lorem Ipsum" ] - ] - , p "feature__card__description" - [ "Shpadoinkle UIs are composed of components with no side-effects. So, runtime errors are exceedingly rare. Code is easy to test because model updates are pure functions. " ] - ] - ] +template :: Env -> Frontend -> Html m a -> Html m a +template ev fe content = html [ lang "en" ] + [ headView ev fe + , body "top-border" $ navView <> + [ main_ + [ hero + , featureSection + , componentsSection ] + , footerView ] - , div "feature-button" - [ div "split-button feature-button__content" - [ button [ className "split-button__button", type' "button" ] - [ span "split-button__split1 feature-button__split" - [ "Checkout the getting started guide" ] - , span "split-button__split2 feature-button__split" - [ img' [ alt "Right arrow", src "assets/right-arrow.svg" ] - ] - ] - ] - ] - , img' [ alt "background transition", className "transition", src "assets/orange_purple_transition.svg" ] ] - - -componentsSection :: Html m a -componentsSection = div "components__section" - [ div "component" - [ div "component__info" - [ h2 "component__info__title" - [ "A Component" ] - , p "component__info__description" - [ "A Shpadoinkle component ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. " ] - ] - , div "component__example" - [ div' "component__example__code" - ] - ] - , div "component" - [ div "component__info" - [ h2 "component__info__title" - [ "A Component" ] - , p "component__info__description" - [ text "A Shpadoinkle component ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. " ] - ] - , div "component__example" - [ div' "component__example__code" - ] - ] - , div "component" - [ div "component__info" - [ h2 "component__info__title" - [ "A Component" ] - , p "component__info__description" - [ "A Shpadoinkle component ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. " ] - ] - , div "component__example" - [ div' "component__example__code" - ] - ] - , img' [ alt "background transition", className "transition", src "assets/purple_black_transition.svg" ] - ] - - -template :: Html m a -> Html m a -template content = - html [ lang "en" ] - [ headView - , body "top-border" $ navView <> - [ main_ - [ hero - , featureSection - , componentsSection - ] - , footerView - ] - ] diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index b6c38029..46202030 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -59,179 +59,6 @@ domain :: IsString s => s domain = "https://shpadoinkle.org" -hero :: Html m a -hero = - div - [ class' $ T.w_full <> T.bg_gray_900 <> T.p_10 - <> T.text_center <> T.text_white - ] - [ h1 - [ class' $ T.uppercase <> T.tracking_widest <> T.text_6xl <> T.font_thin - ] - [ "Shpadoinkle!" - ] - , h2 [ class' T.text_2xl ] - [ "I think I know precisely what I mean" ] - , button [ class' $ T.px_3 <> T.py_2 <> T.my_5 <> T.bg_blue_500 ] [ "Get Started" ] - ] - - -pitch :: Html m a -pitch = - div - [ class' $ T.mx_auto <> T.text_white <> T.max_w_3xl <> T.my_5 - <> T.flex <> T.space_x_4 <> T.justify_between - ] - [ div - [ class' $ T.bg_gray_900 <> T.p_4 <> "w-1/3" ] - [ h3_ [ "Declarative" ] - , "Because types!" - ] - - , div - [ class' $ T.bg_gray_900 <> T.p_4 <> "w-1/3" ] - [ h3_ [ "Modular" ] - , "Because packages!" - ] - - , div - [ class' $ T.bg_gray_900 <> T.p_4 <> "w-1/3" ] - [ h3_ [ "Performant" ] - , "Because simple!" - ] - ] - - -top :: Hooglable m => MonadJSM m => Hoogle -> Html m Hoogle -top hoo = - header - [ class' $ T.bg_gray_900 <> T.py_2 <> T.px_5 - ] - [ div - [ class' $ T.text_center <> T.flex - <> T.items_center <> T.justify_between <> T.max_w_3xl - <> T.mx_auto - ] - [ img' $ let d = 50 in [ src "/static/logo.png", width d, height d ] - , nav [ class' $ T.text_white <> T.flex <> T.space_x_4 ] - [ a [ href $ domain <> "/docs/index.html" ] [ "Docs" ] - , a [ href $ domain <> "/docs/tutorial/index.html" ] [ "Tutorial" ] - , a [ href "" ] [ "Community" ] - , hoogleWidget hoo - ] - ] - ] - - -mirrorCfg :: Code -> JSM Object -mirrorCfg (Code cc) = do - cfg <- obj - (cfg <# "mode") "haskell" - (cfg <# "value") $ toStrict $ decodeUtf8 cc - (cfg <# "indentUnit") (4 :: Int) - (cfg <# "matchBrackets") True - (cfg <# "lineNumbers") True - return cfg - - -mirror :: Code -> Html m Code -mirror cc = baked $ do - (notify, stream) <- mkGlobalMailboxAfforded constUpdate - doc <- currentDocumentUnchecked - container <- toJSVal =<< createElement doc "div" - cfg <- mirrorCfg cc - cm <- jsg2 "CodeMirror" container cfg - _ <- cm ^. js2 "on" "change" (fun $ \_ _ _ -> do - jsv <- cm ^. js0 "getValue" - raw :: Maybe Text <- fromJSVal jsv - maybe (pure ()) (notify . Code . encodeUtf8 . fromStrict) raw - ) - window <- currentWindowUnchecked - _ <- setTimeout window (fun $ \_ _ _ -> () <$ cm ^. js0 "refresh") (Just 33) - return (RawNode container, stream) - - -example :: MonadJSM m => ExampleEffects m => Example -> Html m Example -example (Example cc token nonce state') = div "example" - [ div [ className "mirror-wrap" - , onInput $ const $ #state .~ ELoading - ] - [ mapC (mappend compileExample . onRecord #inputHaskell) $ mirror cc ] - , case state' of - EReady -> iframe - [ src $ "https://isreal.shpadoinkle.org/" - <> toUrlPiece (Swan.serve token) - <> "/index.html?nonce=" - <> pack (show $ nonce - 1) - ] [] - EError e -> errorMessages e - ELoading -> text "Loading..." - ] - - -compileExample :: MonadJSM m => ExampleEffects m => Continuation m Example -compileExample = kleisli $ \(Example cc token nonce _) -> do - mutex <- ask - cur <- readTVarIO mutex - atomically $ writeTVar mutex $ Just cc - case cur of - Just _ -> return done - Nothing -> do - res <- compile token nonce $ exampleTemplate cc - cur' <- readTVarIO mutex - case cur' of - Nothing -> ret res 1 - Just cc' | cc' == cc -> do - atomically $ writeTVar mutex Nothing - ret res 1 - Just cc' -> do - atomically $ writeTVar mutex Nothing - res' <- compile token (nonce + 1) $ exampleTemplate cc' - ret res' 2 - where - ret res n = - return . pur $ (#snowNonce %~ (+ n)) . case res of - Left e -> #state .~ EError e - Right _ -> #state .~ EReady - - -errorMessages :: CompileError -> Html m a -errorMessages = div "errors" . fmap singleError . breakError . replace bunkWarning "" . unCompileError - where - singleError e = - let offLineNumber = Prelude.head . splitOn ":" $ T.drop 1 e - lineNumber = pack . show . subtract topOffset . read $ unpack offLineNumber - pad = let padSize = T.length offLineNumber - T.length lineNumber in if padSize > 0 then T.replicate padSize " " else "" - replaceLineNumber = replace (offLineNumber <> ":") (lineNumber <> ":") . replace (offLineNumber <> " |") (pad <> lineNumber <> " |") - in div "error" . intersperse br'_ . fmap (text . replaceLineNumber) . splitOn "\n" $ T.drop 1 e - breakError = filter (not . T.null . strip) . splitOn "Main.hs" - bunkWarning = "Warning: don't know how to find change monitoring files for the installed\npackage databases for ghcjs\n" - - -hoogleWidget :: forall m. Hooglable m => MonadJSM m => Hoogle -> Html m Hoogle -hoogleWidget hoo = - div - [ onInputM (query . Search) ] - [ onRecord #search $ I.search [] (search hoo) - , onRecord #targets $ div [ class' T.p_2 ] [ dropdown theme defConfig $ targets hoo ] - ] - - where - - query :: Search -> m (Hoogle -> Hoogle) - query ss = do - ts <- findTargets ss - return $ #targets <>~ Nothing `withOptions` ts - - - theme :: Dropdown 'One Target -> Theme m 'One Target - theme _ = Theme div_ (const mempty) div_ targetWidget - - -targetWidget :: Target -> Html m a -targetWidget = div' . pure . innerHTML . pack . targetItem - - home :: Hooglable m => ExampleEffects m => MonadJSM m => Home -> Html m Home home home' = section_ [ onRecordEndo #hoogle top home' @@ -291,34 +118,6 @@ view = \case MFourOhFour -> fourOhFour -template :: Env -> Frontend -> Html m Frontend -> Html m Frontend -template ev fe v = H.html_ - [ H.head_ - [ H.link' - [ H.rel "stylesheet" - , H.href "/static/tailwind.min.css" - ] - , H.link' - [ H.rel "stylesheet" - , H.href "/static/style.css" - ] - , H.link' - [ H.rel "stylesheet" - , H.href $ codemirrorCDN "codemirror.min.css" - ] - , H.meta [ H.charset "ISO-8859-1" ] [] - , toHydration fe - , H.script [ H.src $ entrypoint ev ] [] - , H.script [ H.src $ codemirrorCDN "codemirror.min.js" ] [] - , H.script [ H.src $ codemirrorCDN "mode/haskell/haskell.min.js" ] [] - ] - , H.body_ - [ v - ] - ] - where codemirrorCDN = mappend "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.59.4/" - - compileExamples :: MonadJSM m => ExampleEffects m => Home -> m Home compileExamples home' = do hw <- runContinuation compileExample $ home' ^. #examples . #helloWorld -- GitLab From adeda739c8431ef53bbb48a37cd39c1460c9cf43 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Sat, 10 Apr 2021 14:00:34 -0600 Subject: [PATCH 021/116] asciidoc embedding, website wireup progress --- html/Shpadoinkle/Html.hs | 7 +- html/Shpadoinkle/Html/Property.hs | 2 +- template/Shpadoinkle/Template/TH.hs | 12 +- template/package.yaml | 1 + .../Website/Component/LiveExample.hs | 39 +++++- .../Shpadoinkle/Website/Page/Documentation.hs | 60 ++++++++ website/Shpadoinkle/Website/Page/Home.hs | 6 +- .../Shpadoinkle/Website/Partials/Template.hs | 37 +++-- website/Shpadoinkle/Website/Run.hs | 76 +++++----- website/Shpadoinkle/Website/View.hs | 132 ++++++------------ website/package.yaml | 4 + 11 files changed, 223 insertions(+), 153 deletions(-) create mode 100644 website/Shpadoinkle/Website/Page/Documentation.hs diff --git a/html/Shpadoinkle/Html.hs b/html/Shpadoinkle/Html.hs index 45e399d4..758cf880 100644 --- a/html/Shpadoinkle/Html.hs +++ b/html/Shpadoinkle/Html.hs @@ -12,9 +12,10 @@ module Shpadoinkle.Html import Shpadoinkle (Html, Prop, baked, dataProp, - injectProps, listen, listenC, - listenRaw, listener, - listenerProp, mapProps, text) + h, injectProps, listen, + listenC, listenRaw, listener, + listenerProp, mapProps, text, + textProp) import Shpadoinkle.Html.Element import Shpadoinkle.Html.Event import Shpadoinkle.Html.Event.Debounce diff --git a/html/Shpadoinkle/Html/Property.hs b/html/Shpadoinkle/Html/Property.hs index 5cd72dba..c6c6bbe7 100644 --- a/html/Shpadoinkle/Html/Property.hs +++ b/html/Shpadoinkle/Html/Property.hs @@ -89,7 +89,7 @@ $(msum <$> mapM mkTextProp , "max", "min", "step", "wrap", "target", "download", "hreflang", "media", "ping", "shape", "coords" , "alt", "preload", "poster", "name'", "kind'", "srclang", "sandbox", "srcdoc", "align" , "headers", "scope", "datetime", "pubdate", "manifest", "contextmenu", "draggable" - , "dropzone", "itemprop", "charset", "content", "property", "innerHTML" + , "dropzone", "itemprop", "charset", "content", "property", "innerHTML", "lang" ]) $(msum <$> mapM mkIntProp diff --git a/template/Shpadoinkle/Template/TH.hs b/template/Shpadoinkle/Template/TH.hs index 15d52c0c..2a858a88 100644 --- a/template/Shpadoinkle/Template/TH.hs +++ b/template/Shpadoinkle/Template/TH.hs @@ -5,13 +5,23 @@ module Shpadoinkle.Template.TH where -import Data.Text (Text, cons, unpack) +import Data.Text (Text, cons, pack, replace, unpack) import Data.Text.IO import Language.Haskell.TH.Syntax import Prelude hiding (readFile) +import System.Exit (ExitCode (..)) +import System.Process.Text (readProcessWithExitCode) import Text.HTML.Parser (Attr (..), Token (..), parseTokens) +embedAsciidoc :: FilePath -> Q Exp +embedAsciidoc path = do + out@(exit, _, _) <- runIO $ readProcessWithExitCode "asciidoctor" ["-s", path ] "" + case exit of + ExitSuccess -> embedHtml $ unpack $ replace ".adoc" ".html" $ pack path + ExitFailure _ -> error $ show out + + embedHtml :: FilePath -> Q Exp embedHtml path = do ts <- runIO $ parseTokens <$> readFile path diff --git a/template/package.yaml b/template/package.yaml index f5ee7bb1..139d790a 100644 --- a/template/package.yaml +++ b/template/package.yaml @@ -32,6 +32,7 @@ dependencies: - text >= 1.2.3 && < 1.3 - template-haskell - html-parse + - process-extras - Shpadoinkle - Shpadoinkle-backend-static diff --git a/website/Shpadoinkle/Website/Component/LiveExample.hs b/website/Shpadoinkle/Website/Component/LiveExample.hs index 5097aa48..c4a0e977 100644 --- a/website/Shpadoinkle/Website/Component/LiveExample.hs +++ b/website/Shpadoinkle/Website/Component/LiveExample.hs @@ -1,7 +1,38 @@ +{-# LANGUAGE ExtendedDefaultRules #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE OverloadedLabels #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# OPTIONS_GHC -fno-warn-type-defaults #-} + + module Shpadoinkle.Website.Component.LiveExample where -example :: MonadJSM m => ExampleEffects m => Example -> Html m Example +import Control.Lens +import Control.Monad.Reader (MonadReader (ask)) +import Data.List as List (intersperse) +import Data.Text as T +import Data.Text.Lazy (fromStrict, toStrict) +import Data.Text.Lazy.Encoding (decodeUtf8, encodeUtf8) +import GHCJS.DOM (currentDocumentUnchecked, + currentWindowUnchecked) +import GHCJS.DOM.Document (createElement) +import GHCJS.DOM.WindowOrWorkerGlobalScope (setTimeout) +import Language.Javascript.JSaddle +import Prelude hiding (div) +import Servant.API (toUrlPiece) +import Shpadoinkle +import Shpadoinkle.Html +import Shpadoinkle.Isreal.Types as Swan +import Shpadoinkle.Lens +import Shpadoinkle.Website.Types + + +default (Text) + + +example :: (MonadJSM m, ExampleEffects m) => Example -> Html m Example example (Example cc token nonce state') = div "example" [ div [ className "mirror-wrap" , onInput $ const $ #state .~ ELoading @@ -19,7 +50,7 @@ example (Example cc token nonce state') = div "example" ] -compileExample :: MonadJSM m => ExampleEffects m => Continuation m Example +compileExample :: (MonadJSM m, ExampleEffects m) => Continuation m Example compileExample = kleisli $ \(Example cc token nonce _) -> do mutex <- ask cur <- readTVarIO mutex @@ -53,8 +84,8 @@ errorMessages = div "errors" . fmap singleError . breakError . replace bunkWarni lineNumber = pack . show . subtract topOffset . read $ unpack offLineNumber pad = let padSize = T.length offLineNumber - T.length lineNumber in if padSize > 0 then T.replicate padSize " " else "" replaceLineNumber = replace (offLineNumber <> ":") (lineNumber <> ":") . replace (offLineNumber <> " |") (pad <> lineNumber <> " |") - in div "error" . intersperse br'_ . fmap (text . replaceLineNumber) . splitOn "\n" $ T.drop 1 e - breakError = filter (not . T.null . strip) . splitOn "Main.hs" + in div "error" . List.intersperse br'_ . fmap (text . replaceLineNumber) . splitOn "\n" $ T.drop 1 e + breakError = Prelude.filter (not . T.null . strip) . splitOn "Main.hs" bunkWarning = "Warning: don't know how to find change monitoring files for the installed\npackage databases for ghcjs\n" diff --git a/website/Shpadoinkle/Website/Page/Documentation.hs b/website/Shpadoinkle/Website/Page/Documentation.hs new file mode 100644 index 00000000..4472e158 --- /dev/null +++ b/website/Shpadoinkle/Website/Page/Documentation.hs @@ -0,0 +1,60 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE TemplateHaskell #-} + + +module Shpadoinkle.Website.Page.Documentation where + + +import Data.Text +import Shpadoinkle.Html +import Shpadoinkle.Template.TH +import Shpadoinkle.Website.Types + + +concepts + , gsIndex, gsAddingToYourProject, gsExtendAnExample + , rpIndex, rpCore, rpConsole, rpBackends, rpHtml, rpLens, rpRouter, rpWidgets + , rtIndex, rtCalculator, rtImmediateExecution, rtComposing :: Html m a +concepts = div_ $(embedAsciidoc "./docs/concepts.adoc") +gsIndex = div_ $(embedAsciidoc "./docs/getting-started/index.adoc") +gsAddingToYourProject = div_ $(embedAsciidoc "./docs/getting-started/adding-to-your-project.adoc") +gsExtendAnExample = div_ $(embedAsciidoc "./docs/getting-started/extend-an-example.adoc") +rpIndex = div_ $(embedAsciidoc "./docs/packages/index.adoc") +rpCore = div_ $(embedAsciidoc "./docs/packages/core.adoc") +rpConsole = div_ $(embedAsciidoc "./docs/packages/console.adoc") +rpBackends = div_ $(embedAsciidoc "./docs/packages/backends.adoc") +rpHtml = div_ $(embedAsciidoc "./docs/packages/html.adoc") +rpLens = div_ $(embedAsciidoc "./docs/packages/lens.adoc") +rpRouter = div_ $(embedAsciidoc "./docs/packages/router.adoc") +rpWidgets = div_ $(embedAsciidoc "./docs/packages/widgets.adoc") +rtIndex = div_ $(embedAsciidoc "./docs/tutorial/index.adoc") +rtCalculator = div_ $(embedAsciidoc "./docs/tutorial/calculator.adoc") +rtImmediateExecution = div_ $(embedAsciidoc "./docs/tutorial/immediate-execution.adoc") +rtComposing = div_ $(embedAsciidoc "./docs/tutorial/composing.adoc") + + +gettingStarted :: RGettingStarted -> Html m a +gettingStarted = \case + RGSIndex -> gsIndex + RGSAddingToYourProject -> gsAddingToYourProject + RGSExtendAnExample -> gsExtendAnExample + + +packages :: RPackages -> Html m a +packages = \case + RPIndex -> rpIndex + RPCore -> rpCore + RPConsole -> rpConsole + RPBackends -> rpBackends + RPHtml -> rpHtml + RPLens -> rpLens + RPRouter -> rpRouter + RPWidgets -> rpWidgets + + +tutorial :: RTutorial -> Html m a +tutorial = \case + RTIndex -> rtIndex + RTCalculator -> rtCalculator + RTImmediateExecution -> rtImmediateExecution + RTComposing -> rtComposing diff --git a/website/Shpadoinkle/Website/Page/Home.hs b/website/Shpadoinkle/Website/Page/Home.hs index 9f3d024f..fa0bff69 100644 --- a/website/Shpadoinkle/Website/Page/Home.hs +++ b/website/Shpadoinkle/Website/Page/Home.hs @@ -4,11 +4,13 @@ module Shpadoinkle.Website.Page.Home where +import Prelude hiding (div, span) import Shpadoinkle.Html +import Shpadoinkle.Website.Types -view :: [Html m a] -view = [hero, featureSection, componentsSection] +view :: Home -> [Html m Home] +view _ = [hero, featureSection, componentsSection] hero :: Html m a diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index 5b90fbcd..89d6333d 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -5,8 +5,12 @@ module Shpadoinkle.Website.Partials.Template where import Data.Text - +import Prelude hiding (div) +import Shpadoinkle (h) import Shpadoinkle.Html +import Shpadoinkle.Router +import Shpadoinkle.Run (Env (..), entrypoint) +import Shpadoinkle.Website.Types codemirrorCDN :: Text -> Text @@ -16,14 +20,14 @@ codemirrorCDN = mappend "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.59. headView :: Env -> Frontend -> Html m a headView ev fe = head_ [ meta' [ charset "UTF-8" ] - , link' [ href "favicon.svg", rel "icon", type_ "image/svg+xml" ] - , meta' [ content "width=device-width, initial-scale=1.0", name "viewport" ] + , link' [ href "favicon.svg", rel "icon", type' "image/svg+xml" ] + , meta' [ content "width=device-width, initial-scale=1.0", name' "viewport" ] , link' [ rel "stylesheet" , href $ codemirrorCDN "codemirror.min.css" ] , toHydration fe - , title_ [ "Shpadoinkle" ] + , h "title" [] [ "Shpadoinkle" ] , script [ src $ entrypoint ev ] [] , script [ src $ codemirrorCDN "codemirror.min.js" ] [] , script [ src $ codemirrorCDN "mode/haskell/haskell.min.js" ] [] @@ -31,7 +35,7 @@ headView ev fe = head_ navView :: [Html m a] -navView +navView = [ nav "nav" [ a [ href "/" ] [ img' [ alt "shpadoinkle logo", className "nav__logo", src "assets/landing_logo.svg" ] @@ -40,27 +44,24 @@ navView , img' [ alt "menu toggle icon", className "nav__menu-icon", src "assets/menu_close_icon.svg" ] , ul "nav__links" [ li "nav__link" - [ a [ href "/" ] - [ "Home" ] - , svg [ className "nav__link-underline", viewBox "0 0 129 10", ("xmlns", "http://www.w3.org/2000/svg") ] - [ path' [ d "M.365 10c-.59-1.096-.508-1.247.562-1.814.438-.233.775-.565 1.197-.816.423-.253.855-.537 1.351-.678C5.002 6.258 6.18 5.496 7.313 4.68c.077-.056.087-.16.154-.294l-1.152.113c.235-.713.713-1.174 1.556-1.372 1.976-.466 3.94-.987 5.965-1.303 1.292-.2 2.682-.122 4.023-.078 1.23.04 2.45.22 3.68.308 2.194.158 4.399.256 6.587.448 3.387.296 6.753.77 10.15.941 3.217.161 6.462.02 9.695.025 3.515.004 7.039.143 10.542.004 4.409-.174 8.602.52 12.83 1.219 1.291.213 2.65.25 3.983.285 1.983.05 3.976.1 5.953.015 2.823-.121 5.642-.312 8.388-.876 2.096-.429 4.259-.707 6.31-1.222 2.662-.667 5.367-.357 8.057-.41.559-.01 1.12.24 1.693.305 2.774.318 5.45-.08 8.109-.611 1.04-.208 1.921-.6 2.857-.98 1.357-.551 2.996-.738 4.516-1.08 1.546-.35 2.96.181 4.434.331.749.077 1.524.01 2.287.01.674 0 .983.233 1.06.732.093.597-.52.602-.99.675-2.23.345-4.5.575-6.691 1.02-1.349.272-2.634.787-3.85 1.313-2.482 1.073-5.294 1.444-7.984 2.048-1.409.317-2.973.25-4.454.419-3.277.37-6.508-.214-9.761-.187-1.237.01-2.479.271-3.702.473-2.37.392-4.726.821-7.088 1.238-.252.045-.51.092-.743.172-2.847.985-5.879 1.109-8.907.802-4.293-.434-8.586-.926-12.818-1.594-2.513-.396-4.913-.103-7.364.029-1.1.059-2.27-.032-3.285.214-2.615.634-5.281.357-7.927.424-.465.011-.935-.17-1.41-.23-.83-.103-1.667-.174-2.498-.272-.417-.05-.849-.089-1.235-.202-2.997-.88-6.041-1.432-9.322-1.028-.91.112-1.891-.113-2.823-.05-1.973.133-3.934.366-5.904.53-.477.04-1.044.071-1.444-.074-1.037-.377-2.022-.166-3.056-.063-1.992.2-4 .32-5.998.491-2.51.217-4.893.67-6.968 1.814-.676.373-1.551.556-2.403.848", fill "#D9CE5F" ] - ] + [ a [ href "/" ] [ "Home" ] + , img' [ className "nav__link-underline", src "assets/underline.svg" ] ] , li "nav__link" [ a [ href "getstarted.html" ] - [ text "Get Started" ] + [ "Get Started" ] ] , li "nav__link" [ a [ href "/" ] [ "Tutorial" ] ] - , li [ "nav__link" ] + , li "nav__link" [ a [ href "/" ] [ "Sandbox" ] ] ] ] - , nav [ className "nav-mobile", attribute "x-show" "isOpen" ] + , nav "nav-mobile" [ div "nav-mobile__wrapper" [ a [ className "nav-mobile__link", href "#" ] [ "Home" ] @@ -85,7 +86,7 @@ footerView = , div "flex flex-col justify-between w-full gap-8 pt-12 leading-8 md:flex-row md:w-2/5 md:gap-0" [ div_ [ p "footer__nav__category" - [ text "Quick Links" ] + [ "Quick Links" ] , nav_ [ ul "footer__nav__links" [ li_ @@ -135,14 +136,10 @@ footerView = template :: Env -> Frontend -> Html m a -> Html m a -template ev fe content = html [ lang "en" ] +template ev fe content' = html [ lang "en" ] [ headView ev fe , body "top-border" $ navView <> - [ main_ - [ hero - , featureSection - , componentsSection - ] + [ content' , footerView ] ] diff --git a/website/Shpadoinkle/Website/Run.hs b/website/Shpadoinkle/Website/Run.hs index 5b613ba6..89969412 100644 --- a/website/Shpadoinkle/Website/Run.hs +++ b/website/Shpadoinkle/Website/Run.hs @@ -11,55 +11,59 @@ module Main where -import Control.Monad.Catch (MonadThrow) -import Control.Monad.IO.Class (MonadIO (..)) +import Control.Monad.Catch (MonadThrow) +import Control.Monad.IO.Class (MonadIO (..)) import Control.Monad.Reader -import Data.Proxy (Proxy (..)) -import Data.Text (Text) -import Servant.API (type (:<|>) ((:<|>))) +import Data.Proxy (Proxy (..)) +import Data.Text (Text) +import Servant.API (type (:<|>) ((:<|>))) #ifndef ghcjs_HOST_OS -import Servant.Server as Servant (serve) +import Servant.Server as Servant (serve) #endif #ifndef ghcjs_HOST_OS -import Shpadoinkle (JSM, MonadJSM, - MonadUnliftIO (..), TVar, - constUpdate, liftJSM, - newTVarIO) +import Shpadoinkle (JSM, MonadJSM, + MonadUnliftIO (..), + TVar, constUpdate, + liftJSM, newTVarIO) #else -import Shpadoinkle (JSM, MonadUnliftIO (..), - TVar, constUpdate, liftJSM, - newTVarIO) +import Shpadoinkle (JSM, MonadUnliftIO (..), + TVar, constUpdate, + liftJSM, newTVarIO) #endif -import Shpadoinkle.Backend.Snabbdom (runSnabbdom, stage) -import Shpadoinkle.Isreal.Types as Swan (API, Code, - CompileError, - SnowNonce, - SnowToken (..)) -import Shpadoinkle.Router (fullPageSPA', withHydration) -import Shpadoinkle.Router.Client (BaseUrl (..), ClientEnv (..), - ClientM, Scheme (Https), - client, runXHR') -import Shpadoinkle.Widgets.Types (Search (..)) +import Shpadoinkle.Backend.Snabbdom (runSnabbdom, stage) +import Shpadoinkle.Isreal.Types as Swan (API, Code, + CompileError, + SnowNonce, + SnowToken (..)) +import Shpadoinkle.Router (fullPageSPA', + withHydration) +import Shpadoinkle.Router.Client (BaseUrl (..), + ClientEnv (..), ClientM, + Scheme (Https), client, + runXHR') +import Shpadoinkle.Widgets.Types (Search (..)) #ifndef ghcjs_HOST_OS -import Shpadoinkle.Router.Server (serveUI) -import Shpadoinkle.Run (Env (Dev), liveWithBackend, - runJSorWarp) +import Shpadoinkle.Router.Server (serveUI) +import Shpadoinkle.Run (Env (Dev), + liveWithBackend, + runJSorWarp) #else -import Shpadoinkle.Run (runJSorWarp) +import Shpadoinkle.Run (runJSorWarp) #endif import Shpadoinkle.DeveloperTools -import Shpadoinkle.Website.Types (Frontend, Hooglable (..), - HoogleAPI, - Route (RFourOhFour), SPA, - Swan (..), routes) -import Shpadoinkle.Website.Types.Hoogle (Target) +import Shpadoinkle.Website.Types (Frontend, + Hooglable (..), + HoogleAPI, + Route (RFourOhFour), + SPA, Swan (..), routes) +import Shpadoinkle.Website.Types.Hoogle (Target) #ifndef ghcjs_HOST_OS -import Shpadoinkle.Website.View (start, startJS, template, - view) +import Shpadoinkle.Website.Partials.Template (template) +import Shpadoinkle.Website.View (start, startJS, view) #else -import Shpadoinkle.Website.View (start, startJS, view) +import Shpadoinkle.Website.View (start, startJS, view) #endif @@ -115,7 +119,7 @@ main = runJSorWarp 8080 app #ifndef ghcjs_HOST_OS dev :: IO () -dev = liveWithBackend 8080 app . pure $ Servant.serve (Proxy @ (SPA IO)) $ serveUI @ (SPA IO) "." (\r -> do +dev = liveWithBackend 8080 app . pure $ Servant.serve (Proxy @ (SPA IO)) $ serveUI @(SPA IO) "." (\r -> do x <- start r return $ template @App Dev x (view x)) routes diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index 46202030..c03bd59a 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -5,51 +5,56 @@ {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TemplateHaskell #-} - {-# OPTIONS_GHC -fno-warn-type-defaults #-} module Shpadoinkle.Website.View where -import Control.Lens ((%~), (&), (.~), (<>~), - (^.)) -import Control.Monad.IO.Class (MonadIO, liftIO) +import Control.Lens ((%~), (&), (.~), + (<>~), (^.)) +import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Reader -import Data.Generics.Labels () -import Data.List (intersperse) +import Data.Generics.Labels () +import Data.List (intersperse) import Data.String -import Data.Text as T (Text, drop, length, - null, pack, - replace, replicate, - splitOn, strip, - unpack) -import Data.Text.Lazy (fromStrict, toStrict) -import Data.Text.Lazy.Encoding (decodeUtf8, encodeUtf8) +import Data.Text as T (Text, drop, + length, null, + pack, replace, + replicate, + splitOn, strip, + unpack) +import Data.Text.Lazy (fromStrict, + toStrict) +import Data.Text.Lazy.Encoding (decodeUtf8, + encodeUtf8) import GHCJS.DOM import GHCJS.DOM.Document -import GHCJS.DOM.WindowOrWorkerGlobalScope (setTimeout) -import Language.Javascript.JSaddle hiding (JSM, MonadJSM) -import Prelude hiding (div) -import Servant.API (toUrlPiece) +import GHCJS.DOM.WindowOrWorkerGlobalScope (setTimeout) +import Language.Javascript.JSaddle hiding (JSM, + MonadJSM) +import Prelude hiding (div) +import Servant.API (toUrlPiece) import Shpadoinkle -import Shpadoinkle.Html as H -import Shpadoinkle.Isreal.Types as Swan -import Shpadoinkle.Lens (onRecord, onRecordEndo, - onSum) -import Shpadoinkle.Router (toHydration) -import Shpadoinkle.Run (Env, entrypoint) +import Shpadoinkle.Html as H +import Shpadoinkle.Isreal.Types as Swan +import Shpadoinkle.Lens (onRecord, + onRecordEndo, onSum) +import Shpadoinkle.Router (toHydration) +import Shpadoinkle.Run (Env, entrypoint) import Shpadoinkle.Template.TH - +import Shpadoinkle.Website.Component.LiveExample +import qualified Shpadoinkle.Website.Page.Documentation as Documentation +import qualified Shpadoinkle.Website.Page.Home as Home import Shpadoinkle.Website.Partials.Template -import qualified Shpadoinkle.Website.Tailwind as T +import qualified Shpadoinkle.Website.Tailwind as T import Shpadoinkle.Website.Types import Shpadoinkle.Website.Types.Hoogle import Shpadoinkle.Widgets.Form.Dropdown -import qualified Shpadoinkle.Widgets.Form.Input as I -import Shpadoinkle.Widgets.Types (Pick (One), Search (..), - withOptions) +import qualified Shpadoinkle.Widgets.Form.Input as I +import Shpadoinkle.Widgets.Types (Pick (One), + Search (..), + withOptions) default (Text) @@ -59,63 +64,18 @@ domain :: IsString s => s domain = "https://shpadoinkle.org" -home :: Hooglable m => ExampleEffects m => MonadJSM m => Home -> Html m Home -home home' = section_ - [ onRecordEndo #hoogle top home' - , hero - , pitch - , onRecordEndo (#examples . #helloWorld) example home' - , onRecordEndo (#examples . #counter) example home' - ] - - -concepts - , gsIndex, gsAddingToYourProject, gsExtendAnExample - , rpIndex, rpCore, rpConsole, rpBackends, rpHtml, rpLens, rpRouter, rpWidgets - , rtIndex, rtCalculator, rtImmediateExecution, rtComposing - , fourOhFour :: Html m a -concepts = div_ $(embedHtml "./docs/concepts.html") -gsIndex = div_ $(embedHtml "./docs/getting-started/index.html") -gsAddingToYourProject = div_ $(embedHtml "./docs/getting-started/adding-to-your-project.html") -gsExtendAnExample = div_ $(embedHtml "./docs/getting-started/extend-an-example.html") -rpIndex = div_ $(embedHtml "./docs/packages/index.html") -rpCore = div_ $(embedHtml "./docs/packages/core.html") -rpConsole = div_ $(embedHtml "./docs/packages/console.html") -rpBackends = div_ $(embedHtml "./docs/packages/backends.html") -rpHtml = div_ $(embedHtml "./docs/packages/html.html") -rpLens = div_ $(embedHtml "./docs/packages/lens.html") -rpRouter = div_ $(embedHtml "./docs/packages/router.html") -rpWidgets = div_ $(embedHtml "./docs/packages/widgets.html") -rtIndex = div_ $(embedHtml "./docs/tutorial/index.html") -rtCalculator = div_ $(embedHtml "./docs/tutorial/calculator.html") -rtImmediateExecution = div_ $(embedHtml "./docs/tutorial/immediate-execution.html") -rtComposing = div_ $(embedHtml "./docs/tutorial/composing.html") -fourOhFour = h2_ [ "404" ] - - -view :: Hooglable m => ExampleEffects m => MonadJSM m => Frontend -> Html m Frontend -view = \case - MHome x -> #_MHome `onSum` home x - MConcepts -> concepts - MGettingStarted gsr -> case gsr of - RGSIndex -> gsIndex - RGSAddingToYourProject -> gsAddingToYourProject - RGSExtendAnExample -> gsExtendAnExample - MPackages pr -> case pr of - RPIndex -> rpIndex - RPCore -> rpCore - RPConsole -> rpConsole - RPBackends -> rpBackends - RPHtml -> rpHtml - RPLens -> rpLens - RPRouter -> rpRouter - RPWidgets -> rpWidgets - MTutorial tutr -> case tutr of - RTIndex -> rtIndex - RTCalculator -> rtCalculator - RTImmediateExecution -> rtImmediateExecution - RTComposing -> rtComposing - MFourOhFour -> fourOhFour +fourOhFour :: Html m a +fourOhFour = h2_ [ "404" ] + + +view :: (Hooglable m, ExampleEffects m, MonadJSM m) => Frontend -> Html m Frontend +view = h "main" [] . \case + MHome x -> onSum #_MHome <$> Home.view x + MConcepts -> [ Documentation.concepts ] + MGettingStarted gsr -> [ Documentation.gettingStarted gsr ] + MPackages pr -> [ Documentation.packages pr ] + MTutorial tutr -> [ Documentation.tutorial tutr ] + MFourOhFour -> [ fourOhFour ] compileExamples :: MonadJSM m => ExampleEffects m => Home -> m Home diff --git a/website/package.yaml b/website/package.yaml index 08d6e18f..b81c6344 100644 --- a/website/package.yaml +++ b/website/package.yaml @@ -71,6 +71,10 @@ executables: run: main: Shpadoinkle/Website/Run.hs other-modules: + - Shpadoinkle.Website.Component.LiveExample + - Shpadoinkle.Website.Partials.Template + - Shpadoinkle.Website.Page.Home + - Shpadoinkle.Website.Page.Documentation - Shpadoinkle.Website.Tailwind - Shpadoinkle.Website.Types - Shpadoinkle.Website.Types.Hoogle -- GitLab From 39de20cf490177767685aa536b14a0148be89dcf Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Sat, 10 Apr 2021 14:04:11 -0600 Subject: [PATCH 022/116] no more asciidoctor script --- nix/asciidoctor.sh | 2 -- 1 file changed, 2 deletions(-) delete mode 100755 nix/asciidoctor.sh diff --git a/nix/asciidoctor.sh b/nix/asciidoctor.sh deleted file mode 100755 index 72f47099..00000000 --- a/nix/asciidoctor.sh +++ /dev/null @@ -1,2 +0,0 @@ -echo "Processing asciidoctor..." -find . -name "*.adoc" -print0 | xargs -0 asciidoctor -s -- GitLab From 249df94bbca28c4abe98f4be6b2d224d1b93445f Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Sat, 10 Apr 2021 14:17:55 -0600 Subject: [PATCH 023/116] wrapper --- nix/overlay-shpadoinkle.nix | 4 +--- website/Shpadoinkle/Website/Partials/Template.hs | 12 ++++++++---- website/Shpadoinkle/Website/View.hs | 2 +- website/docs/default.nix | 15 --------------- 4 files changed, 10 insertions(+), 23 deletions(-) delete mode 100644 website/docs/default.nix diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index 4249712a..5b0f9e60 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -151,9 +151,7 @@ in { }); websiteOverride = x: x.overrideAttrs (old: { - preBuild = '' - ln -s ${import ../website/docs {}} docs - ''; + buildInputs = old.buildInputs ++ [ super.asciidoctor ]; }); hpkgs = { diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index 89d6333d..342fae92 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -138,8 +138,12 @@ footerView = template :: Env -> Frontend -> Html m a -> Html m a template ev fe content' = html [ lang "en" ] [ headView ev fe - , body "top-border" $ navView <> - [ content' - , footerView - ] + , body "top-border" [ content' ] + ] + + +wrapper :: [Html m a] -> [Html m a] +wrapper content' = navView <> + [ h "main" [] content' + , footerView ] diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index c03bd59a..5e1805be 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -69,7 +69,7 @@ fourOhFour = h2_ [ "404" ] view :: (Hooglable m, ExampleEffects m, MonadJSM m) => Frontend -> Html m Frontend -view = h "main" [] . \case +view = div_ . wrapper . \case MHome x -> onSum #_MHome <$> Home.view x MConcepts -> [ Documentation.concepts ] MGettingStarted gsr -> [ Documentation.gettingStarted gsr ] diff --git a/website/docs/default.nix b/website/docs/default.nix deleted file mode 100644 index 29a7665e..00000000 --- a/website/docs/default.nix +++ /dev/null @@ -1,15 +0,0 @@ -{ chan ? (import ../../nix/chan.nix) }: -let pkgs = import ../../nix/pkgs.nix { inherit chan; }; - util = import ../../nix/util.nix { inherit pkgs; }; - gitignore = util.gitignore [ "*.html" ]; -in - pkgs.stdenv.mkDerivation { - name = "docs"; - src = gitignore ./.; - buildInputs = [ pkgs.asciidoctor ]; - installPhase = '' - ${builtins.readFile ../../nix/asciidoctor.sh} - rm **/*.adoc - cp -r . $out - ''; - } -- GitLab From 6a79b021fa58114b0e0ac504b9bb59d9292f2d00 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Sat, 10 Apr 2021 14:26:48 -0600 Subject: [PATCH 024/116] roars to life --- .../Shpadoinkle/Website/Partials/Template.hs | 14 +- website/assets/1.svg | 14 + website/assets/2.svg | 14 + website/assets/3.svg | 14 + website/assets/caution_icon.svg | 11 + website/assets/composable_icon.svg | 19 + website/assets/current_page_underline.svg | 9 + website/assets/declarative_icon.svg | 24 + website/assets/fast_icon.svg | 14 + website/assets/get_started_header_bg.svg | 14 + website/assets/hero_image.svg | 1337 +++++++++++++++++ website/assets/info_icon.svg | 11 + website/assets/landing_bottom_transition.svg | 14 + website/assets/landing_footer_logo.svg | 104 ++ website/assets/landing_logo.svg | 106 ++ website/{static => assets}/logo.png | Bin website/assets/lorem_ipsum_logo.svg | 17 + website/assets/menu_close_icon.svg | 11 + website/assets/menu_close_icon_yellow.svg | 11 + website/assets/menu_icon.svg | 9 + .../assets/mobile/get_started_header_bg.svg | 7 + website/assets/mobile/get_started_logo.svg | 106 ++ website/assets/mobile/hero_image.svg | 717 +++++++++ website/assets/mobile/landing_footer_logo.svg | 104 ++ website/assets/mobile/landing_logo.svg | 106 ++ .../mobile/try_shpadoinkle_footer_logo.svg | 104 ++ .../mobile/try_shpadoinkle_header_image.svg | 393 +++++ .../mobile/try_shpadoinkle_header_logo.svg | 106 ++ .../mobile/try_shpadoinkle_top_transition.svg | 17 + website/assets/orange_purple_transition.svg | 11 + website/assets/purple_black_transition.svg | 10 + website/assets/reliable_icon.svg | 21 + website/assets/right-arrow-white.svg | 11 + website/assets/right-arrow.svg | 11 + website/assets/style.css | 1 + website/{static => assets}/tailwind.min.css | 0 .../assets/try_shpadoinkle_footer_logo.svg | 104 ++ .../try_shpadoinkle_footer_transition.svg | 14 + .../assets/try_shpadoinkle_top_transition.svg | 17 + website/static/style.css | 14 - 40 files changed, 3622 insertions(+), 19 deletions(-) create mode 100644 website/assets/1.svg create mode 100644 website/assets/2.svg create mode 100644 website/assets/3.svg create mode 100644 website/assets/caution_icon.svg create mode 100644 website/assets/composable_icon.svg create mode 100644 website/assets/current_page_underline.svg create mode 100644 website/assets/declarative_icon.svg create mode 100644 website/assets/fast_icon.svg create mode 100644 website/assets/get_started_header_bg.svg create mode 100644 website/assets/hero_image.svg create mode 100644 website/assets/info_icon.svg create mode 100644 website/assets/landing_bottom_transition.svg create mode 100644 website/assets/landing_footer_logo.svg create mode 100644 website/assets/landing_logo.svg rename website/{static => assets}/logo.png (100%) create mode 100644 website/assets/lorem_ipsum_logo.svg create mode 100644 website/assets/menu_close_icon.svg create mode 100644 website/assets/menu_close_icon_yellow.svg create mode 100644 website/assets/menu_icon.svg create mode 100644 website/assets/mobile/get_started_header_bg.svg create mode 100644 website/assets/mobile/get_started_logo.svg create mode 100644 website/assets/mobile/hero_image.svg create mode 100644 website/assets/mobile/landing_footer_logo.svg create mode 100644 website/assets/mobile/landing_logo.svg create mode 100644 website/assets/mobile/try_shpadoinkle_footer_logo.svg create mode 100644 website/assets/mobile/try_shpadoinkle_header_image.svg create mode 100644 website/assets/mobile/try_shpadoinkle_header_logo.svg create mode 100644 website/assets/mobile/try_shpadoinkle_top_transition.svg create mode 100644 website/assets/orange_purple_transition.svg create mode 100644 website/assets/purple_black_transition.svg create mode 100644 website/assets/reliable_icon.svg create mode 100644 website/assets/right-arrow-white.svg create mode 100644 website/assets/right-arrow.svg create mode 100644 website/assets/style.css rename website/{static => assets}/tailwind.min.css (100%) create mode 100644 website/assets/try_shpadoinkle_footer_logo.svg create mode 100644 website/assets/try_shpadoinkle_footer_transition.svg create mode 100644 website/assets/try_shpadoinkle_top_transition.svg delete mode 100644 website/static/style.css diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index 342fae92..bd6da57f 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -26,6 +26,10 @@ headView ev fe = head_ [ rel "stylesheet" , href $ codemirrorCDN "codemirror.min.css" ] + , link' + [ rel "stylesheet" + , href "/assets/style.css" + ] , toHydration fe , h "title" [] [ "Shpadoinkle" ] , script [ src $ entrypoint ev ] [] @@ -38,14 +42,14 @@ navView :: [Html m a] navView = [ nav "nav" [ a [ href "/" ] - [ img' [ alt "shpadoinkle logo", className "nav__logo", src "assets/landing_logo.svg" ] + [ img' [ alt "shpadoinkle logo", className "nav__logo", src "/assets/landing_logo.svg" ] ] - , img' [ alt "menu toggle icon", className "nav__menu-icon", src "assets/menu_icon.svg" ] - , img' [ alt "menu toggle icon", className "nav__menu-icon", src "assets/menu_close_icon.svg" ] + , img' [ alt "menu toggle icon", className "nav__menu-icon", src "/assets/menu_icon.svg" ] + , img' [ alt "menu toggle icon", className "nav__menu-icon", src "/assets/menu_close_icon.svg" ] , ul "nav__links" [ li "nav__link" [ a [ href "/" ] [ "Home" ] - , img' [ className "nav__link-underline", src "assets/underline.svg" ] + , img' [ className "nav__link-underline", src "/assets/underline.svg" ] ] , li "nav__link" [ a [ href "getstarted.html" ] @@ -81,7 +85,7 @@ footerView = footer_ [ div "footer__wrapper" [ a [ href "/" ] - [ img' [ alt "shpadoinkle logo", src "assets/try_shpadoinkle_footer_logo.svg" ] + [ img' [ alt "shpadoinkle logo", src "/assets/try_shpadoinkle_footer_logo.svg" ] ] , div "flex flex-col justify-between w-full gap-8 pt-12 leading-8 md:flex-row md:w-2/5 md:gap-0" [ div_ diff --git a/website/assets/1.svg b/website/assets/1.svg new file mode 100644 index 00000000..6bdce194 --- /dev/null +++ b/website/assets/1.svg @@ -0,0 +1,14 @@ + + + Group 15 + + + + + + 1 + + + + + \ No newline at end of file diff --git a/website/assets/2.svg b/website/assets/2.svg new file mode 100644 index 00000000..182d58fd --- /dev/null +++ b/website/assets/2.svg @@ -0,0 +1,14 @@ + + + Group 16 + + + + + + 2 + + + + + \ No newline at end of file diff --git a/website/assets/3.svg b/website/assets/3.svg new file mode 100644 index 00000000..c42507eb --- /dev/null +++ b/website/assets/3.svg @@ -0,0 +1,14 @@ + + + Group 17 + + + + + + 3 + + + + + \ No newline at end of file diff --git a/website/assets/caution_icon.svg b/website/assets/caution_icon.svg new file mode 100644 index 00000000..f0007724 --- /dev/null +++ b/website/assets/caution_icon.svg @@ -0,0 +1,11 @@ + + + icons8-error + + + + + + + + \ No newline at end of file diff --git a/website/assets/composable_icon.svg b/website/assets/composable_icon.svg new file mode 100644 index 00000000..3da3cad7 --- /dev/null +++ b/website/assets/composable_icon.svg @@ -0,0 +1,19 @@ + + + + Group 24 + Created with Sketch. + + + + + + + + + + + + + + \ No newline at end of file diff --git a/website/assets/current_page_underline.svg b/website/assets/current_page_underline.svg new file mode 100644 index 00000000..0ff653c5 --- /dev/null +++ b/website/assets/current_page_underline.svg @@ -0,0 +1,9 @@ + + + Fill 1 + + + + + + \ No newline at end of file diff --git a/website/assets/declarative_icon.svg b/website/assets/declarative_icon.svg new file mode 100644 index 00000000..db6256c2 --- /dev/null +++ b/website/assets/declarative_icon.svg @@ -0,0 +1,24 @@ + + + + Group 23 + Created with Sketch. + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/website/assets/fast_icon.svg b/website/assets/fast_icon.svg new file mode 100644 index 00000000..934fae55 --- /dev/null +++ b/website/assets/fast_icon.svg @@ -0,0 +1,14 @@ + + + + Group 22 + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/website/assets/get_started_header_bg.svg b/website/assets/get_started_header_bg.svg new file mode 100644 index 00000000..c4817d65 --- /dev/null +++ b/website/assets/get_started_header_bg.svg @@ -0,0 +1,14 @@ + + + + Group 19 + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/website/assets/hero_image.svg b/website/assets/hero_image.svg new file mode 100644 index 00000000..8e8fc64e --- /dev/null +++ b/website/assets/hero_image.svg @@ -0,0 +1,1337 @@ + + + Artboard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/website/assets/info_icon.svg b/website/assets/info_icon.svg new file mode 100644 index 00000000..e60dc0f2 --- /dev/null +++ b/website/assets/info_icon.svg @@ -0,0 +1,11 @@ + + + Shape + + + + + + + + \ No newline at end of file diff --git a/website/assets/landing_bottom_transition.svg b/website/assets/landing_bottom_transition.svg new file mode 100644 index 00000000..32900d05 --- /dev/null +++ b/website/assets/landing_bottom_transition.svg @@ -0,0 +1,14 @@ + + + + Group 19 + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/website/assets/landing_footer_logo.svg b/website/assets/landing_footer_logo.svg new file mode 100644 index 00000000..83f09c34 --- /dev/null +++ b/website/assets/landing_footer_logo.svg @@ -0,0 +1,104 @@ + + + + Group 21 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ® + + + + + \ No newline at end of file diff --git a/website/assets/landing_logo.svg b/website/assets/landing_logo.svg new file mode 100644 index 00000000..0e466088 --- /dev/null +++ b/website/assets/landing_logo.svg @@ -0,0 +1,106 @@ + + + + Group 18 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ® + + + + + \ No newline at end of file diff --git a/website/static/logo.png b/website/assets/logo.png similarity index 100% rename from website/static/logo.png rename to website/assets/logo.png diff --git a/website/assets/lorem_ipsum_logo.svg b/website/assets/lorem_ipsum_logo.svg new file mode 100644 index 00000000..a4885381 --- /dev/null +++ b/website/assets/lorem_ipsum_logo.svg @@ -0,0 +1,17 @@ + + + + Group 26 + Created with Sketch. + + + + + + + + + + + + \ No newline at end of file diff --git a/website/assets/menu_close_icon.svg b/website/assets/menu_close_icon.svg new file mode 100644 index 00000000..a5057b85 --- /dev/null +++ b/website/assets/menu_close_icon.svg @@ -0,0 +1,11 @@ + + + icons8-multiply + + + + + + + + \ No newline at end of file diff --git a/website/assets/menu_close_icon_yellow.svg b/website/assets/menu_close_icon_yellow.svg new file mode 100644 index 00000000..66081635 --- /dev/null +++ b/website/assets/menu_close_icon_yellow.svg @@ -0,0 +1,11 @@ + + + icons8-multiply + + + + + + + + \ No newline at end of file diff --git a/website/assets/menu_icon.svg b/website/assets/menu_icon.svg new file mode 100644 index 00000000..efffb93b --- /dev/null +++ b/website/assets/menu_icon.svg @@ -0,0 +1,9 @@ + + + Shape Copy + + + + + + \ No newline at end of file diff --git a/website/assets/mobile/get_started_header_bg.svg b/website/assets/mobile/get_started_header_bg.svg new file mode 100644 index 00000000..17487d45 --- /dev/null +++ b/website/assets/mobile/get_started_header_bg.svg @@ -0,0 +1,7 @@ + + + Artboard + + + + \ No newline at end of file diff --git a/website/assets/mobile/get_started_logo.svg b/website/assets/mobile/get_started_logo.svg new file mode 100644 index 00000000..f83f575d --- /dev/null +++ b/website/assets/mobile/get_started_logo.svg @@ -0,0 +1,106 @@ + + + + Group 18 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ® + + + + + \ No newline at end of file diff --git a/website/assets/mobile/hero_image.svg b/website/assets/mobile/hero_image.svg new file mode 100644 index 00000000..2b7939d9 --- /dev/null +++ b/website/assets/mobile/hero_image.svg @@ -0,0 +1,717 @@ + + + Artboard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/website/assets/mobile/landing_footer_logo.svg b/website/assets/mobile/landing_footer_logo.svg new file mode 100644 index 00000000..a5f01203 --- /dev/null +++ b/website/assets/mobile/landing_footer_logo.svg @@ -0,0 +1,104 @@ + + + + Group 21 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ® + + + + + \ No newline at end of file diff --git a/website/assets/mobile/landing_logo.svg b/website/assets/mobile/landing_logo.svg new file mode 100644 index 00000000..85dbe792 --- /dev/null +++ b/website/assets/mobile/landing_logo.svg @@ -0,0 +1,106 @@ + + + + Group 18 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ® + + + + + \ No newline at end of file diff --git a/website/assets/mobile/try_shpadoinkle_footer_logo.svg b/website/assets/mobile/try_shpadoinkle_footer_logo.svg new file mode 100644 index 00000000..e505d059 --- /dev/null +++ b/website/assets/mobile/try_shpadoinkle_footer_logo.svg @@ -0,0 +1,104 @@ + + + + Group 4 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ® + + + + + + \ No newline at end of file diff --git a/website/assets/mobile/try_shpadoinkle_header_image.svg b/website/assets/mobile/try_shpadoinkle_header_image.svg new file mode 100644 index 00000000..4f81cb38 --- /dev/null +++ b/website/assets/mobile/try_shpadoinkle_header_image.svg @@ -0,0 +1,393 @@ + + + + Group + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/website/assets/mobile/try_shpadoinkle_header_logo.svg b/website/assets/mobile/try_shpadoinkle_header_logo.svg new file mode 100644 index 00000000..da4eb720 --- /dev/null +++ b/website/assets/mobile/try_shpadoinkle_header_logo.svg @@ -0,0 +1,106 @@ + + + + Group 19 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ® + + + + + \ No newline at end of file diff --git a/website/assets/mobile/try_shpadoinkle_top_transition.svg b/website/assets/mobile/try_shpadoinkle_top_transition.svg new file mode 100644 index 00000000..99c50bae --- /dev/null +++ b/website/assets/mobile/try_shpadoinkle_top_transition.svg @@ -0,0 +1,17 @@ + + + + Group 3 + Created with Sketch. + + + + + + + + + + + + \ No newline at end of file diff --git a/website/assets/orange_purple_transition.svg b/website/assets/orange_purple_transition.svg new file mode 100644 index 00000000..ab10891d --- /dev/null +++ b/website/assets/orange_purple_transition.svg @@ -0,0 +1,11 @@ + + + Artboard + + + + + + + + \ No newline at end of file diff --git a/website/assets/purple_black_transition.svg b/website/assets/purple_black_transition.svg new file mode 100644 index 00000000..7bfb6258 --- /dev/null +++ b/website/assets/purple_black_transition.svg @@ -0,0 +1,10 @@ + + + Artboard + + + + + + + \ No newline at end of file diff --git a/website/assets/reliable_icon.svg b/website/assets/reliable_icon.svg new file mode 100644 index 00000000..d7f99af2 --- /dev/null +++ b/website/assets/reliable_icon.svg @@ -0,0 +1,21 @@ + + + + Group 25 + Created with Sketch. + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/website/assets/right-arrow-white.svg b/website/assets/right-arrow-white.svg new file mode 100644 index 00000000..ac5d7232 --- /dev/null +++ b/website/assets/right-arrow-white.svg @@ -0,0 +1,11 @@ + + + Path + + + + + + + + \ No newline at end of file diff --git a/website/assets/right-arrow.svg b/website/assets/right-arrow.svg new file mode 100644 index 00000000..ff09d376 --- /dev/null +++ b/website/assets/right-arrow.svg @@ -0,0 +1,11 @@ + + + Path + + + + + + + + \ No newline at end of file diff --git a/website/assets/style.css b/website/assets/style.css new file mode 100644 index 00000000..97607c00 --- /dev/null +++ b/website/assets/style.css @@ -0,0 +1 @@ +@font-face{font-family:Adelle;src:url(/assets/Adelle-Regular.a8eabd20.woff2) format('woff2');font-weight:400;font-style:normal}@font-face{font-family:AdelleBold;src:url(/assets/Adelle-Bold.b552b619.woff2) format('woff2');font-weight:400;font-style:normal}/*! modern-normalize v1.0.0 | MIT License | https://github.com/sindresorhus/modern-normalize */*,::after,::before{box-sizing:border-box}:root{-moz-tab-size:4;-o-tab-size:4;tab-size:4}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}body{font-family:system-ui,-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji'}hr{height:0;color:inherit}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Consolas,'Liberation Mono',Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],button{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}button{background-color:transparent;background-image:none}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}fieldset{margin:0;padding:0}ol,ul{list-style:none;margin:0;padding:0}html{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";line-height:1.5}body{font-family:inherit;line-height:inherit}*,::after,::before{box-sizing:border-box;border-width:0;border-style:solid;border-color:currentColor}hr{border-top-width:1px}img{border-style:solid}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#a1a1aa}input:-ms-input-placeholder,textarea:-ms-input-placeholder{opacity:1;color:#a1a1aa}input::placeholder,textarea::placeholder{opacity:1;color:#a1a1aa}button{cursor:pointer}table{border-collapse:collapse}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,optgroup,select,textarea{padding:0;line-height:inherit;color:inherit}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.rounded{border-radius:.25rem}.flex{display:flex}.table{display:table}.table-row{display:table-row}.grid{display:grid}.hidden{display:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.flex-1{flex:1 1 0%}.font-bold{font-family:AdelleBold,Verdana}.font-bold{font-weight:700}.h-32{height:8rem}.text-xs{font-size:.75rem;line-height:1rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.leading-8{line-height:2rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-0{margin-left:0}.mr-4{margin-right:1rem}.mb-4{margin-bottom:1rem}.mb-24{margin-bottom:6rem}.max-w-sm{max-width:24rem}.min-h-screen{min-height:100vh}.opacity-0{opacity:0}.opacity-100{opacity:1}.p-2{padding:.5rem}.p-6{padding:1.5rem}.p-10{padding:2.5rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.px-10{padding-left:2.5rem;padding-right:2.5rem}.py-16{padding-top:4rem;padding-bottom:4rem}.pt-0{padding-top:0}.pt-12{padding-top:3rem}.fixed{position:fixed}.sticky{position:sticky}.top-0{top:0}.right-0{right:0}.bottom-0{bottom:0}*{--tw-shadow:0 0 #0000}*{--tw-ring-inset:var(--tw-empty, );/*!*//*!*/--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59, 130, 246, 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000}.text-white{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.w-full{width:100%}.gap-8{gap:2rem}.gap-10{gap:2.5rem}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.transform{--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;transform:translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-90{--tw-scale-x:.9;--tw-scale-y:.9}.scale-100{--tw-scale-x:1;--tw-scale-y:1}.transition{transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:150ms}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}@-webkit-keyframes spin{to{transform:rotate(360deg)}}@keyframes spin{to{transform:rotate(360deg)}}@-webkit-keyframes ping{100%,75%{transform:scale(2);opacity:0}}@keyframes ping{100%,75%{transform:scale(2);opacity:0}}@-webkit-keyframes pulse{50%{opacity:.5}}@keyframes pulse{50%{opacity:.5}}@-webkit-keyframes bounce{0%,100%{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@keyframes bounce{0%,100%{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}#preamble>.sectionbody>.paragraph>p{padding:1rem 0}.listingblock{padding:2rem 0;overflow-x:scroll}.listingblock>.content>.highlight>code{border:2px solid #e2e0d5;background-color:transparent;padding:15px 10px}div.admonitionblock{position:relative;margin:3rem 0}div.admonitionblock>table>tbody>tr>td.icon{border-radius:18px;color:#fff;display:flex;height:30px;justify-content:flex-start;left:5px;padding:0 1.5rem;position:absolute;top:-15px;width:135px}div.admonitionblock>table>tbody>tr>td.content{padding:20px 10px 15px 1rem}div.admonitionblock.caution{border:2px dashed #ac2a34}div.admonitionblock.caution>table>tbody>tr>td.icon{background:#ac2a34}div.admonitionblock.caution>table>tbody>tr>td.content{color:#ac2a34}div.admonitionblock.note{border:2px dashed #0f406b}div.admonitionblock.note>table>tbody>tr>td.icon{background:#0f406b}div.admonitionblock.note>table>tbody>tr>td.content{color:#0f406b}div.admonitionblock.warning{border:2px dashed #e37743}div.admonitionblock.warning>table>tbody>tr>td.icon{background:#e37743}div.admonitionblock.warning>table>tbody>tr>td.content{color:#e37743}h1,h2,h3,h4,h5{padding:1rem 0}h1{font-size:1.875rem}h2{font-size:1.375rem}h3{font-size:1.5rem}@media (min-width:768px){h1{font-size:3.75rem}h2{font-size:2.625rem}h3{font-size:1.325rem}}.split-button:active{transform:translate(2px,2px)}.started-header{background-image:url(/assets/get_started_header_bg.d41c50d2.svg);height:96px;background-position:center;background-repeat:no-repeat;background-size:cover;margin-bottom:3rem;position:relative;z-index:50}@media (min-width:768px){.started-header{margin-bottom:-3.5rem}}@media (min-width:768px){.started-header{background-image:url(/assets/get_started_header_bg.05d59513.svg);height:156px}}body{--tw-bg-opacity:1;background-color:rgba(237,225,113,var(--tw-bg-opacity));--tw-border-opacity:1;border-color:rgba(217,206,95,var(--tw-border-opacity));border-style:solid;font-family:Adelle,ui-serif}.nav{width:100%}@media (min-width:640px){.nav{max-width:640px}}@media (min-width:768px){.nav{max-width:768px}}@media (min-width:1024px){.nav{max-width:1024px}}@media (min-width:1280px){.nav{max-width:1280px}}@media (min-width:1536px){.nav{max-width:1536px}}.nav{display:flex;justify-content:space-between;margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem;padding-top:2rem;position:relative;width:100%}.nav__logo{width:75%}@media (min-width:768px){.nav__logo{width:18rem}}.nav__menu-icon{display:block;padding-right:.75rem;width:2.5rem}@media (min-width:768px){.nav__menu-icon{display:none}}.nav__links{display:none;position:absolute;right:0;text-transform:uppercase;gap:1.25rem}@media (min-width:768px){.nav__links{display:flex}}.nav__link{display:flex;flex-direction:column;justify-content:center;position:relative;text-align:center;width:7rem}.nav__link-underline{position:absolute;top:1.25rem}.nav__link__underline-docs{padding-top:.5rem;position:absolute;top:1.25rem}.nav-mobile{padding-top:3rem}@media (min-width:640px){.nav-mobile{display:none}}.nav-mobile__wrapper>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.nav-mobile__wrapper{padding-top:.5rem;padding-bottom:1rem}.nav-mobile__link{border-color:transparent;display:block;font-size:1.875rem;line-height:2.25rem;padding-top:.5rem;padding-bottom:.5rem;padding-right:1rem;padding-left:1rem;--tw-text-opacity:1;color:rgba(18,18,18,var(--tw-text-opacity))}.nav-mobile__links>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.nav-mobile__links{padding-top:.5rem;padding-bottom:1rem}.nav-mobile__heading{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity));display:flex;flex-direction:column;justify-content:flex-end;height:5rem;font-size:1.5rem;line-height:2rem;padding-bottom:.75rem;padding-left:1rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:100%}.nav-mobile__heading__title{display:flex;justify-content:space-between;padding-right:1rem}.nav-mobile__heading__close{display:block;padding-right:.75rem}@media (min-width:768px){.nav-mobile__heading__close{display:none}}.nav-mobile__links__level1{padding-left:3rem}.nav-mobile__links__level2{padding-left:1rem}.hero-bg{display:none;margin-top:-21rem;width:100%}@media (min-width:768px){.hero-bg{display:block}}.hero-bg-mobile{display:block;margin-top:-16rem;width:100%}@media (min-width:768px){.hero-bg-mobile{display:none}}.hero{width:100%}@media (min-width:640px){.hero{max-width:640px}}@media (min-width:768px){.hero{max-width:768px}}@media (min-width:1024px){.hero{max-width:1024px}}@media (min-width:1280px){.hero{max-width:1280px}}@media (min-width:1536px){.hero{max-width:1536px}}.hero{margin-left:auto;margin-right:auto;margin-top:3rem;padding-left:1rem;padding-right:1rem;padding-bottom:6rem}@media (min-width:768px){.hero{margin-top:6rem}}.hero-title-wrapper{justify-content:space-between;position:relative}.hero-title{font-size:1.875rem;line-height:2.25rem}@media (min-width:768px){.hero-title{font-size:2.25rem;line-height:2.5rem;line-height:1.625}}@media (min-width:1024px){.hero-title{font-size:3rem;line-height:1}}.hero-button{display:flex;margin-bottom:3rem;padding-top:1.25rem}@media (min-width:768px){.hero-button{padding-top:2.5rem}}.hero-button__content{--tw-border-opacity:1;border-color:rgba(210,199,91,var(--tw-border-opacity));width:13rem}.hero-button__split{--tw-bg-opacity:1;background-color:rgba(18,18,18,var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.feature-button{display:none;justify-content:center;margin-bottom:3rem}@media (min-width:768px){.feature-button{display:flex;padding-top:1.5rem}}.feature-button__content{--tw-border-opacity:1;border-color:rgba(196,91,40,var(--tw-border-opacity));width:24rem}.feature-button__split{--tw-bg-opacity:1;background-color:rgba(241,226,85,var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.split-button{background-color:transparent;border-radius:.75rem;border-style:solid;border-width:2px;display:flex;height:3rem;position:relative}.split-button__button{display:flex;position:absolute;left:-.5rem;top:-.75rem}.split-button__split1{border-radius:.75rem;display:inline-flex;align-items:center;font-weight:500;height:3rem;margin-right:.5rem;padding-top:.75rem;padding-bottom:.75rem;padding-left:1.5rem;padding-right:1.5rem}.split-button__split2{border-radius:.75rem;display:inline-flex;align-items:center;font-weight:500;height:3rem;font-size:1.125rem;line-height:1.75rem;padding-top:.75rem;padding-bottom:.75rem;padding-left:.75rem;padding-right:.75rem;--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.transition{width:100%}.feature__section{width:full;margin-top:-.5rem;background-color:#e37743}.feature__wrapper{width:100%}@media (min-width:640px){.feature__wrapper{max-width:640px}}@media (min-width:768px){.feature__wrapper{max-width:768px}}@media (min-width:1024px){.feature__wrapper{max-width:1024px}}@media (min-width:1280px){.feature__wrapper{max-width:1280px}}@media (min-width:1536px){.feature__wrapper{max-width:1536px}}.feature__wrapper{margin-left:auto;margin-right:auto;padding-left:0;padding-right:0;padding-top:3rem}@media (min-width:768px){.feature__wrapper{padding-left:1rem;padding-right:1rem}}.feature__title{font-size:1.875rem;line-height:2.25rem;margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem;width:100%}@media (min-width:768px){.feature__title{font-size:2.25rem;line-height:2.5rem;padding-left:0;padding-right:0;text-align:center;width:60%}}.feature__cards{display:flex;flex-wrap:wrap;justify-content:center;padding-top:3rem}.feature__card{display:flex;justify-content:center;padding-bottom:1.5rem;width:100%}@media (min-width:768px){.feature__card{margin-bottom:3.5rem;width:50%}}@media (min-width:1024px){.feature__card{width:33.333333%}}.feature__card__content{--tw-bg-opacity:1;background-color:rgba(208,79,34,var(--tw-bg-opacity));border-radius:0;padding-top:1rem;padding-bottom:1rem;padding-left:2rem;padding-right:2rem;width:100%}@media (min-width:768px){.feature__card__content{border-radius:1.5rem;height:20rem;width:20rem}}.feature__card__heading{display:inline-flex;align-items:center;padding-bottom:1.25rem}.feature__card__title{font-size:1.875rem;line-height:2.25rem;margin-left:1rem;margin-bottom:-.5rem}.feature__card__description{font-size:1.125rem;line-height:1.75rem}.components__section{--tw-bg-opacity:1;background-color:rgba(58,52,83,var(--tw-bg-opacity));flex-direction:column;align-items:center;margin-top:-.5rem;padding-top:3rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:100%}.component{width:100%}@media (min-width:640px){.component{max-width:640px}}@media (min-width:768px){.component{max-width:768px}}@media (min-width:1024px){.component{max-width:1024px}}@media (min-width:1280px){.component{max-width:1280px}}@media (min-width:1536px){.component{max-width:1536px}}.component{display:flex;flex-wrap:wrap;align-items:center;margin-left:auto;margin-right:auto;margin-bottom:3rem;padding-left:1rem;padding-right:1rem}.component__info{padding-right:0;padding-bottom:1rem;width:100%}@media (min-width:1024px){.component__info{padding-bottom:0;padding-right:2rem;width:33.333333%}}.component__info__title{padding-bottom:.75rem}.component__example{width:100%}@media (min-width:1024px){.component__example{width:66.666667%}}.component__example__code{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity));border-radius:.75rem;height:12rem}footer{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity));margin-top:-.5rem}.footer__wrapper{width:100%}@media (min-width:640px){.footer__wrapper{max-width:640px}}@media (min-width:768px){.footer__wrapper{max-width:768px}}@media (min-width:1024px){.footer__wrapper{max-width:1024px}}@media (min-width:1280px){.footer__wrapper{max-width:1280px}}@media (min-width:1536px){.footer__wrapper{max-width:1536px}}.footer__wrapper{margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem}.footer__nav{display:flex;flex-direction:column;justify-content:space-between;line-height:2rem;padding-top:3rem;width:100%;gap:2rem}@media (min-width:768px){.footer__nav{flex-direction:row;width:40%;gap:0}}.footer__nav__category{--tw-text-opacity:1;color:rgba(91,56,233,var(--tw-text-opacity))}.footer__nav__links{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));text-transform:uppercase}.footer_copyright{font-size:.875rem;line-height:1.25rem;padding-top:3rem;padding-bottom:3rem;--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.documentation{--tw-bg-opacity:1;background-color:rgba(250,247,230,var(--tw-bg-opacity));--tw-border-opacity:1;border-color:rgba(217,206,95,var(--tw-border-opacity));border-style:solid;line-height:2.25rem}.mobile-mode{background-color:#ede171!important}.top-border{border-top-width:4px}.sidebar{display:flex}.side-nav>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.side-nav{--tw-bg-opacity:1;background-color:rgba(242,239,217,var(--tw-bg-opacity));display:none;flex-direction:column;padding-left:3.5rem;padding-top:5rem;position:relative;width:21rem;z-index:0}@media (min-width:768px){.side-nav{display:flex}}.side-nav__expand>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.side-nav__expand__button{border-radius:.75rem;cursor:pointer;display:flex;align-items:center;height:3.5rem;font-size:1.125rem;line-height:1.75rem;padding-left:1rem;padding-right:1rem}.side-nav__expand__button:hover{--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.side-nav__expand__button{width:100%}.side-nav__link:hover{--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.side-nav__expand__icon{margin-left:auto}.side-nav__expand__list{padding-left:2rem}.side-nav__expand__list2{padding-left:1.5rem}.docs{width:100%}@media (min-width:640px){.docs{max-width:640px}}@media (min-width:768px){.docs{max-width:768px}}@media (min-width:1024px){.docs{max-width:1024px}}@media (min-width:1280px){.docs{max-width:1280px}}@media (min-width:1536px){.docs{max-width:1536px}}.docs{margin-left:auto;margin-right:auto;margin-left:0;margin-bottom:6rem;padding-left:1rem;padding-right:1rem;padding-top:0}@media (min-width:768px){.docs{margin-left:2rem;padding-top:8rem}}.separator{--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-style:solid;border-top-width:2px;margin-top:2.5rem;margin-bottom:2.5rem}.code-block{--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-style:solid;border-width:2px;margin-top:2.5rem;margin-bottom:2.5rem;width:100%}.code-block__content{margin:1.5rem}.bullet-list{margin-top:.5rem;margin-bottom:.5rem;margin-left:1rem}.bullet-list__callout>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.bullet-list__callout{display:flex}code.doc{overflow-x:auto;background-color:transparent;--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-width:2px;display:block;margin-top:1rem;margin-bottom:1rem;padding:1rem}.admon{border-style:dashed;border-width:2px;margin-top:2.5rem;margin-bottom:2.5rem;width:100%}.admon-warning{--tw-border-opacity:1;border-color:rgba(220,38,38,var(--tw-border-opacity))}.admon__button{border-radius:9999px;display:flex;align-items:center;justify-content:center;height:2rem;margin-left:1rem;margin-top:-1rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:8rem;gap:.5rem}.admon__button-warning{--tw-bg-opacity:1;background-color:rgba(220,38,38,var(--tw-bg-opacity))}.admon__button-note{--tw-bg-opacity:1;background-color:rgba(30,58,138,var(--tw-bg-opacity))}.admon-note{--tw-border-opacity:1;border-color:rgba(30,58,138,var(--tw-border-opacity))}.secondary-nav{margin-bottom:1.5rem}@media (min-width:768px){.secondary-nav{display:none}}.secondary-nav__content{--tw-border-opacity:1;border-color:rgba(210,199,91,var(--tw-border-opacity));width:10rem}.secondary-nav__split{--tw-bg-opacity:1;background-color:rgba(18,18,18,var(--tw-bg-opacity));border-radius:.75rem;display:inline-flex;align-items:center;justify-content:center;font-weight:500;height:3rem;padding-left:.5rem;padding-right:.5rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:9rem}.secondary-nav__split>p{margin-right:6px}@media (min-width:768px){.md\:flex-row{flex-direction:row}.md\:ml-8{margin-left:2rem}.md\:pt-32{padding-top:8rem}.md\:w-2\/5{width:40%}.md\:gap-0{gap:0}} \ No newline at end of file diff --git a/website/static/tailwind.min.css b/website/assets/tailwind.min.css similarity index 100% rename from website/static/tailwind.min.css rename to website/assets/tailwind.min.css diff --git a/website/assets/try_shpadoinkle_footer_logo.svg b/website/assets/try_shpadoinkle_footer_logo.svg new file mode 100644 index 00000000..ec8a8e7c --- /dev/null +++ b/website/assets/try_shpadoinkle_footer_logo.svg @@ -0,0 +1,104 @@ + + + + Group 4 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ® + + + + + + \ No newline at end of file diff --git a/website/assets/try_shpadoinkle_footer_transition.svg b/website/assets/try_shpadoinkle_footer_transition.svg new file mode 100644 index 00000000..da1aecd4 --- /dev/null +++ b/website/assets/try_shpadoinkle_footer_transition.svg @@ -0,0 +1,14 @@ + + + + Group 18 + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/website/assets/try_shpadoinkle_top_transition.svg b/website/assets/try_shpadoinkle_top_transition.svg new file mode 100644 index 00000000..50bf036b --- /dev/null +++ b/website/assets/try_shpadoinkle_top_transition.svg @@ -0,0 +1,17 @@ + + + + Group 15 + Created with Sketch. + + + + + + + + + + + + \ No newline at end of file diff --git a/website/static/style.css b/website/static/style.css deleted file mode 100644 index 11166984..00000000 --- a/website/static/style.css +++ /dev/null @@ -1,14 +0,0 @@ -html, -body { - background: black; - min-width: 100%; - min-height: 100%; -} - -.error{ - white-space: pre; - font-family: monospace; - border: 1px solid red; - margin: 5px; - padding: 5px; -} -- GitLab From 73405e683d640ee4bf4da8259fbe255da6e391dd Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Sat, 10 Apr 2021 17:09:46 -0600 Subject: [PATCH 025/116] compile time checked assets --- examples/servant-crud/Types.hs | 8 +- html/Shpadoinkle/Html/TH/AssetLink.hs | 18 +++ html/package.yaml | 3 + .../Shpadoinkle/Website/Partials/Footer.hs | 67 ++++++++++ website/Shpadoinkle/Website/Partials/Nav.hs | 65 ++++++++++ .../Shpadoinkle/Website/Partials/Template.hs | 120 ++---------------- website/Shpadoinkle/Website/Style.hs | 2 +- website/Shpadoinkle/Website/Tailwind.hs | 11 -- website/Shpadoinkle/Website/Types.hs | 2 + website/Shpadoinkle/Website/View.hs | 4 +- website/assets/nav_line.svg | 4 + website/package.yaml | 4 +- 12 files changed, 183 insertions(+), 125 deletions(-) create mode 100644 html/Shpadoinkle/Html/TH/AssetLink.hs create mode 100644 website/Shpadoinkle/Website/Partials/Footer.hs create mode 100644 website/Shpadoinkle/Website/Partials/Nav.hs delete mode 100644 website/Shpadoinkle/Website/Tailwind.hs create mode 100644 website/assets/nav_line.svg diff --git a/examples/servant-crud/Types.hs b/examples/servant-crud/Types.hs index 53472075..cb54ad52 100644 --- a/examples/servant-crud/Types.hs +++ b/examples/servant-crud/Types.hs @@ -193,7 +193,7 @@ emptyEditForm = SpaceCraftUpdate } -data Frontend +data ViewModel = MEcho (Maybe Text) | MList Roster | MDetail (Maybe SpaceCraftId) (SpaceCraftUpdate 'Edit) @@ -201,6 +201,12 @@ data Frontend deriving (Eq, Ord, Show, Generic) +data Frontend = Frontend + { route :: Route + , model :: ViewModel + } + + instance (ToJSON (Column [SpaceCraft])) => ToJSON Frontend instance (FromJSON (Column [SpaceCraft])) => FromJSON Frontend instance diff --git a/html/Shpadoinkle/Html/TH/AssetLink.hs b/html/Shpadoinkle/Html/TH/AssetLink.hs new file mode 100644 index 00000000..31d22c32 --- /dev/null +++ b/html/Shpadoinkle/Html/TH/AssetLink.hs @@ -0,0 +1,18 @@ +module Shpadoinkle.Html.TH.AssetLink where + + +import Control.Monad (unless) +import Crypto.Hash (Digest, SHA1 (..), hash) +import Data.ByteString as B +import Language.Haskell.TH.Syntax (Exp (LitE), Lit (StringL), Q, + runIO) +import System.Directory (doesFileExist) + + +assetLink :: FilePath -> Q Exp +assetLink fp = runIO $ do + exists <- doesFileExist fp + unless exists . fail $ "No asset found at " <> fp + c <- B.readFile fp + let h = hash c :: Digest SHA1 + return . LitE . StringL $ fp <> "?_=" <> show h diff --git a/html/package.yaml b/html/package.yaml index 42f5004b..c7560ede 100644 --- a/html/package.yaml +++ b/html/package.yaml @@ -34,6 +34,8 @@ dependencies: - bytestring - containers >= 0.6.0 && < 0.7 - compactable + - cryptonite + - directory - text >= 1.2.3 && < 1.3 - time - template-haskell >= 2.14.0 && < 2.17 @@ -69,6 +71,7 @@ library: - Shpadoinkle.Html.LocalStorage - Shpadoinkle.Html.TH - Shpadoinkle.Html.TH.CSS + - Shpadoinkle.Html.TH.AssetLink - Shpadoinkle.WebWorker - Shpadoinkle.Keyboard source-dirs: . diff --git a/website/Shpadoinkle/Website/Partials/Footer.hs b/website/Shpadoinkle/Website/Partials/Footer.hs new file mode 100644 index 00000000..8fa3fb7d --- /dev/null +++ b/website/Shpadoinkle/Website/Partials/Footer.hs @@ -0,0 +1,67 @@ +{-# LANGUAGE OverloadedStrings #-} + + +module Shpadoinkle.Website.Partials.Footer (view) where + + +import Prelude hiding (div) +import Shpadoinkle.Html + + +view :: Html m a +view = + footer_ + [ div "footer__wrapper" + [ a [ href "/" ] + [ img' [ alt "shpadoinkle logo", src "/assets/try_shpadoinkle_footer_logo.svg" ] + ] + , div "flex flex-col justify-between w-full gap-8 pt-12 leading-8 md:flex-row md:w-2/5 md:gap-0" + [ div_ + [ p "footer__nav__category" + [ "Quick Links" ] + , nav_ + [ ul "footer__nav__links" + [ li_ + [ a [ href "/" ] + [ "Sandbox" ] + ] + , li_ + [ a [ href "/" ] + [ "Get Started" ] + ] + , li_ + [ a [ href "/" ] + [ "Tutorial" ] + ] + , li_ + [ a [ href "/" ] + [ "Sandbox" ] + ] + ] + ] + ] + , div_ + [ p "footer__nav__category" + [ "Community" ] + , nav_ + [ ul "footer__nav__links" + [ li_ + [ a [ href "/" ] + [ "Reddit" ] + ] + , li_ + [ a [ href "/" ] + [ "Twitter" ] + ] + , li_ + [ a [ href "/" ] + [ "Hackage" ] + ] + ] + ] + ] + ] + , div "footer_copyright" + [ "© 2020-2021 Platonic Systems" ] + ] + ] diff --git a/website/Shpadoinkle/Website/Partials/Nav.hs b/website/Shpadoinkle/Website/Partials/Nav.hs new file mode 100644 index 00000000..9c8b744d --- /dev/null +++ b/website/Shpadoinkle/Website/Partials/Nav.hs @@ -0,0 +1,65 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} + + +module Shpadoinkle.Website.Partials.Nav (view) where + + +import Prelude hiding (div) +import Shpadoinkle.Html +import Shpadoinkle.Html.TH.AssetLink +import qualified Shpadoinkle.Website.Style as Style +import Shpadoinkle.Website.Types +import Shpadoinkle.Widgets.Types + + +data Nav + = NHome + | NGettingStarted + | NTutorial + | NSandbox + deriving (Eq, Ord, Enum, Bounded) + + +instance Humanize Nav where + humanize = \case + NHome -> "Home" + NGettingStarted -> "Get Started" + NTutorial -> "Tutorial" + NSandbox -> "Sandbox" + + +toActive :: Frontend -> Maybe Nav +toActive = \case + MHome _ -> Just NHome + MGettingStarted _ -> Just NGettingStarted + MTutorial _ -> Just NTutorial + MSandbox -> Just NSandbox + _ -> Nothing + + +navLinkDesktop :: Frontend -> Nav -> Html m a +navLinkDesktop fe n = li [ class' Style.nav__link ] $ + a [ href "getstarted.html" ] [ text $ humanize n ] + : [ img' [ src $(assetLink "./assets/nav_line.svg") ] | toActive fe == Just n ] + + +navLinkMobile :: Nav -> Html m a +navLinkMobile n = a [ class' Style.nav_mobile__link, href "#" ] [ text $ humanize n ] + + +view :: Frontend -> [Html m a] +view r = + [ nav [ class' Style.nav ] + [ a [ href "/" ] + [ img' [ class' Style.nav__logo, src $(assetLink "./assets/landing_logo.svg") ] + ] + , img' [ class' Style.nav__menu_icon, src $(assetLink "./assets/menu_icon.svg") ] + , img' [ class' Style.nav__menu_icon, src $(assetLink "./assets/menu_close_icon.svg") ] + , ul [ class' Style.nav__links ] $ navLinkDesktop r <$> [minBound..maxBound] + ] + , nav [ class' Style.nav_mobile ] + [ div [ class' Style.nav_mobile__wrapper ] $ navLinkMobile <$> [minBound..maxBound] + ] + ] diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index bd6da57f..18122161 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -1,15 +1,20 @@ {-# LANGUAGE OverloadedStrings #-} -module Shpadoinkle.Website.Partials.Template where +module Shpadoinkle.Website.Partials.Template + ( wrapper + , template + ) where import Data.Text -import Prelude hiding (div) -import Shpadoinkle (h) +import Prelude hiding (div) +import Shpadoinkle (h) import Shpadoinkle.Html import Shpadoinkle.Router -import Shpadoinkle.Run (Env (..), entrypoint) +import Shpadoinkle.Run (Env (..), entrypoint) +import qualified Shpadoinkle.Website.Partials.Footer as Footer +import qualified Shpadoinkle.Website.Partials.Nav as Nav import Shpadoinkle.Website.Types @@ -38,107 +43,6 @@ headView ev fe = head_ ] -navView :: [Html m a] -navView = - [ nav "nav" - [ a [ href "/" ] - [ img' [ alt "shpadoinkle logo", className "nav__logo", src "/assets/landing_logo.svg" ] - ] - , img' [ alt "menu toggle icon", className "nav__menu-icon", src "/assets/menu_icon.svg" ] - , img' [ alt "menu toggle icon", className "nav__menu-icon", src "/assets/menu_close_icon.svg" ] - , ul "nav__links" - [ li "nav__link" - [ a [ href "/" ] [ "Home" ] - , img' [ className "nav__link-underline", src "/assets/underline.svg" ] - ] - , li "nav__link" - [ a [ href "getstarted.html" ] - [ "Get Started" ] - ] - , li "nav__link" - [ a [ href "/" ] - [ "Tutorial" ] - ] - , li "nav__link" - [ a [ href "/" ] - [ "Sandbox" ] - ] - ] - ] - , nav "nav-mobile" - [ div "nav-mobile__wrapper" - [ a [ className "nav-mobile__link", href "#" ] - [ "Home" ] - , a [ className "nav-mobile__link", href "getstarted.html" ] - [ "Get Started" ] - , a [ className "nav-mobile__link", href "#" ] - [ "Tutorial" ] - , a [ className "nav-mobile__link", href "#" ] - [ "Sandbox" ] - ] - ] - ] - - -footerView :: Html m a -footerView = - footer_ - [ div "footer__wrapper" - [ a [ href "/" ] - [ img' [ alt "shpadoinkle logo", src "/assets/try_shpadoinkle_footer_logo.svg" ] - ] - , div "flex flex-col justify-between w-full gap-8 pt-12 leading-8 md:flex-row md:w-2/5 md:gap-0" - [ div_ - [ p "footer__nav__category" - [ "Quick Links" ] - , nav_ - [ ul "footer__nav__links" - [ li_ - [ a [ href "/" ] - [ "Sandbox" ] - ] - , li_ - [ a [ href "/" ] - [ "Get Started" ] - ] - , li_ - [ a [ href "/" ] - [ "Tutorial" ] - ] - , li_ - [ a [ href "/" ] - [ "Sandbox" ] - ] - ] - ] - ] - , div_ - [ p "footer__nav__category" - [ "Community" ] - , nav_ - [ ul "footer__nav__links" - [ li_ - [ a [ href "/" ] - [ "Reddit" ] - ] - , li_ - [ a [ href "/" ] - [ "Twitter" ] - ] - , li_ - [ a [ href "/" ] - [ "Hackage" ] - ] - ] - ] - ] - ] - , div "footer_copyright" - [ "© 2020-2021 Platonic Systems" ] - ] - ] - - template :: Env -> Frontend -> Html m a -> Html m a template ev fe content' = html [ lang "en" ] [ headView ev fe @@ -146,8 +50,8 @@ template ev fe content' = html [ lang "en" ] ] -wrapper :: [Html m a] -> [Html m a] -wrapper content' = navView <> +wrapper :: Frontend -> [Html m a] -> [Html m a] +wrapper fe content' = Nav.view fe <> [ h "main" [] content' - , footerView + , Footer.view ] diff --git a/website/Shpadoinkle/Website/Style.hs b/website/Shpadoinkle/Website/Style.hs index c50131aa..1000cc63 100644 --- a/website/Shpadoinkle/Website/Style.hs +++ b/website/Shpadoinkle/Website/Style.hs @@ -8,4 +8,4 @@ module Shpadoinkle.Website.Style where import Shpadoinkle.Html.TH.CSS -$(extractNamespace "./static/style.css") +$(extractNamespace "./assets/style.css") diff --git a/website/Shpadoinkle/Website/Tailwind.hs b/website/Shpadoinkle/Website/Tailwind.hs deleted file mode 100644 index 7e014fd4..00000000 --- a/website/Shpadoinkle/Website/Tailwind.hs +++ /dev/null @@ -1,11 +0,0 @@ -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TemplateHaskell #-} - - -module Shpadoinkle.Website.Tailwind where - - -import Shpadoinkle.Html.TH.CSS - - -$(extractNamespace "./static/tailwind.min.css") diff --git a/website/Shpadoinkle/Website/Types.hs b/website/Shpadoinkle/Website/Types.hs index 3570536b..f18d2054 100644 --- a/website/Shpadoinkle/Website/Types.hs +++ b/website/Shpadoinkle/Website/Types.hs @@ -195,6 +195,7 @@ data Route | RGettingStarted RGettingStarted | RPackages RPackages | RTutorial RTutorial + | RSandbox | RFourOhFour deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) @@ -226,6 +227,7 @@ data Frontend | MGettingStarted RGettingStarted | MPackages RPackages | MTutorial RTutorial + | MSandbox | MFourOhFour deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index 5e1805be..54043f72 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -47,7 +47,6 @@ import Shpadoinkle.Website.Component.LiveExample import qualified Shpadoinkle.Website.Page.Documentation as Documentation import qualified Shpadoinkle.Website.Page.Home as Home import Shpadoinkle.Website.Partials.Template -import qualified Shpadoinkle.Website.Tailwind as T import Shpadoinkle.Website.Types import Shpadoinkle.Website.Types.Hoogle import Shpadoinkle.Widgets.Form.Dropdown @@ -69,12 +68,13 @@ fourOhFour = h2_ [ "404" ] view :: (Hooglable m, ExampleEffects m, MonadJSM m) => Frontend -> Html m Frontend -view = div_ . wrapper . \case +view fe = div_ . wrapper fe $ case fe of MHome x -> onSum #_MHome <$> Home.view x MConcepts -> [ Documentation.concepts ] MGettingStarted gsr -> [ Documentation.gettingStarted gsr ] MPackages pr -> [ Documentation.packages pr ] MTutorial tutr -> [ Documentation.tutorial tutr ] + MSandbox -> [] MFourOhFour -> [ fourOhFour ] diff --git a/website/assets/nav_line.svg b/website/assets/nav_line.svg new file mode 100644 index 00000000..a6117261 --- /dev/null +++ b/website/assets/nav_line.svg @@ -0,0 +1,4 @@ + + + + diff --git a/website/package.yaml b/website/package.yaml index b81c6344..8f938d06 100644 --- a/website/package.yaml +++ b/website/package.yaml @@ -73,9 +73,10 @@ executables: other-modules: - Shpadoinkle.Website.Component.LiveExample - Shpadoinkle.Website.Partials.Template + - Shpadoinkle.Website.Partials.Nav + - Shpadoinkle.Website.Partials.Footer - Shpadoinkle.Website.Page.Home - Shpadoinkle.Website.Page.Documentation - - Shpadoinkle.Website.Tailwind - Shpadoinkle.Website.Types - Shpadoinkle.Website.Types.Hoogle - Shpadoinkle.Website.Style @@ -104,7 +105,6 @@ executables: buildable: True main: Shpadoinkle/Website/Disembodied.hs other-modules: - - Shpadoinkle.Website.Tailwind - Shpadoinkle.Website.Types - Shpadoinkle.Website.Types.Hoogle - Shpadoinkle.Website.Style -- GitLab From c60c1ec2d0dc6f531addf3f071ff372707f2a3c7 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Sat, 10 Apr 2021 17:19:34 -0600 Subject: [PATCH 026/116] clean up --- website/Shpadoinkle/Website/Page/Home.hs | 19 +++++++------ website/Shpadoinkle/Website/View.hs | 36 ++++-------------------- 2 files changed, 16 insertions(+), 39 deletions(-) diff --git a/website/Shpadoinkle/Website/Page/Home.hs b/website/Shpadoinkle/Website/Page/Home.hs index fa0bff69..634afb61 100644 --- a/website/Shpadoinkle/Website/Page/Home.hs +++ b/website/Shpadoinkle/Website/Page/Home.hs @@ -4,8 +4,10 @@ module Shpadoinkle.Website.Page.Home where +import Data.Text (Text) import Prelude hiding (div, span) import Shpadoinkle.Html +import qualified Shpadoinkle.Website.Style as Style import Shpadoinkle.Website.Types @@ -13,16 +15,17 @@ view :: Home -> [Html m Home] view _ = [hero, featureSection, componentsSection] +heroTitle :: Text -> Html m a +heroTitle = p [ class' Style.hero_title ] . pure . text + + hero :: Html m a hero = div_ - [ div "hero" - [ div "hero-title-wrapper" - [ p "hero-title" - [ "A new Haskell UI" ] - , p "hero-title" - [ "programming" ] - , p "hero-title" - [ "paradigm" ] + [ div [ class' Style.hero ] + [ div [ class' Style.hero_title_wrapper ] $ heroTitle <$> + [ "A new Haskell UI" + , "programming" + , "paradigm" ] , div "hero-button" [ div "split-button hero-button__content" diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index 54043f72..5c7c06a5 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -11,49 +11,22 @@ module Shpadoinkle.Website.View where -import Control.Lens ((%~), (&), (.~), - (<>~), (^.)) +import Control.Lens ((%~), (&), (^.)) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Reader import Data.Generics.Labels () -import Data.List (intersperse) import Data.String -import Data.Text as T (Text, drop, - length, null, - pack, replace, - replicate, - splitOn, strip, - unpack) -import Data.Text.Lazy (fromStrict, - toStrict) -import Data.Text.Lazy.Encoding (decodeUtf8, - encodeUtf8) -import GHCJS.DOM -import GHCJS.DOM.Document -import GHCJS.DOM.WindowOrWorkerGlobalScope (setTimeout) -import Language.Javascript.JSaddle hiding (JSM, - MonadJSM) +import Data.Text (Text) import Prelude hiding (div) -import Servant.API (toUrlPiece) import Shpadoinkle import Shpadoinkle.Html as H import Shpadoinkle.Isreal.Types as Swan -import Shpadoinkle.Lens (onRecord, - onRecordEndo, onSum) -import Shpadoinkle.Router (toHydration) -import Shpadoinkle.Run (Env, entrypoint) -import Shpadoinkle.Template.TH +import Shpadoinkle.Lens (onSum) import Shpadoinkle.Website.Component.LiveExample import qualified Shpadoinkle.Website.Page.Documentation as Documentation import qualified Shpadoinkle.Website.Page.Home as Home import Shpadoinkle.Website.Partials.Template import Shpadoinkle.Website.Types -import Shpadoinkle.Website.Types.Hoogle -import Shpadoinkle.Widgets.Form.Dropdown -import qualified Shpadoinkle.Widgets.Form.Input as I -import Shpadoinkle.Widgets.Types (Pick (One), - Search (..), - withOptions) default (Text) @@ -67,7 +40,7 @@ fourOhFour :: Html m a fourOhFour = h2_ [ "404" ] -view :: (Hooglable m, ExampleEffects m, MonadJSM m) => Frontend -> Html m Frontend +view :: (ExampleEffects m) => Frontend -> Html m Frontend view fe = div_ . wrapper fe $ case fe of MHome x -> onSum #_MHome <$> Home.view x MConcepts -> [ Documentation.concepts ] @@ -99,6 +72,7 @@ start = \case RPackages x -> pure $ MPackages x RTutorial x -> pure $ MTutorial x RFourOhFour -> pure MFourOhFour + RSandbox -> pure MSandbox startJS :: MonadJSM m => ExampleEffects m => Route -> m Frontend -- GitLab From 4094f6bd384ea90b2279d9d8cd10ed788218310e Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Sun, 11 Apr 2021 11:33:28 -0600 Subject: [PATCH 027/116] move to one type per module --- html/Shpadoinkle/Html/TH/AssetLink.hs | 11 +- router/Shpadoinkle/Router.hs | 2 +- website/Shpadoinkle/Website/Page/Home.hs | 176 +++++++++--------- .../Shpadoinkle/Website/Partials/Footer.hs | 47 +++-- website/Shpadoinkle/Website/Partials/Nav.hs | 49 ++--- .../Shpadoinkle/Website/Partials/Template.hs | 4 +- website/Shpadoinkle/Website/Routes.hs | 111 +++++++++++ website/Shpadoinkle/Website/Types.hs | 160 +--------------- website/Shpadoinkle/Website/Types/Example.hs | 24 +++ .../Shpadoinkle/Website/Types/ExampleState.hs | 17 ++ website/Shpadoinkle/Website/Types/Home.hs | 41 ++++ website/Shpadoinkle/Website/Types/Nav.hs | 25 +++ website/assets/nav_line.svg | 2 +- website/package.yaml | 5 + widgets/Shpadoinkle/Widgets/Types/Core.hs | 1 + 15 files changed, 364 insertions(+), 311 deletions(-) create mode 100644 website/Shpadoinkle/Website/Routes.hs create mode 100644 website/Shpadoinkle/Website/Types/Example.hs create mode 100644 website/Shpadoinkle/Website/Types/ExampleState.hs create mode 100644 website/Shpadoinkle/Website/Types/Home.hs create mode 100644 website/Shpadoinkle/Website/Types/Nav.hs diff --git a/html/Shpadoinkle/Html/TH/AssetLink.hs b/html/Shpadoinkle/Html/TH/AssetLink.hs index 31d22c32..26f3b095 100644 --- a/html/Shpadoinkle/Html/TH/AssetLink.hs +++ b/html/Shpadoinkle/Html/TH/AssetLink.hs @@ -10,9 +10,14 @@ import System.Directory (doesFileExist) assetLink :: FilePath -> Q Exp -assetLink fp = runIO $ do - exists <- doesFileExist fp +assetLink = assetLinkWithBase "" + + +assetLinkWithBase :: FilePath -> FilePath -> Q Exp +assetLinkWithBase base fp' = runIO $ do + let fp = base <> fp' + exists <- doesFileExist $ "." <> fp unless exists . fail $ "No asset found at " <> fp - c <- B.readFile fp + c <- B.readFile $ "." <> fp let h = hash c :: Digest SHA1 return . LitE . StringL $ fp <> "?_=" <> show h diff --git a/router/Shpadoinkle/Router.hs b/router/Shpadoinkle/Router.hs index d34817d0..46eaf109 100644 --- a/router/Shpadoinkle/Router.hs +++ b/router/Shpadoinkle/Router.hs @@ -167,7 +167,7 @@ toHydration fe = -- | Change the browser's URL to the canonical URL for a given route `r`. -navigate :: forall a m r. MonadJSM m => Routed a r => r -> m () +navigate :: forall a m r. (MonadJSM m, Routed a r) => r -> m () navigate r = do w <- currentWindowUnchecked history <- getHistory w diff --git a/website/Shpadoinkle/Website/Page/Home.hs b/website/Shpadoinkle/Website/Page/Home.hs index 634afb61..6cd5c479 100644 --- a/website/Shpadoinkle/Website/Page/Home.hs +++ b/website/Shpadoinkle/Website/Page/Home.hs @@ -1,139 +1,131 @@ {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} module Shpadoinkle.Website.Page.Home where -import Data.Text (Text) -import Prelude hiding (div, span) -import Shpadoinkle.Html -import qualified Shpadoinkle.Website.Style as Style +import Data.Text (Text) +import Prelude hiding (div, span) +import Shpadoinkle.Html as H +import Shpadoinkle.Html.TH.AssetLink +import Shpadoinkle.Website.Style as Style import Shpadoinkle.Website.Types view :: Home -> [Html m Home] -view _ = [hero, featureSection, componentsSection] +view _ = [hero', featureSection, componentsSection] heroTitle :: Text -> Html m a -heroTitle = p [ class' Style.hero_title ] . pure . text +heroTitle = p [ class' hero_title ] . pure . text -hero :: Html m a -hero = div_ - [ div [ class' Style.hero ] - [ div [ class' Style.hero_title_wrapper ] $ heroTitle <$> +hero' :: Html m a +hero' = div_ + [ div [ class' hero ] + [ div [ class' hero_title_wrapper ] $ heroTitle <$> [ "A new Haskell UI" , "programming" , "paradigm" ] - , div "hero-button" - [ div "split-button hero-button__content" - [ button [ className "split-button__button", type' "button" ] - [ span "split-button__split1 hero-button__split" + , div [ class' hero_button ] + [ div [ class' $ split_button <> hero_button__content ] + [ button [ class' split_button__button, type' "button" ] + [ span [ class' $ split_button__split1 <> hero_button__split ] [ "Get Started" ] - , span "split-button__split2 hero-button__split" - [ img' [ alt "Right arrow", src "assets/right-arrow-white.svg" ] + , span [ class' $ split_button__split2 <> hero_button__split ] + [ img' [ alt "Right arrow", src $(assetLink "/assets/right-arrow-white.svg") ] ] ] ] ] ] - , img' [ alt "desert cactus", className "hero-bg", src "/assets/hero_image.svg" ] - , img' [ alt "desert cactus", className "hero-bg-mobile", src "/assets/mobile/hero_image.svg" ] + , img' [ alt "desert cactus", class' hero_bg, src $(assetLink "/assets/hero_image.svg") ] + , img' [ alt "desert cactus", class' hero_bg_mobile, src $(assetLink "/assets/mobile/hero_image.svg") ] + ] + + +data FeatureCard = FeatureCard + { card'icon :: Text + , card'title :: Text + , card'description :: Text + } + + +featureCard :: FeatureCard -> Html m a +featureCard fc = div [ class' feature__card ] + [ div [ class' feature__card__content ] + [ div [ class' feature__card__heading ] + [ img' [ src $ card'icon fc ] + , p [ class' feature__card__title ] + [ text $ card'title fc ] + ] + , p [ class' feature__card__description ] + [ text $ card'description fc ] + ] ] featureSection :: Html m a -featureSection = div "feature__section" - [ div "feature__wrapper" - [ p "feature__title" +featureSection = div [ class' feature__section ] + [ div [ class' feature__wrapper ] + [ p [ class' feature__title ] [ "A programming model for declarative, high performance user interface." ] - , div "feature__cards" - [ div "feature__card" - [ div "feature__card__content" - [ div "feature__card__heading" - [ img [ alt "rabbit", src "assets/fast_icon.svg" ] - [] - , p "feature__card__title" - [ "Fast" ] - ] - , p "feature__card__description" - [ "Shpadoinkle is fast because it does little work. The renderer is modular, so you can always benefit from the latest advances in virtual DOM rendering. " ] - ] - ] - , div "feature__card" - [ div "feature__card__content" - [ div "feature__card__heading" - [ img' [ alt "playing cards", src "assets/declarative_icon.svg" ] - , p "feature__card__title" - [ "Declarative" ] - ] - , p "feature__card__description" - [ "Your Shpadoinkle code is high-level. You need not worry about low-level details, causality, or when DOM nodes get replaced. " ] - ] - ] - , div "feature__card" - [ div "feature__card__content" - [ div "feature__card__heading" - [ img' [ alt "puzzle pieces", src "assets/composable_icon.svg" ] - , p "feature__card__title" - [ "Composable" ] - ] - , p "feature__card__description" - [ "With Shpadoinkle, there is no need to associate model update code with DOM locations. This avoids elaborate passing of messages and payloads. Components are highly composable. " ] - ] - ] - , div "feature__card" - [ div "feature__card__content" - [ div "feature__card__heading" - [ img' [ alt "revolver gun", src "assets/reliable_icon.svg" ] - , p "feature__card__title" - [ "Reliable" ] - ] - , p "feature__card__description" - [ "Shpadoinkle UIs are composed of components with no side-effects. So, runtime errors are exceedingly rare. Code is easy to test because model updates are pure functions. " ] - ] - ] - , div "feature__card" - [ div "feature__card__content" - [ div "feature__card__heading" - [ img' [ alt "whip", src "assets/lorem_ipsum_logo.svg" ] - , p "feature__card__title" - [ "Lorem Ipsum" ] - ] - , p "feature__card__description" - [ "Shpadoinkle UIs are composed of components with no side-effects. So, runtime errors are exceedingly rare. Code is easy to test because model updates are pure functions. " ] - ] - ] + , div [ class' feature__cards ] + [ featureCard $ FeatureCard + { card'icon = $(assetLink "/assets/fast_icon.svg") + , card'title = "Fast" + , card'description = "Shpadoinkle is fast because it does little work. The renderer is modular, so you can always benefit from the latest advances in virtual DOM rendering." + } + , featureCard $ FeatureCard + { card'icon = $(assetLink "/assets/declarative_icon.svg") + , card'title = "Declarative" + , card'description = "Your Shpadoinkle code is high-level. You need not worry about low-level details, causality, or when DOM nodes get replaced." + } + , featureCard $ FeatureCard + { card'icon = $(assetLink "/assets/composable_icon.svg") + , card'title = "Composable" + , card'description = "With Shpadoinkle, there is no need to associate model update code with DOM locations. This avoids elaborate passing of messages and payloads. Components are highly composable." + } + , featureCard $ FeatureCard + { card'icon = $(assetLink "/assets/reliable_icon.svg") + , card'title = "Reliable" + , card'description = "Shpadoinkle UIs are composed of components with no side-effects. So, runtime errors are exceedingly rare. Code is easy to test because model updates are pure functions." + } + , featureCard $ FeatureCard + { card'icon = $(assetLink "/assets/lorem_ipsum_logo.svg") + , card'title = "Lorem Ipsum" + , card'description = "Shpadoinkle UIs are composed of components with no side-effects. So, runtime errors are exceedingly rare. Code is easy to test because model updates are pure functions." + } ] ] - , div "feature-button" - [ div "split-button feature-button__content" - [ button [ className "split-button__button", type' "button" ] - [ span "split-button__split1 feature-button__split" + , div [ class' feature_button ] + [ div [ class' $ split_button <> feature_button__content ] + [ button [ class' split_button__button, type' "button" ] + [ span [ class' $ split_button__split1 <> feature_button__split ] [ "Checkout the getting started guide" ] - , span "split-button__split2 feature-button__split" - [ img' [ alt "Right arrow", src "assets/right-arrow.svg" ] + , span [ class' $ split_button__split2 <> feature_button__split ] + [ img' [ alt "Right arrow", src $(assetLink "/assets/right-arrow.svg") ] ] ] ] ] - , img' [ alt "background transition", className "transition", src "assets/orange_purple_transition.svg" ] + , img' [ alt "background transition", class' transition, src $(assetLink "/assets/orange_purple_transition.svg") ] ] componentsSection :: Html m a -componentsSection = div "components__section" - [ div "component" - [ div "component__info" - [ h2 "component__info__title" +componentsSection = div [ class' components__section ] + [ div [ class' component ] + [ div [ class' component__info ] + [ h2 [ class' component__info__title ] [ "A Component" ] - , p "component__info__description" + , p_ [ "A Shpadoinkle component ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. " ] ] - , div "component__example" - [ div' "component__example__code" + , div [ class' component__example ] + [ div' [ class' component__example__code ] ] ] , div "component" diff --git a/website/Shpadoinkle/Website/Partials/Footer.hs b/website/Shpadoinkle/Website/Partials/Footer.hs index 8fa3fb7d..45c21f14 100644 --- a/website/Shpadoinkle/Website/Partials/Footer.hs +++ b/website/Shpadoinkle/Website/Partials/Footer.hs @@ -1,50 +1,45 @@ {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} module Shpadoinkle.Website.Partials.Footer (view) where -import Prelude hiding (div) +import Prelude hiding (div) import Shpadoinkle.Html +import Shpadoinkle.Html.TH.AssetLink +import Shpadoinkle.Website.Style as Style +import Shpadoinkle.Website.Types +import Shpadoinkle.Widgets.Types + + +footerLink :: Nav -> Html m a +footerLink n = li_ + [ a [ href "/" ] + [ text $ humanize n ] + ] view :: Html m a view = footer_ - [ div "footer__wrapper" + [ div [ class' footer__wrapper ] [ a [ href "/" ] - [ img' [ alt "shpadoinkle logo", src "/assets/try_shpadoinkle_footer_logo.svg" ] + [ img' [ alt "shpadoinkle logo", src $(assetLink "/assets/try_shpadoinkle_footer_logo.svg") ] ] - , div "flex flex-col justify-between w-full gap-8 pt-12 leading-8 md:flex-row md:w-2/5 md:gap-0" + , div [ class' $ flex <> flex_col <> justify_between <> w_full <> gap_8 <> pt_12 <> leading_8 <> "md:flex-row" <> "md:w-2/5" <> "md:gap-0" ] [ div_ - [ p "footer__nav__category" + [ p [ class' footer__nav__category ] [ "Quick Links" ] , nav_ - [ ul "footer__nav__links" - [ li_ - [ a [ href "/" ] - [ "Sandbox" ] - ] - , li_ - [ a [ href "/" ] - [ "Get Started" ] - ] - , li_ - [ a [ href "/" ] - [ "Tutorial" ] - ] - , li_ - [ a [ href "/" ] - [ "Sandbox" ] - ] - ] + [ ul [ class' footer__nav__links ] $ footerLink <$> [minBound..maxBound] ] ] , div_ - [ p "footer__nav__category" + [ p [ class' footer__nav__category ] [ "Community" ] , nav_ - [ ul "footer__nav__links" + [ ul [ class' footer__nav__links ] [ li_ [ a [ href "/" ] [ "Reddit" ] @@ -61,7 +56,7 @@ view = ] ] ] - , div "footer_copyright" + , div [ class' footer_copyright ] [ "© 2020-2021 Platonic Systems" ] ] ] diff --git a/website/Shpadoinkle/Website/Partials/Nav.hs b/website/Shpadoinkle/Website/Partials/Nav.hs index 9c8b744d..1dc67281 100644 --- a/website/Shpadoinkle/Website/Partials/Nav.hs +++ b/website/Shpadoinkle/Website/Partials/Nav.hs @@ -7,59 +7,34 @@ module Shpadoinkle.Website.Partials.Nav (view) where import Prelude hiding (div) -import Shpadoinkle.Html +import Shpadoinkle.Html as H import Shpadoinkle.Html.TH.AssetLink -import qualified Shpadoinkle.Website.Style as Style +import Shpadoinkle.Website.Style as Style import Shpadoinkle.Website.Types import Shpadoinkle.Widgets.Types -data Nav - = NHome - | NGettingStarted - | NTutorial - | NSandbox - deriving (Eq, Ord, Enum, Bounded) - - -instance Humanize Nav where - humanize = \case - NHome -> "Home" - NGettingStarted -> "Get Started" - NTutorial -> "Tutorial" - NSandbox -> "Sandbox" - - -toActive :: Frontend -> Maybe Nav -toActive = \case - MHome _ -> Just NHome - MGettingStarted _ -> Just NGettingStarted - MTutorial _ -> Just NTutorial - MSandbox -> Just NSandbox - _ -> Nothing - - navLinkDesktop :: Frontend -> Nav -> Html m a -navLinkDesktop fe n = li [ class' Style.nav__link ] $ +navLinkDesktop fe n = li [ class' nav__link ] $ a [ href "getstarted.html" ] [ text $ humanize n ] - : [ img' [ src $(assetLink "./assets/nav_line.svg") ] | toActive fe == Just n ] + : [ img' [ class' nav__link_underline, src $(assetLink "/assets/nav_line.svg") ] | toActive fe == Just n ] navLinkMobile :: Nav -> Html m a -navLinkMobile n = a [ class' Style.nav_mobile__link, href "#" ] [ text $ humanize n ] +navLinkMobile n = a [ class' nav_mobile__link, href "#" ] [ text $ humanize n ] view :: Frontend -> [Html m a] view r = - [ nav [ class' Style.nav ] + [ H.nav [ class' Style.nav ] [ a [ href "/" ] - [ img' [ class' Style.nav__logo, src $(assetLink "./assets/landing_logo.svg") ] + [ img' [ class' nav__logo, src $(assetLink "/assets/landing_logo.svg") ] ] - , img' [ class' Style.nav__menu_icon, src $(assetLink "./assets/menu_icon.svg") ] - , img' [ class' Style.nav__menu_icon, src $(assetLink "./assets/menu_close_icon.svg") ] - , ul [ class' Style.nav__links ] $ navLinkDesktop r <$> [minBound..maxBound] + , img' [ class' nav__menu_icon, src $(assetLink "/assets/menu_icon.svg") ] + , img' [ class' nav__menu_icon, src $(assetLink "/assets/menu_close_icon.svg") ] + , ul [ class' nav__links ] $ navLinkDesktop r <$> [minBound..maxBound] ] - , nav [ class' Style.nav_mobile ] - [ div [ class' Style.nav_mobile__wrapper ] $ navLinkMobile <$> [minBound..maxBound] + , H.nav [ class' nav_mobile ] + [ div [ class' nav_mobile__wrapper ] $ navLinkMobile <$> [minBound..maxBound] ] ] diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index 18122161..9d536959 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} module Shpadoinkle.Website.Partials.Template @@ -11,6 +12,7 @@ import Data.Text import Prelude hiding (div) import Shpadoinkle (h) import Shpadoinkle.Html +import Shpadoinkle.Html.TH.AssetLink import Shpadoinkle.Router import Shpadoinkle.Run (Env (..), entrypoint) import qualified Shpadoinkle.Website.Partials.Footer as Footer @@ -33,7 +35,7 @@ headView ev fe = head_ ] , link' [ rel "stylesheet" - , href "/assets/style.css" + , href $(assetLink "/assets/style.css") ] , toHydration fe , h "title" [] [ "Shpadoinkle" ] diff --git a/website/Shpadoinkle/Website/Routes.hs b/website/Shpadoinkle/Website/Routes.hs new file mode 100644 index 00000000..191bf79b --- /dev/null +++ b/website/Shpadoinkle/Website/Routes.hs @@ -0,0 +1,111 @@ +module Shpadoinkle.Website.Routes where + + +data RGettingStarted + = RGSIndex + | RGSAddingToYourProject + | RGSExtendAnExample + deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) + + +type SPAGettingStarted m + = View m Frontend + :<|> "adding-to-your-project" :> View m Frontend + :<|> "extend-an-example" :> View m Frontend + + +gettingStartedRoutes :: SPAGettingStarted m :>> Route +gettingStartedRoutes + = RGettingStarted RGSIndex + :<|> RGettingStarted RGSAddingToYourProject + :<|> RGettingStarted RGSExtendAnExample + + +data RPackages + = RPIndex + | RPCore + | RPConsole + | RPBackends + | RPHtml + | RPLens + | RPRouter + | RPWidgets + deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) + + +type SPAPackages m + = View m Frontend + :<|> "core" :> View m Frontend + :<|> "console" :> View m Frontend + :<|> "backends" :> View m Frontend + :<|> "html" :> View m Frontend + :<|> "lens" :> View m Frontend + :<|> "router" :> View m Frontend + :<|> "widgets" :> View m Frontend + + +packagesRoutes :: SPAPackages m :>> Route +packagesRoutes + = RPackages RPIndex + :<|> RPackages RPCore + :<|> RPackages RPConsole + :<|> RPackages RPBackends + :<|> RPackages RPHtml + :<|> RPackages RPLens + :<|> RPackages RPRouter + :<|> RPackages RPWidgets + + +data RTutorial + = RTIndex + | RTCalculator + | RTImmediateExecution + | RTComposing + deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) + + +type SPATutorial m + = View m Frontend + :<|> "calculator" :> View m Frontend + :<|> "immediate-execution" :> View m Frontend + :<|> "composing" :> View m Frontend + + +tutorialRoutes :: SPATutorial m :>> Route +tutorialRoutes + = RTutorial RTIndex + :<|> RTutorial RTCalculator + :<|> RTutorial RTImmediateExecution + :<|> RTutorial RTComposing + + +data Route + = RHome + | RConcepts + | RGettingStarted RGettingStarted + | RPackages RPackages + | RTutorial RTutorial + | RSandbox + | RFourOhFour + deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) + + +type SPA m + = "home" :> View m Frontend + :<|> "concepts" :> View m Frontend + :<|> "getting-started" :> SPAGettingStarted m + :<|> "packages" :> SPAPackages m + :<|> "tutorial" :> SPATutorial m + :<|> "404" :> View m Frontend + :<|> View m Frontend + + +routes :: SPA m :>> Route +routes + = RHome + :<|> RConcepts + :<|> gettingStartedRoutes + :<|> packagesRoutes + :<|> tutorialRoutes + :<|> RFourOhFour + :<|> RHome diff --git a/website/Shpadoinkle/Website/Types.hs b/website/Shpadoinkle/Website/Types.hs index f18d2054..76d6a823 100644 --- a/website/Shpadoinkle/Website/Types.hs +++ b/website/Shpadoinkle/Website/Types.hs @@ -34,33 +34,22 @@ import Shpadoinkle.Isreal.Types (Code (..), CompileError, SnowNonce, SnowToken) import Shpadoinkle.Router (HasRouter (..), View) import Shpadoinkle.Widgets.Form.Dropdown -import Shpadoinkle.Widgets.Types (Input, Pick (One), - Search (..)) +import Shpadoinkle.Widgets.Types (Humanize (..), Input, + Pick (One), Search (..)) import Shpadoinkle.Website.Types.Hoogle (Target) +import Shpadoinkle.Website.Types.Nav -data Examples a = Examples - { helloWorld :: a - , counter :: a - } - deriving stock (Eq, Ord, Show, Read, Generic) - deriving anyclass (FromJSON, ToJSON, NFData) +toActive :: Frontend -> Maybe Nav +toActive = \case + MHome _ -> Just NHome + MGettingStarted _ -> Just NGettingStarted + MTutorial _ -> Just NTutorial + MSandbox -> Just NSandbox + _ -> Nothing -data ExampleState = EError CompileError | ELoading | EReady - deriving stock (Eq, Ord, Show, Read, Generic) - deriving anyclass (FromJSON, ToJSON, NFData) - - -data Example = Example - { inputHaskell :: Code - , snowToken :: SnowToken - , snowNonce :: SnowNonce - , state :: ExampleState - } - deriving stock (Eq, Ord, Show, Read, Generic) - deriving anyclass (FromJSON, ToJSON, NFData) helloWorldExample :: Code @@ -82,25 +71,6 @@ topOffset :: Int topOffset = subtract 1 $ length $ splitOn "\n" $ decodeUtf8 $(embedFile "./example.template.top") -data Home = Home - { hoogle :: Hoogle - , examples :: Examples Example - } - deriving stock (Eq, Ord, Show, Read, Generic) - deriving anyclass (FromJSON, ToJSON, NFData) - - -emptyExample :: Code -> SnowToken -> Example -emptyExample cc st = Example cc st 0 ELoading - - -emptyHome :: Examples SnowToken -> Home -emptyHome st = Home mempty $ Examples - { helloWorld = emptyExample helloWorldExample (st ^. #helloWorld) - , counter = emptyExample counterExample (st ^. #counter) - } - - data Hoogle = Hoogle { search :: Input Search , targets :: Dropdown 'One Target @@ -111,116 +81,6 @@ data Hoogle = Hoogle deriving Monoid via GenericMonoid Hoogle -data RGettingStarted - = RGSIndex - | RGSAddingToYourProject - | RGSExtendAnExample - deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) - - -type SPAGettingStarted m - = View m Frontend - :<|> "adding-to-your-project" :> View m Frontend - :<|> "extend-an-example" :> View m Frontend - - -gettingStartedRoutes :: SPAGettingStarted m :>> Route -gettingStartedRoutes - = RGettingStarted RGSIndex - :<|> RGettingStarted RGSAddingToYourProject - :<|> RGettingStarted RGSExtendAnExample - - -data RPackages - = RPIndex - | RPCore - | RPConsole - | RPBackends - | RPHtml - | RPLens - | RPRouter - | RPWidgets - deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) - - -type SPAPackages m - = View m Frontend - :<|> "core" :> View m Frontend - :<|> "console" :> View m Frontend - :<|> "backends" :> View m Frontend - :<|> "html" :> View m Frontend - :<|> "lens" :> View m Frontend - :<|> "router" :> View m Frontend - :<|> "widgets" :> View m Frontend - - -packagesRoutes :: SPAPackages m :>> Route -packagesRoutes - = RPackages RPIndex - :<|> RPackages RPCore - :<|> RPackages RPConsole - :<|> RPackages RPBackends - :<|> RPackages RPHtml - :<|> RPackages RPLens - :<|> RPackages RPRouter - :<|> RPackages RPWidgets - - -data RTutorial - = RTIndex - | RTCalculator - | RTImmediateExecution - | RTComposing - deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) - - -type SPATutorial m - = View m Frontend - :<|> "calculator" :> View m Frontend - :<|> "immediate-execution" :> View m Frontend - :<|> "composing" :> View m Frontend - - -tutorialRoutes :: SPATutorial m :>> Route -tutorialRoutes - = RTutorial RTIndex - :<|> RTutorial RTCalculator - :<|> RTutorial RTImmediateExecution - :<|> RTutorial RTComposing - - -data Route - = RHome - | RConcepts - | RGettingStarted RGettingStarted - | RPackages RPackages - | RTutorial RTutorial - | RSandbox - | RFourOhFour - deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) - - -type SPA m - = "home" :> View m Frontend - :<|> "concepts" :> View m Frontend - :<|> "getting-started" :> SPAGettingStarted m - :<|> "packages" :> SPAPackages m - :<|> "tutorial" :> SPATutorial m - :<|> "404" :> View m Frontend - :<|> View m Frontend - - -routes :: SPA m :>> Route -routes - = RHome - :<|> RConcepts - :<|> gettingStartedRoutes - :<|> packagesRoutes - :<|> tutorialRoutes - :<|> RFourOhFour - :<|> RHome - - data Frontend = MHome Home | MConcepts diff --git a/website/Shpadoinkle/Website/Types/Example.hs b/website/Shpadoinkle/Website/Types/Example.hs new file mode 100644 index 00000000..54d429dc --- /dev/null +++ b/website/Shpadoinkle/Website/Types/Example.hs @@ -0,0 +1,24 @@ +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} + + +module Shpadoinkle.Website.Types.Example where + + +import Data.Aeson (FromJSON, ToJSON) +import GHC.Generics (Generic) +import Shpadoinkle (NFData) +import Shpadoinkle.Isreal.Types (Code, SnowNonce, + SnowToken) +import Shpadoinkle.Website.Types.ExampleState (ExampleState) + + +data Example = Example + { inputHaskell :: Code + , snowToken :: SnowToken + , snowNonce :: SnowNonce + , state :: ExampleState + } + deriving stock (Eq, Ord, Show, Read, Generic) + deriving anyclass (FromJSON, ToJSON, NFData) diff --git a/website/Shpadoinkle/Website/Types/ExampleState.hs b/website/Shpadoinkle/Website/Types/ExampleState.hs new file mode 100644 index 00000000..4ea05852 --- /dev/null +++ b/website/Shpadoinkle/Website/Types/ExampleState.hs @@ -0,0 +1,17 @@ +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} + + +module Shpadoinkle.Website.Types.ExampleState where + + +import Data.Aeson (FromJSON, ToJSON) +import GHC.Generics (Generic) +import Shpadoinkle (NFData) +import Shpadoinkle.Isreal.Types (CompileError) + + +data ExampleState = EError CompileError | ELoading | EReady + deriving stock (Eq, Ord, Show, Read, Generic) + deriving anyclass (FromJSON, ToJSON, NFData) diff --git a/website/Shpadoinkle/Website/Types/Home.hs b/website/Shpadoinkle/Website/Types/Home.hs new file mode 100644 index 00000000..ddcec9bb --- /dev/null +++ b/website/Shpadoinkle/Website/Types/Home.hs @@ -0,0 +1,41 @@ +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE OverloadedLabels #-} + + +module Shpadoinkle.Website.Types.Home where + + +import Control.Lens ((^.)) +import Data.Aeson (FromJSON, ToJSON) +import GHC.Generics (Generic) +import Shpadoinkle (NFData) +import Shpadoinkle.Website.Types.Example + + +data Home = Home + { hoogle :: Hoogle + , examples :: Examples Example + } + deriving stock (Eq, Ord, Show, Read, Generic) + deriving anyclass (FromJSON, ToJSON, NFData) + + +data Examples a = Examples + { helloWorld :: a + , counter :: a + } + deriving stock (Eq, Ord, Show, Read, Generic) + deriving anyclass (FromJSON, ToJSON, NFData) + + +emptyExample :: Code -> SnowToken -> Example +emptyExample cc st = Example cc st 0 ELoading + + +emptyHome :: Examples SnowToken -> Home +emptyHome st = Home mempty $ Examples + { helloWorld = emptyExample helloWorldExample (st ^. #helloWorld) + , counter = emptyExample counterExample (st ^. #counter) + } diff --git a/website/Shpadoinkle/Website/Types/Nav.hs b/website/Shpadoinkle/Website/Types/Nav.hs new file mode 100644 index 00000000..9a35e759 --- /dev/null +++ b/website/Shpadoinkle/Website/Types/Nav.hs @@ -0,0 +1,25 @@ +{-# LANGUAGE LambdaCase #-} + + +module Shpadoinkle.Website.Types.Nav where + + +import Shpadoinkle.Widgets.Types (Humanize (..)) + + +data Nav + = NHome + | NGettingStarted + | NTutorial + | NSandbox + deriving (Eq, Ord, Enum, Bounded) + + +instance Humanize Nav where + humanize = \case + NHome -> "Home" + NGettingStarted -> "Get Started" + NTutorial -> "Tutorial" + NSandbox -> "Sandbox" + + diff --git a/website/assets/nav_line.svg b/website/assets/nav_line.svg index a6117261..8104e9bf 100644 --- a/website/assets/nav_line.svg +++ b/website/assets/nav_line.svg @@ -1,4 +1,4 @@ - + diff --git a/website/package.yaml b/website/package.yaml index 8f938d06..ad7c998e 100644 --- a/website/package.yaml +++ b/website/package.yaml @@ -77,8 +77,13 @@ executables: - Shpadoinkle.Website.Partials.Footer - Shpadoinkle.Website.Page.Home - Shpadoinkle.Website.Page.Documentation + - Shpadoinkle.Website.Routes - Shpadoinkle.Website.Types + - Shpadoinkle.Website.Types.Nav + - Shpadoinkle.Website.Types.Home - Shpadoinkle.Website.Types.Hoogle + - Shpadoinkle.Website.Types.Example + - Shpadoinkle.Website.Types.ExampleState - Shpadoinkle.Website.Style - Shpadoinkle.Website.View source-dirs: . diff --git a/widgets/Shpadoinkle/Widgets/Types/Core.hs b/widgets/Shpadoinkle/Widgets/Types/Core.hs index 47a1094f..26c52f19 100644 --- a/widgets/Shpadoinkle/Widgets/Types/Core.hs +++ b/widgets/Shpadoinkle/Widgets/Types/Core.hs @@ -83,6 +83,7 @@ class Present a where instance {-# OVERLAPPABLE #-} Humanize a => Present a + #ifdef TESTING instance Arbitrary Hygiene where arbitrary = arbitraryBoundedEnum #endif -- GitLab From 2780d1122989ccd9a6af99019300fe612751d7cc Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 12 Apr 2021 13:05:12 -0600 Subject: [PATCH 028/116] home page fully refactored --- html/Shpadoinkle/Html/Property.hs | 6 +- router/Shpadoinkle/Router.hs | 24 ++++ .../Website/Component/LiveExample.hs | 83 ++++++++---- .../Shpadoinkle/Website/Page/Documentation.hs | 40 +++--- website/Shpadoinkle/Website/Page/Home.hs | 10 +- .../Shpadoinkle/Website/Partials/Footer.hs | 20 +-- website/Shpadoinkle/Website/Partials/Nav.hs | 18 ++- .../Shpadoinkle/Website/Partials/Template.hs | 9 +- website/Shpadoinkle/Website/Routes.hs | 111 ---------------- website/Shpadoinkle/Website/Run.hs | 86 +++++++------ website/Shpadoinkle/Website/Types.hs | 120 ------------------ .../Website/Types/Effects/Example.hs | 18 +++ .../Website/Types/Effects/Hooglable.hs | 17 +++ .../Shpadoinkle/Website/Types/Effects/Swan.hs | 20 +++ website/Shpadoinkle/Website/Types/Frontend.hs | 26 ++++ website/Shpadoinkle/Website/Types/Home.hs | 25 +++- website/Shpadoinkle/Website/Types/Hoogle.hs | 81 ++++-------- .../Shpadoinkle/Website/Types/Hoogle/API.hs | 18 +++ .../Website/Types/Hoogle/Target.hs | 63 +++++++++ website/Shpadoinkle/Website/Types/Nav.hs | 3 +- website/Shpadoinkle/Website/Types/Routes.hs | 55 ++++++++ .../Website/Types/Routes/GettingStarted.hs | 35 +++++ .../Website/Types/Routes/Packages.hs | 49 +++++++ .../Website/Types/Routes/Tutorial.hs | 37 ++++++ website/Shpadoinkle/Website/View.hs | 5 +- website/assets/Adelle-Bold.woff2 | Bin 0 -> 49868 bytes website/assets/Adelle-BoldItalic.woff2 | Bin 0 -> 48484 bytes website/assets/Adelle-Italic.woff2 | Bin 0 -> 48568 bytes website/assets/Adelle-Regular.woff2 | Bin 0 -> 50144 bytes website/assets/style.css | 2 +- website/package.yaml | 12 +- 31 files changed, 589 insertions(+), 404 deletions(-) delete mode 100644 website/Shpadoinkle/Website/Routes.hs delete mode 100644 website/Shpadoinkle/Website/Types.hs create mode 100644 website/Shpadoinkle/Website/Types/Effects/Example.hs create mode 100644 website/Shpadoinkle/Website/Types/Effects/Hooglable.hs create mode 100644 website/Shpadoinkle/Website/Types/Effects/Swan.hs create mode 100644 website/Shpadoinkle/Website/Types/Frontend.hs create mode 100644 website/Shpadoinkle/Website/Types/Hoogle/API.hs create mode 100644 website/Shpadoinkle/Website/Types/Hoogle/Target.hs create mode 100644 website/Shpadoinkle/Website/Types/Routes.hs create mode 100644 website/Shpadoinkle/Website/Types/Routes/GettingStarted.hs create mode 100644 website/Shpadoinkle/Website/Types/Routes/Packages.hs create mode 100644 website/Shpadoinkle/Website/Types/Routes/Tutorial.hs create mode 100755 website/assets/Adelle-Bold.woff2 create mode 100755 website/assets/Adelle-BoldItalic.woff2 create mode 100755 website/assets/Adelle-Italic.woff2 create mode 100755 website/assets/Adelle-Regular.woff2 diff --git a/html/Shpadoinkle/Html/Property.hs b/html/Shpadoinkle/Html/Property.hs index c6c6bbe7..ae6a7bf3 100644 --- a/html/Shpadoinkle/Html/Property.hs +++ b/html/Shpadoinkle/Html/Property.hs @@ -93,7 +93,11 @@ $(msum <$> mapM mkTextProp ]) $(msum <$> mapM mkIntProp - [ "tabIndex", "width", "height" ]) + [ "tabIndex", "width", "height", "maxLength", "minLength" ]) + + +newTab :: (Text, Prop m a) +newTab = target "_blank" tabbable :: (Text, Prop m a) diff --git a/router/Shpadoinkle/Router.hs b/router/Shpadoinkle/Router.hs index 46eaf109..72911275 100644 --- a/router/Shpadoinkle/Router.hs +++ b/router/Shpadoinkle/Router.hs @@ -41,6 +41,8 @@ module Shpadoinkle.Router ( , withHydration, toHydration -- * Re-Exports , Raw, S.RawM', S.RawM, MonadJSM, HasLink(..) + -- * Sub route utilities + , MapSwitch (..) ) where @@ -502,3 +504,25 @@ data View :: (Type -> Type) -> Type -> Type instance HasLink (View m a) where type MkLink (View m a) b = b toLink toA _ = toA + + +type family SwitchOutput layout b :: Type where + SwitchOutput (a :<|> r) b = b :<|> SwitchOutput r b + SwitchOutput a b = b + + +type family SwitchInput layout b :: Type where + SwitchInput (a :<|> r) b = a + SwitchInput a b = a + + +class MapSwitch layout b where + mapUnions :: (SwitchInput layout b -> b) -> layout -> SwitchOutput layout b + + +instance (MapSwitch r b, SwitchInput r b ~ a) => MapSwitch (a :<|> r) b where + mapUnions f (a :<|> r) = f a :<|> mapUnions @r @b f r + + +instance {-# OVERLAPPABLE #-} (SwitchOutput a b ~ b, SwitchInput a b ~ a) => MapSwitch a b where + mapUnions f a = f a diff --git a/website/Shpadoinkle/Website/Component/LiveExample.hs b/website/Shpadoinkle/Website/Component/LiveExample.hs index c4a0e977..936cd331 100644 --- a/website/Shpadoinkle/Website/Component/LiveExample.hs +++ b/website/Shpadoinkle/Website/Component/LiveExample.hs @@ -3,30 +3,52 @@ {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-} module Shpadoinkle.Website.Component.LiveExample where -import Control.Lens -import Control.Monad.Reader (MonadReader (ask)) -import Data.List as List (intersperse) -import Data.Text as T -import Data.Text.Lazy (fromStrict, toStrict) -import Data.Text.Lazy.Encoding (decodeUtf8, encodeUtf8) -import GHCJS.DOM (currentDocumentUnchecked, - currentWindowUnchecked) -import GHCJS.DOM.Document (createElement) -import GHCJS.DOM.WindowOrWorkerGlobalScope (setTimeout) -import Language.Javascript.JSaddle -import Prelude hiding (div) -import Servant.API (toUrlPiece) -import Shpadoinkle -import Shpadoinkle.Html -import Shpadoinkle.Isreal.Types as Swan -import Shpadoinkle.Lens -import Shpadoinkle.Website.Types +import Control.Lens ((%~), (.~), (^.)) +import Control.Monad.Reader (MonadReader (ask)) +import Data.ByteString.Lazy as BS (fromStrict) +import Data.FileEmbed (embedFile) +import Data.Generics.Labels () +import Data.List as List (intersperse) +import Data.Text as T +import Data.Text.Lazy as TL (fromStrict, + splitOn, + toStrict) +import Data.Text.Lazy.Encoding (decodeUtf8, + encodeUtf8) +import GHCJS.DOM (currentDocumentUnchecked, + currentWindowUnchecked) +import GHCJS.DOM.Document (createElement) +import GHCJS.DOM.WindowOrWorkerGlobalScope (setTimeout) +import Language.Javascript.JSaddle (JSM, MonadJSM, + Object, fromJSVal, + fun, js0, js2, jsg2, + obj, toJSVal, (<#)) +import Prelude hiding (div) +import Servant.API (toUrlPiece) +import Shpadoinkle (Continuation, Html, + RawNode (..), + atomically, baked, + constUpdate, done, + kleisli, mapC, pur, + readTVarIO, text, + writeTVar) +import Shpadoinkle.Html (br'_, className, + div, iframe, + mkGlobalMailboxAfforded, + onInput, src) +import Shpadoinkle.Isreal.Types as Swan +import Shpadoinkle.Lens (onRecord) +import Shpadoinkle.Website.Types.Effects.Example (ExampleEffects, + Swan (..)) +import Shpadoinkle.Website.Types.Example (Example (..)) +import Shpadoinkle.Website.Types.ExampleState (ExampleState (..)) default (Text) @@ -50,6 +72,21 @@ example (Example cc token nonce state') = div "example" ] +exampleTemplate :: Code -> Code +exampleTemplate (Code inputHaskell') = Code + $ BS.fromStrict $(embedFile "./example.template.top") + <> inputHaskell' + <> BS.fromStrict $(embedFile "./example.template.bottom") + + +topOffset :: Int +topOffset = subtract 1 + . Prelude.length + . TL.splitOn "\n" + . decodeUtf8 + $ BS.fromStrict $(embedFile "./example.template.top") + + compileExample :: (MonadJSM m, ExampleEffects m) => Continuation m Example compileExample = kleisli $ \(Example cc token nonce _) -> do mutex <- ask @@ -70,7 +107,7 @@ compileExample = kleisli $ \(Example cc token nonce _) -> do res' <- compile token (nonce + 1) $ exampleTemplate cc' ret res' 2 where - ret res n = + ret res (n :: SnowNonce) = return . pur $ (#snowNonce %~ (+ n)) . case res of Left e -> #state .~ EError e Right _ -> #state .~ EReady @@ -80,12 +117,12 @@ errorMessages :: CompileError -> Html m a errorMessages = div "errors" . fmap singleError . breakError . replace bunkWarning "" . unCompileError where singleError e = - let offLineNumber = Prelude.head . splitOn ":" $ T.drop 1 e + let offLineNumber = Prelude.head . T.splitOn ":" $ T.drop 1 e lineNumber = pack . show . subtract topOffset . read $ unpack offLineNumber pad = let padSize = T.length offLineNumber - T.length lineNumber in if padSize > 0 then T.replicate padSize " " else "" replaceLineNumber = replace (offLineNumber <> ":") (lineNumber <> ":") . replace (offLineNumber <> " |") (pad <> lineNumber <> " |") - in div "error" . List.intersperse br'_ . fmap (text . replaceLineNumber) . splitOn "\n" $ T.drop 1 e - breakError = Prelude.filter (not . T.null . strip) . splitOn "Main.hs" + in div "error" . List.intersperse br'_ . fmap (text . replaceLineNumber) . T.splitOn "\n" $ T.drop 1 e + breakError = Prelude.filter (not . T.null . strip) . T.splitOn "Main.hs" bunkWarning = "Warning: don't know how to find change monitoring files for the installed\npackage databases for ghcjs\n" @@ -110,7 +147,7 @@ mirror cc = baked $ do _ <- cm ^. js2 "on" "change" (fun $ \_ _ _ -> do jsv <- cm ^. js0 "getValue" raw :: Maybe Text <- fromJSVal jsv - maybe (pure ()) (notify . Code . encodeUtf8 . fromStrict) raw + maybe (pure ()) (notify . Code . encodeUtf8 . TL.fromStrict) raw ) window <- currentWindowUnchecked _ <- setTimeout window (fun $ \_ _ _ -> () <$ cm ^. js0 "refresh") (Just 33) diff --git a/website/Shpadoinkle/Website/Page/Documentation.hs b/website/Shpadoinkle/Website/Page/Documentation.hs index 4472e158..3d549412 100644 --- a/website/Shpadoinkle/Website/Page/Documentation.hs +++ b/website/Shpadoinkle/Website/Page/Documentation.hs @@ -8,7 +8,9 @@ module Shpadoinkle.Website.Page.Documentation where import Data.Text import Shpadoinkle.Html import Shpadoinkle.Template.TH -import Shpadoinkle.Website.Types +import qualified Shpadoinkle.Website.Types.Routes.GettingStarted as GettingStarted +import qualified Shpadoinkle.Website.Types.Routes.Packages as Packages +import qualified Shpadoinkle.Website.Types.Routes.Tutorial as Tutorial concepts @@ -33,28 +35,28 @@ rtImmediateExecution = div_ $(embedAsciidoc "./docs/tutorial/immediate-executio rtComposing = div_ $(embedAsciidoc "./docs/tutorial/composing.adoc") -gettingStarted :: RGettingStarted -> Html m a +gettingStarted :: GettingStarted.Route -> Html m a gettingStarted = \case - RGSIndex -> gsIndex - RGSAddingToYourProject -> gsAddingToYourProject - RGSExtendAnExample -> gsExtendAnExample + GettingStarted.RGSIndex -> gsIndex + GettingStarted.RGSAddingToYourProject -> gsAddingToYourProject + GettingStarted.RGSExtendAnExample -> gsExtendAnExample -packages :: RPackages -> Html m a +packages :: Packages.Route -> Html m a packages = \case - RPIndex -> rpIndex - RPCore -> rpCore - RPConsole -> rpConsole - RPBackends -> rpBackends - RPHtml -> rpHtml - RPLens -> rpLens - RPRouter -> rpRouter - RPWidgets -> rpWidgets + Packages.RPIndex -> rpIndex + Packages.RPCore -> rpCore + Packages.RPConsole -> rpConsole + Packages.RPBackends -> rpBackends + Packages.RPHtml -> rpHtml + Packages.RPLens -> rpLens + Packages.RPRouter -> rpRouter + Packages.RPWidgets -> rpWidgets -tutorial :: RTutorial -> Html m a +tutorial :: Tutorial.Route -> Html m a tutorial = \case - RTIndex -> rtIndex - RTCalculator -> rtCalculator - RTImmediateExecution -> rtImmediateExecution - RTComposing -> rtComposing + Tutorial.RTIndex -> rtIndex + Tutorial.RTCalculator -> rtCalculator + Tutorial.RTImmediateExecution -> rtImmediateExecution + Tutorial.RTComposing -> rtComposing diff --git a/website/Shpadoinkle/Website/Page/Home.hs b/website/Shpadoinkle/Website/Page/Home.hs index 6cd5c479..fb52a722 100644 --- a/website/Shpadoinkle/Website/Page/Home.hs +++ b/website/Shpadoinkle/Website/Page/Home.hs @@ -5,12 +5,12 @@ module Shpadoinkle.Website.Page.Home where -import Data.Text (Text) -import Prelude hiding (div, span) -import Shpadoinkle.Html as H +import Data.Text (Text) +import Prelude hiding (div, span) +import Shpadoinkle.Html as H import Shpadoinkle.Html.TH.AssetLink -import Shpadoinkle.Website.Style as Style -import Shpadoinkle.Website.Types +import Shpadoinkle.Website.Style as Style +import Shpadoinkle.Website.Types.Home (Home) view :: Home -> [Html m Home] diff --git a/website/Shpadoinkle/Website/Partials/Footer.hs b/website/Shpadoinkle/Website/Partials/Footer.hs index 45c21f14..0fea4b0b 100644 --- a/website/Shpadoinkle/Website/Partials/Footer.hs +++ b/website/Shpadoinkle/Website/Partials/Footer.hs @@ -6,11 +6,13 @@ module Shpadoinkle.Website.Partials.Footer (view) where import Prelude hiding (div) -import Shpadoinkle.Html -import Shpadoinkle.Html.TH.AssetLink +import Shpadoinkle.Html (Html, a, alt, class', div, div_, + footer_, href, img', li_, nav_, + newTab, p, src, text, ul) +import Shpadoinkle.Html.TH.AssetLink (assetLink) import Shpadoinkle.Website.Style as Style -import Shpadoinkle.Website.Types -import Shpadoinkle.Widgets.Types +import Shpadoinkle.Website.Types.Nav (Nav) +import Shpadoinkle.Widgets.Types (humanize) footerLink :: Nav -> Html m a @@ -41,16 +43,16 @@ view = , nav_ [ ul [ class' footer__nav__links ] [ li_ - [ a [ href "/" ] - [ "Reddit" ] + [ a [ newTab, href "https://shpadoinkle.zulipchat.com" ] + [ "Zulip" ] ] , li_ - [ a [ href "/" ] + [ a [ newTab, href "https://twitter.com/ShpadoinkleUI" ] [ "Twitter" ] ] , li_ - [ a [ href "/" ] - [ "Hackage" ] + [ a [ newTab, href "https://gitlab.com/platonic/shpadoinkle" ] + [ "GitLab" ] ] ] ] diff --git a/website/Shpadoinkle/Website/Partials/Nav.hs b/website/Shpadoinkle/Website/Partials/Nav.hs index 1dc67281..d2617a02 100644 --- a/website/Shpadoinkle/Website/Partials/Nav.hs +++ b/website/Shpadoinkle/Website/Partials/Nav.hs @@ -6,14 +6,24 @@ module Shpadoinkle.Website.Partials.Nav (view) where -import Prelude hiding (div) -import Shpadoinkle.Html as H +import Prelude hiding (div) +import Shpadoinkle.Html as H import Shpadoinkle.Html.TH.AssetLink -import Shpadoinkle.Website.Style as Style -import Shpadoinkle.Website.Types +import Shpadoinkle.Website.Style as Style +import Shpadoinkle.Website.Types.Frontend +import Shpadoinkle.Website.Types.Nav import Shpadoinkle.Widgets.Types +toActive :: Frontend -> Maybe Nav +toActive = \case + MHome _ -> Just NHome + MGettingStarted _ -> Just NGettingStarted + MTutorial _ -> Just NTutorial + MSandbox -> Just NSandbox + _ -> Nothing + + navLinkDesktop :: Frontend -> Nav -> Html m a navLinkDesktop fe n = li [ class' nav__link ] $ a [ href "getstarted.html" ] [ text $ humanize n ] diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index 9d536959..af50eede 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -8,16 +8,15 @@ module Shpadoinkle.Website.Partials.Template ) where -import Data.Text +import Data.Text (Text) import Prelude hiding (div) -import Shpadoinkle (h) import Shpadoinkle.Html -import Shpadoinkle.Html.TH.AssetLink -import Shpadoinkle.Router +import Shpadoinkle.Html.TH.AssetLink (assetLink) +import Shpadoinkle.Router (toHydration) import Shpadoinkle.Run (Env (..), entrypoint) import qualified Shpadoinkle.Website.Partials.Footer as Footer import qualified Shpadoinkle.Website.Partials.Nav as Nav -import Shpadoinkle.Website.Types +import Shpadoinkle.Website.Types.Frontend (Frontend) codemirrorCDN :: Text -> Text diff --git a/website/Shpadoinkle/Website/Routes.hs b/website/Shpadoinkle/Website/Routes.hs deleted file mode 100644 index 191bf79b..00000000 --- a/website/Shpadoinkle/Website/Routes.hs +++ /dev/null @@ -1,111 +0,0 @@ -module Shpadoinkle.Website.Routes where - - -data RGettingStarted - = RGSIndex - | RGSAddingToYourProject - | RGSExtendAnExample - deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) - - -type SPAGettingStarted m - = View m Frontend - :<|> "adding-to-your-project" :> View m Frontend - :<|> "extend-an-example" :> View m Frontend - - -gettingStartedRoutes :: SPAGettingStarted m :>> Route -gettingStartedRoutes - = RGettingStarted RGSIndex - :<|> RGettingStarted RGSAddingToYourProject - :<|> RGettingStarted RGSExtendAnExample - - -data RPackages - = RPIndex - | RPCore - | RPConsole - | RPBackends - | RPHtml - | RPLens - | RPRouter - | RPWidgets - deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) - - -type SPAPackages m - = View m Frontend - :<|> "core" :> View m Frontend - :<|> "console" :> View m Frontend - :<|> "backends" :> View m Frontend - :<|> "html" :> View m Frontend - :<|> "lens" :> View m Frontend - :<|> "router" :> View m Frontend - :<|> "widgets" :> View m Frontend - - -packagesRoutes :: SPAPackages m :>> Route -packagesRoutes - = RPackages RPIndex - :<|> RPackages RPCore - :<|> RPackages RPConsole - :<|> RPackages RPBackends - :<|> RPackages RPHtml - :<|> RPackages RPLens - :<|> RPackages RPRouter - :<|> RPackages RPWidgets - - -data RTutorial - = RTIndex - | RTCalculator - | RTImmediateExecution - | RTComposing - deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) - - -type SPATutorial m - = View m Frontend - :<|> "calculator" :> View m Frontend - :<|> "immediate-execution" :> View m Frontend - :<|> "composing" :> View m Frontend - - -tutorialRoutes :: SPATutorial m :>> Route -tutorialRoutes - = RTutorial RTIndex - :<|> RTutorial RTCalculator - :<|> RTutorial RTImmediateExecution - :<|> RTutorial RTComposing - - -data Route - = RHome - | RConcepts - | RGettingStarted RGettingStarted - | RPackages RPackages - | RTutorial RTutorial - | RSandbox - | RFourOhFour - deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) - - -type SPA m - = "home" :> View m Frontend - :<|> "concepts" :> View m Frontend - :<|> "getting-started" :> SPAGettingStarted m - :<|> "packages" :> SPAPackages m - :<|> "tutorial" :> SPATutorial m - :<|> "404" :> View m Frontend - :<|> View m Frontend - - -routes :: SPA m :>> Route -routes - = RHome - :<|> RConcepts - :<|> gettingStartedRoutes - :<|> packagesRoutes - :<|> tutorialRoutes - :<|> RFourOhFour - :<|> RHome diff --git a/website/Shpadoinkle/Website/Run.hs b/website/Shpadoinkle/Website/Run.hs index 89969412..0c02116b 100644 --- a/website/Shpadoinkle/Website/Run.hs +++ b/website/Shpadoinkle/Website/Run.hs @@ -11,59 +11,67 @@ module Main where -import Control.Monad.Catch (MonadThrow) -import Control.Monad.IO.Class (MonadIO (..)) +import Control.Monad.Catch (MonadThrow) +import Control.Monad.IO.Class (MonadIO (..)) import Control.Monad.Reader -import Data.Proxy (Proxy (..)) -import Data.Text (Text) -import Servant.API (type (:<|>) ((:<|>))) +import Data.Proxy (Proxy (..)) +import Data.Text (Text) +import Servant.API (type (:<|>) ((:<|>))) #ifndef ghcjs_HOST_OS -import Servant.Server as Servant (serve) +import Servant.Server as Servant (serve) #endif #ifndef ghcjs_HOST_OS -import Shpadoinkle (JSM, MonadJSM, - MonadUnliftIO (..), - TVar, constUpdate, - liftJSM, newTVarIO) +import Shpadoinkle (JSM, MonadJSM, + MonadUnliftIO (..), + TVar, constUpdate, + liftJSM, + newTVarIO) #else -import Shpadoinkle (JSM, MonadUnliftIO (..), - TVar, constUpdate, - liftJSM, newTVarIO) +import Shpadoinkle (JSM, + MonadUnliftIO (..), + TVar, constUpdate, + liftJSM, + newTVarIO) #endif -import Shpadoinkle.Backend.Snabbdom (runSnabbdom, stage) -import Shpadoinkle.Isreal.Types as Swan (API, Code, - CompileError, - SnowNonce, - SnowToken (..)) -import Shpadoinkle.Router (fullPageSPA', - withHydration) -import Shpadoinkle.Router.Client (BaseUrl (..), - ClientEnv (..), ClientM, - Scheme (Https), client, - runXHR') -import Shpadoinkle.Widgets.Types (Search (..)) +import Shpadoinkle.Backend.Snabbdom (runSnabbdom, + stage) +import Shpadoinkle.Isreal.Types as Swan (API, Code, + CompileError, + SnowNonce, + SnowToken (..)) +import Shpadoinkle.Router (fullPageSPA', + withHydration) +import Shpadoinkle.Router.Client (BaseUrl (..), + ClientEnv (..), + ClientM, + Scheme (Https), + client, runXHR') +import Shpadoinkle.Widgets.Types (Search (..)) #ifndef ghcjs_HOST_OS -import Shpadoinkle.Router.Server (serveUI) -import Shpadoinkle.Run (Env (Dev), - liveWithBackend, - runJSorWarp) +import Shpadoinkle.Router.Server (serveUI) +import Shpadoinkle.Run (Env (Dev), + liveWithBackend, + runJSorWarp) #else -import Shpadoinkle.Run (runJSorWarp) +import Shpadoinkle.Run (runJSorWarp) #endif import Shpadoinkle.DeveloperTools -import Shpadoinkle.Website.Types (Frontend, - Hooglable (..), - HoogleAPI, - Route (RFourOhFour), - SPA, Swan (..), routes) -import Shpadoinkle.Website.Types.Hoogle (Target) +import Shpadoinkle.Website.Types.Effects.Hooglable (Hooglable (..)) +import Shpadoinkle.Website.Types.Effects.Swan (Swan (..)) +import Shpadoinkle.Website.Types.Frontend (Frontend) +import Shpadoinkle.Website.Types.Hoogle.API as Hoogle +import Shpadoinkle.Website.Types.Hoogle.Target (Target) +import Shpadoinkle.Website.Types.Routes (Route (RFourOhFour), + SPA, routes) #ifndef ghcjs_HOST_OS -import Shpadoinkle.Website.Partials.Template (template) -import Shpadoinkle.Website.View (start, startJS, view) +import Shpadoinkle.Website.Partials.Template (template) +import Shpadoinkle.Website.View (start, startJS, + view) #else -import Shpadoinkle.Website.View (start, startJS, view) +import Shpadoinkle.Website.View (start, startJS, + view) #endif diff --git a/website/Shpadoinkle/Website/Types.hs b/website/Shpadoinkle/Website/Types.hs deleted file mode 100644 index 76d6a823..00000000 --- a/website/Shpadoinkle/Website/Types.hs +++ /dev/null @@ -1,120 +0,0 @@ -{-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE DerivingVia #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE OverloadedLabels #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeOperators #-} - - -module Shpadoinkle.Website.Types where - - -import Control.Lens ((^.)) -import Control.Monad.Except (MonadTrans (..)) -import Control.Monad.Reader -import Data.Aeson (FromJSON, ToJSON) -import Data.ByteString.Lazy (fromStrict) -import Data.FileEmbed (embedFile) -import Data.Generics.Labels () -import Data.Monoid.Generic (GenericMonoid (..), - GenericSemigroup (..)) -import Data.Text (Text, splitOn) -import Data.Text.Encoding (decodeUtf8) -import GHC.Generics (Generic) -import Servant.API (Get, JSON, QueryParam, - type (:<|>) (..), type (:>)) -import Shpadoinkle (NFData, TVar) -import Shpadoinkle.Isreal.Types (Code (..), CompileError, - SnowNonce, SnowToken) -import Shpadoinkle.Router (HasRouter (..), View) -import Shpadoinkle.Widgets.Form.Dropdown -import Shpadoinkle.Widgets.Types (Humanize (..), Input, - Pick (One), Search (..)) - -import Shpadoinkle.Website.Types.Hoogle (Target) -import Shpadoinkle.Website.Types.Nav - - - -toActive :: Frontend -> Maybe Nav -toActive = \case - MHome _ -> Just NHome - MGettingStarted _ -> Just NGettingStarted - MTutorial _ -> Just NTutorial - MSandbox -> Just NSandbox - _ -> Nothing - - - -helloWorldExample :: Code -helloWorldExample = Code $ fromStrict $(embedFile "./hello-world.example") - - -counterExample :: Code -counterExample = Code $ fromStrict $(embedFile "./counter.example") - - -exampleTemplate :: Code -> Code -exampleTemplate (Code inputHaskell') = Code - $ fromStrict $(embedFile "./example.template.top") - <> inputHaskell' - <> fromStrict $(embedFile "./example.template.bottom") - - -topOffset :: Int -topOffset = subtract 1 $ length $ splitOn "\n" $ decodeUtf8 $(embedFile "./example.template.top") - - -data Hoogle = Hoogle - { search :: Input Search - , targets :: Dropdown 'One Target - } - deriving stock (Eq, Ord, Show, Read, Generic) - deriving anyclass (FromJSON, ToJSON, NFData) - deriving Semigroup via GenericSemigroup Hoogle - deriving Monoid via GenericMonoid Hoogle - - -data Frontend - = MHome Home - | MConcepts - | MGettingStarted RGettingStarted - | MPackages RPackages - | MTutorial RTutorial - | MSandbox - | MFourOhFour - deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) - - -type HoogleAPI = QueryParam "mode" Text - :> QueryParam "hoogle" Text - :> QueryParam "start" Int - :> QueryParam "count" Int - :> Get '[JSON] [Target] - - -class Swan m where - compile :: SnowToken -> SnowNonce -> Code -> m (Either CompileError Text) - clean :: SnowToken -> m Text - - -type ExampleEffects m = (MonadReader (TVar (Maybe Code)) m, Swan m) - - -instance (MonadTrans t, Monad m, Swan m) => Swan (t m) where - compile x y = lift . compile x y - clean = lift . clean - - -class Hooglable m where - findTargets :: Search -> m [Target] - - -instance (MonadTrans t, Monad m, Hooglable m) => Hooglable (t m) where - findTargets = lift . findTargets diff --git a/website/Shpadoinkle/Website/Types/Effects/Example.hs b/website/Shpadoinkle/Website/Types/Effects/Example.hs new file mode 100644 index 00000000..3e86af7b --- /dev/null +++ b/website/Shpadoinkle/Website/Types/Effects/Example.hs @@ -0,0 +1,18 @@ +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE FlexibleContexts #-} + + +module Shpadoinkle.Website.Types.Effects.Example + ( ExampleEffects + , module Control.Monad.Reader + , module Shpadoinkle.Website.Types.Effects.Swan + ) where + + +import Control.Monad.Reader +import Shpadoinkle.Isreal.Types (Code) +import Shpadoinkle.Website.Types.Effects.Swan +import UnliftIO.STM (TVar) + + +type ExampleEffects m = (MonadReader (TVar (Maybe Code)) m, Swan m) diff --git a/website/Shpadoinkle/Website/Types/Effects/Hooglable.hs b/website/Shpadoinkle/Website/Types/Effects/Hooglable.hs new file mode 100644 index 00000000..5159803c --- /dev/null +++ b/website/Shpadoinkle/Website/Types/Effects/Hooglable.hs @@ -0,0 +1,17 @@ +{-# LANGUAGE FlexibleInstances #-} + + +module Shpadoinkle.Website.Types.Effects.Hooglable where + + +import Control.Monad.Trans (MonadTrans, lift) +import Shpadoinkle.Website.Types.Hoogle.Target (Target) +import Shpadoinkle.Widgets.Types (Search) + + +class Hooglable m where + findTargets :: Search -> m [Target] + + +instance (MonadTrans t, Monad m, Hooglable m) => Hooglable (t m) where + findTargets = lift . findTargets diff --git a/website/Shpadoinkle/Website/Types/Effects/Swan.hs b/website/Shpadoinkle/Website/Types/Effects/Swan.hs new file mode 100644 index 00000000..37975001 --- /dev/null +++ b/website/Shpadoinkle/Website/Types/Effects/Swan.hs @@ -0,0 +1,20 @@ +{-# LANGUAGE FlexibleInstances #-} + + +module Shpadoinkle.Website.Types.Effects.Swan where + + +import Control.Monad.Trans (MonadTrans (lift)) +import Data.Text (Text) +import Shpadoinkle.Isreal.Types (Code, CompileError, SnowNonce, + SnowToken) + + +class Swan m where + compile :: SnowToken -> SnowNonce -> Code -> m (Either CompileError Text) + clean :: SnowToken -> m Text + + +instance (MonadTrans t, Monad m, Swan m) => Swan (t m) where + compile x y = lift . compile x y + clean = lift . clean diff --git a/website/Shpadoinkle/Website/Types/Frontend.hs b/website/Shpadoinkle/Website/Types/Frontend.hs new file mode 100644 index 00000000..b9c487f7 --- /dev/null +++ b/website/Shpadoinkle/Website/Types/Frontend.hs @@ -0,0 +1,26 @@ +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} + + +module Shpadoinkle.Website.Types.Frontend where + + +import Data.Aeson (FromJSON, + ToJSON) +import GHC.Generics (Generic) +import Shpadoinkle (NFData) +import Shpadoinkle.Website.Types.Home (Home) +import qualified Shpadoinkle.Website.Types.Routes.GettingStarted as GettingStarted +import qualified Shpadoinkle.Website.Types.Routes.Packages as Packages +import qualified Shpadoinkle.Website.Types.Routes.Tutorial as Tutorial + + +data Frontend + = MHome Home + | MConcepts + | MGettingStarted GettingStarted.Route + | MPackages Packages.Route + | MTutorial Tutorial.Route + | MSandbox + | MFourOhFour + deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) diff --git a/website/Shpadoinkle/Website/Types/Home.hs b/website/Shpadoinkle/Website/Types/Home.hs index ddcec9bb..2ae1eaf6 100644 --- a/website/Shpadoinkle/Website/Types/Home.hs +++ b/website/Shpadoinkle/Website/Types/Home.hs @@ -2,16 +2,23 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE OverloadedLabels #-} +{-# LANGUAGE TemplateHaskell #-} module Shpadoinkle.Website.Types.Home where -import Control.Lens ((^.)) -import Data.Aeson (FromJSON, ToJSON) -import GHC.Generics (Generic) -import Shpadoinkle (NFData) -import Shpadoinkle.Website.Types.Example +import Control.Lens ((^.)) +import Data.Aeson (FromJSON, ToJSON) +import Data.ByteString.Lazy (fromStrict) +import Data.FileEmbed (embedFile) +import Data.Generics.Labels () +import GHC.Generics (Generic) +import Shpadoinkle (NFData) +import Shpadoinkle.Isreal.Types (Code (..), SnowToken) +import Shpadoinkle.Website.Types.Example (Example (..)) +import Shpadoinkle.Website.Types.ExampleState (ExampleState (ELoading)) +import Shpadoinkle.Website.Types.Hoogle (Hoogle) data Home = Home @@ -39,3 +46,11 @@ emptyHome st = Home mempty $ Examples { helloWorld = emptyExample helloWorldExample (st ^. #helloWorld) , counter = emptyExample counterExample (st ^. #counter) } + + +helloWorldExample :: Code +helloWorldExample = Code $ fromStrict $(embedFile "./hello-world.example") + + +counterExample :: Code +counterExample = Code $ fromStrict $(embedFile "./counter.example") diff --git a/website/Shpadoinkle/Website/Types/Hoogle.hs b/website/Shpadoinkle/Website/Types/Hoogle.hs index ebfc0d4b..feb70c05 100644 --- a/website/Shpadoinkle/Website/Types/Hoogle.hs +++ b/website/Shpadoinkle/Website/Types/Hoogle.hs @@ -1,63 +1,28 @@ -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingVia #-} module Shpadoinkle.Website.Types.Hoogle where -import Data.Aeson (FromJSON (parseJSON), ToJSON (toJSON), - object, withObject, (.:)) -import Data.Aeson.Types (emptyObject) -import qualified Data.Text as T -import GHC.Generics -import Shpadoinkle (NFData) - - -type URL = String - - --- | A location of documentation. -data Target = Target - {targetURL :: URL -- ^ URL where this thing is located - ,targetPackage :: Maybe (String, URL) -- ^ Name and URL of the package it is in (Nothing if it is a package) - ,targetModule :: Maybe (String, URL) -- ^ Name and URL of the module it is in (Nothing if it is a package or module) - ,targetType :: String -- ^ One of package, module or empty string - ,targetItem :: String -- ^ HTML span of the item, using @\@ for the name and @\@ onwards for arguments - ,targetDocs :: String -- ^ HTML documentation to show, a sequence of block level elements - } deriving (Show,Read,Eq,Ord,Generic,NFData) - - -instance Semigroup Target where - x <> _ = x - - -instance ToJSON Target where - toJSON (Target a b c d e f) = object [ - ("url" :: T.Text, toJSON a), - ("package" :: T.Text, maybeNamedURL b), - ("module" :: T.Text, maybeNamedURL c), - ("type" :: T.Text, toJSON d), - ("item" :: T.Text, toJSON e), - ("docs" :: T.Text, toJSON f) - ] - where - maybeNamedURL m = maybe emptyObject namedURL m - namedURL (name, url) = object [("name" :: T.Text, toJSON name), ("url" :: T.Text, toJSON url)] - - -instance FromJSON Target where - parseJSON = withObject "Target" $ \o -> - Target <$> o .: ("url" :: T.Text) - <*> o `namedUrl` ("package" :: T.Text) - <*> o `namedUrl` ("module" :: T.Text) - <*> o .: ("type" :: T.Text) - <*> o .: ("item" :: T.Text) - <*> o .: ("docs" :: T.Text) - where namedUrl o' n = do - mObj <- o' .: n - if null mObj then pure Nothing - else do - pkName <- mObj .: ("name" :: T.Text) - pkUrl <- mObj .: ("url" :: T.Text) - pure $ Just (pkName, pkUrl) +import Data.Aeson (FromJSON, ToJSON) +import Data.Monoid.Generic (GenericMonoid (..), + GenericSemigroup (..)) +import GHC.Generics (Generic) +import Shpadoinkle (NFData) +import Shpadoinkle.Website.Types.Hoogle.Target (Target) +import Shpadoinkle.Widgets.Form.Dropdown (Dropdown) +import Shpadoinkle.Widgets.Types (Input, Pick (One), + Search) + + +data Hoogle = Hoogle + { search :: Input Search + , targets :: Dropdown 'One Target + } + deriving stock (Eq, Ord, Show, Read, Generic) + deriving anyclass (FromJSON, ToJSON, NFData) + deriving Semigroup via GenericSemigroup Hoogle + deriving Monoid via GenericMonoid Hoogle diff --git a/website/Shpadoinkle/Website/Types/Hoogle/API.hs b/website/Shpadoinkle/Website/Types/Hoogle/API.hs new file mode 100644 index 00000000..0fef5cda --- /dev/null +++ b/website/Shpadoinkle/Website/Types/Hoogle/API.hs @@ -0,0 +1,18 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE TypeOperators #-} + + +module Shpadoinkle.Website.Types.Hoogle.API where + + +import Data.Text (Text) +import Servant.API (Get, JSON, QueryParam, + type (:>)) +import Shpadoinkle.Website.Types.Hoogle.Target (Target) + + +type HoogleAPI = QueryParam "mode" Text + :> QueryParam "hoogle" Text + :> QueryParam "start" Int + :> QueryParam "count" Int + :> Get '[JSON] [Target] diff --git a/website/Shpadoinkle/Website/Types/Hoogle/Target.hs b/website/Shpadoinkle/Website/Types/Hoogle/Target.hs new file mode 100644 index 00000000..66daf98d --- /dev/null +++ b/website/Shpadoinkle/Website/Types/Hoogle/Target.hs @@ -0,0 +1,63 @@ +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE OverloadedStrings #-} + + +module Shpadoinkle.Website.Types.Hoogle.Target where + + +import Data.Aeson (FromJSON (parseJSON), ToJSON (toJSON), + object, withObject, (.:)) +import Data.Aeson.Types (emptyObject) +import qualified Data.Text as T +import GHC.Generics (Generic) +import Shpadoinkle (NFData) + + +type URL = String + + +-- | A location of documentation. +data Target = Target + {targetURL :: URL -- ^ URL where this thing is located + ,targetPackage :: Maybe (String, URL) -- ^ Name and URL of the package it is in (Nothing if it is a package) + ,targetModule :: Maybe (String, URL) -- ^ Name and URL of the module it is in (Nothing if it is a package or module) + ,targetType :: String -- ^ One of package, module or empty string + ,targetItem :: String -- ^ HTML span of the item, using @\@ for the name and @\@ onwards for arguments + ,targetDocs :: String -- ^ HTML documentation to show, a sequence of block level elements + } deriving (Show,Read,Eq,Ord,Generic,NFData) + + +instance Semigroup Target where + x <> _ = x + + +instance ToJSON Target where + toJSON (Target a b c d e f) = object [ + ("url" :: T.Text, toJSON a), + ("package" :: T.Text, maybeNamedURL b), + ("module" :: T.Text, maybeNamedURL c), + ("type" :: T.Text, toJSON d), + ("item" :: T.Text, toJSON e), + ("docs" :: T.Text, toJSON f) + ] + where + maybeNamedURL m = maybe emptyObject namedURL m + namedURL (name, url) = object [("name" :: T.Text, toJSON name), ("url" :: T.Text, toJSON url)] + + +instance FromJSON Target where + parseJSON = withObject "Target" $ \o -> + Target <$> o .: ("url" :: T.Text) + <*> o `namedUrl` ("package" :: T.Text) + <*> o `namedUrl` ("module" :: T.Text) + <*> o .: ("type" :: T.Text) + <*> o .: ("item" :: T.Text) + <*> o .: ("docs" :: T.Text) + where namedUrl o' n = do + mObj <- o' .: n + if null mObj then pure Nothing + else do + pkName <- mObj .: ("name" :: T.Text) + pkUrl <- mObj .: ("url" :: T.Text) + pure $ Just (pkName, pkUrl) diff --git a/website/Shpadoinkle/Website/Types/Nav.hs b/website/Shpadoinkle/Website/Types/Nav.hs index 9a35e759..e2a8eeb6 100644 --- a/website/Shpadoinkle/Website/Types/Nav.hs +++ b/website/Shpadoinkle/Website/Types/Nav.hs @@ -1,4 +1,5 @@ -{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} module Shpadoinkle.Website.Types.Nav where diff --git a/website/Shpadoinkle/Website/Types/Routes.hs b/website/Shpadoinkle/Website/Types/Routes.hs new file mode 100644 index 00000000..5fb5b73b --- /dev/null +++ b/website/Shpadoinkle/Website/Types/Routes.hs @@ -0,0 +1,55 @@ +{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE TypeOperators #-} + + +module Shpadoinkle.Website.Types.Routes where + + +import Data.Aeson (FromJSON, + ToJSON) +import GHC.Generics (Generic) +import Servant.API (type (:<|>) (..), + type (:>)) +import Shpadoinkle (NFData) +import Shpadoinkle.Router (View, + mapUnions, + type (:>>)) +import Shpadoinkle.Website.Types.Frontend (Frontend) +import qualified Shpadoinkle.Website.Types.Routes.GettingStarted as GettingStarted +import qualified Shpadoinkle.Website.Types.Routes.Packages as Packages +import qualified Shpadoinkle.Website.Types.Routes.Tutorial as Tutorial + + +data Route + = RHome + | RConcepts + | RGettingStarted GettingStarted.Route + | RPackages Packages.Route + | RTutorial Tutorial.Route + | RSandbox + | RFourOhFour + deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) + + +type SPA m + = "home" :> View m Frontend + :<|> "concepts" :> View m Frontend + :<|> "getting-started" :> GettingStarted.SPA m Frontend + :<|> "packages" :> Packages.SPA m Frontend + :<|> "tutorial" :> Tutorial.SPA m Frontend + :<|> "404" :> View m Frontend + :<|> View m Frontend + + +routes :: SPA m :>> Route +routes + = RHome + :<|> RConcepts + :<|> mapUnions RGettingStarted GettingStarted.routes + :<|> mapUnions RPackages Packages.routes + :<|> mapUnions RTutorial Tutorial.routes + :<|> RFourOhFour + :<|> RHome diff --git a/website/Shpadoinkle/Website/Types/Routes/GettingStarted.hs b/website/Shpadoinkle/Website/Types/Routes/GettingStarted.hs new file mode 100644 index 00000000..d994e5f4 --- /dev/null +++ b/website/Shpadoinkle/Website/Types/Routes/GettingStarted.hs @@ -0,0 +1,35 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE TypeOperators #-} + + +module Shpadoinkle.Website.Types.Routes.GettingStarted where + + +import Data.Aeson (FromJSON, ToJSON) +import GHC.Generics (Generic) +import Servant.API (type (:<|>) ((:<|>)), type (:>)) +import Shpadoinkle (NFData) +import Shpadoinkle.Router (View, type (:>>)) + + + +data Route + = RGSIndex + | RGSAddingToYourProject + | RGSExtendAnExample + deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) + + +type SPA m a + = View m a + :<|> "adding-to-your-project" :> View m a + :<|> "extend-an-example" :> View m a + + +routes :: SPA m a :>> Route +routes + = RGSIndex + :<|> RGSAddingToYourProject + :<|> RGSExtendAnExample diff --git a/website/Shpadoinkle/Website/Types/Routes/Packages.hs b/website/Shpadoinkle/Website/Types/Routes/Packages.hs new file mode 100644 index 00000000..4908403c --- /dev/null +++ b/website/Shpadoinkle/Website/Types/Routes/Packages.hs @@ -0,0 +1,49 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE TypeOperators #-} + + +module Shpadoinkle.Website.Types.Routes.Packages where + + +import Data.Aeson (FromJSON, ToJSON) +import GHC.Generics (Generic) +import Servant.API (type (:<|>) ((:<|>)), type (:>)) +import Shpadoinkle (NFData) +import Shpadoinkle.Router (View, type (:>>)) + + +data Route + = RPIndex + | RPCore + | RPConsole + | RPBackends + | RPHtml + | RPLens + | RPRouter + | RPWidgets + deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) + + +type SPA m a + = View m a + :<|> "core" :> View m a + :<|> "console" :> View m a + :<|> "backends" :> View m a + :<|> "html" :> View m a + :<|> "lens" :> View m a + :<|> "router" :> View m a + :<|> "widgets" :> View m a + + +routes :: SPA m a :>> Route +routes + = RPIndex + :<|> RPCore + :<|> RPConsole + :<|> RPBackends + :<|> RPHtml + :<|> RPLens + :<|> RPRouter + :<|> RPWidgets diff --git a/website/Shpadoinkle/Website/Types/Routes/Tutorial.hs b/website/Shpadoinkle/Website/Types/Routes/Tutorial.hs new file mode 100644 index 00000000..5d7183a2 --- /dev/null +++ b/website/Shpadoinkle/Website/Types/Routes/Tutorial.hs @@ -0,0 +1,37 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE TypeOperators #-} + + +module Shpadoinkle.Website.Types.Routes.Tutorial where + + +import Data.Aeson (FromJSON, ToJSON) +import GHC.Generics (Generic) +import Servant.API (type (:<|>) ((:<|>)), type (:>)) +import Shpadoinkle (NFData) +import Shpadoinkle.Router (View, type (:>>)) + + +data Route + = RTIndex + | RTCalculator + | RTImmediateExecution + | RTComposing + deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) + + +type SPA m a + = View m a + :<|> "calculator" :> View m a + :<|> "immediate-execution" :> View m a + :<|> "composing" :> View m a + + +routes :: SPA m a :>> Route +routes + = RTIndex + :<|> RTCalculator + :<|> RTImmediateExecution + :<|> RTComposing diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index 5c7c06a5..787177b6 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -26,7 +26,10 @@ import Shpadoinkle.Website.Component.LiveExample import qualified Shpadoinkle.Website.Page.Documentation as Documentation import qualified Shpadoinkle.Website.Page.Home as Home import Shpadoinkle.Website.Partials.Template -import Shpadoinkle.Website.Types +import Shpadoinkle.Website.Types.Effects.Example +import Shpadoinkle.Website.Types.Frontend +import Shpadoinkle.Website.Types.Home +import Shpadoinkle.Website.Types.Routes default (Text) diff --git a/website/assets/Adelle-Bold.woff2 b/website/assets/Adelle-Bold.woff2 new file mode 100755 index 0000000000000000000000000000000000000000..27ef5ebb846e1f9a96aa55f24c9551c284e9bace GIT binary patch literal 49868 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 literal 0 HcmV?d00001 diff --git a/website/assets/Adelle-Italic.woff2 b/website/assets/Adelle-Italic.woff2 new file mode 100755 index 0000000000000000000000000000000000000000..54d04788d26c60c4507400e525ac5e92a53bd76a GIT binary patch literal 48568 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 KM76R|fB^vI&|9Ve literal 0 HcmV?d00001 diff --git a/website/assets/Adelle-Regular.woff2 b/website/assets/Adelle-Regular.woff2 new file mode 100755 index 0000000000000000000000000000000000000000..49d01369b252f33438f99ba6d55833833c61edf8 GIT binary patch literal 50144 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 g-SbN~1`Ehe&0MA=wP{1.sectionbody>.paragraph>p{padding:1rem 0}.listingblock{padding:2rem 0;overflow-x:scroll}.listingblock>.content>.highlight>code{border:2px solid #e2e0d5;background-color:transparent;padding:15px 10px}div.admonitionblock{position:relative;margin:3rem 0}div.admonitionblock>table>tbody>tr>td.icon{border-radius:18px;color:#fff;display:flex;height:30px;justify-content:flex-start;left:5px;padding:0 1.5rem;position:absolute;top:-15px;width:135px}div.admonitionblock>table>tbody>tr>td.content{padding:20px 10px 15px 1rem}div.admonitionblock.caution{border:2px dashed #ac2a34}div.admonitionblock.caution>table>tbody>tr>td.icon{background:#ac2a34}div.admonitionblock.caution>table>tbody>tr>td.content{color:#ac2a34}div.admonitionblock.note{border:2px dashed #0f406b}div.admonitionblock.note>table>tbody>tr>td.icon{background:#0f406b}div.admonitionblock.note>table>tbody>tr>td.content{color:#0f406b}div.admonitionblock.warning{border:2px dashed #e37743}div.admonitionblock.warning>table>tbody>tr>td.icon{background:#e37743}div.admonitionblock.warning>table>tbody>tr>td.content{color:#e37743}h1,h2,h3,h4,h5{padding:1rem 0}h1{font-size:1.875rem}h2{font-size:1.375rem}h3{font-size:1.5rem}@media (min-width:768px){h1{font-size:3.75rem}h2{font-size:2.625rem}h3{font-size:1.325rem}}.split-button:active{transform:translate(2px,2px)}.started-header{background-image:url(/assets/get_started_header_bg.d41c50d2.svg);height:96px;background-position:center;background-repeat:no-repeat;background-size:cover;margin-bottom:3rem;position:relative;z-index:50}@media (min-width:768px){.started-header{margin-bottom:-3.5rem}}@media (min-width:768px){.started-header{background-image:url(/assets/get_started_header_bg.05d59513.svg);height:156px}}body{--tw-bg-opacity:1;background-color:rgba(237,225,113,var(--tw-bg-opacity));--tw-border-opacity:1;border-color:rgba(217,206,95,var(--tw-border-opacity));border-style:solid;font-family:Adelle,ui-serif}.nav{width:100%}@media (min-width:640px){.nav{max-width:640px}}@media (min-width:768px){.nav{max-width:768px}}@media (min-width:1024px){.nav{max-width:1024px}}@media (min-width:1280px){.nav{max-width:1280px}}@media (min-width:1536px){.nav{max-width:1536px}}.nav{display:flex;justify-content:space-between;margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem;padding-top:2rem;position:relative;width:100%}.nav__logo{width:75%}@media (min-width:768px){.nav__logo{width:18rem}}.nav__menu-icon{display:block;padding-right:.75rem;width:2.5rem}@media (min-width:768px){.nav__menu-icon{display:none}}.nav__links{display:none;position:absolute;right:0;text-transform:uppercase;gap:1.25rem}@media (min-width:768px){.nav__links{display:flex}}.nav__link{display:flex;flex-direction:column;justify-content:center;position:relative;text-align:center;width:7rem}.nav__link-underline{position:absolute;top:1.25rem}.nav__link__underline-docs{padding-top:.5rem;position:absolute;top:1.25rem}.nav-mobile{padding-top:3rem}@media (min-width:640px){.nav-mobile{display:none}}.nav-mobile__wrapper>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.nav-mobile__wrapper{padding-top:.5rem;padding-bottom:1rem}.nav-mobile__link{border-color:transparent;display:block;font-size:1.875rem;line-height:2.25rem;padding-top:.5rem;padding-bottom:.5rem;padding-right:1rem;padding-left:1rem;--tw-text-opacity:1;color:rgba(18,18,18,var(--tw-text-opacity))}.nav-mobile__links>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.nav-mobile__links{padding-top:.5rem;padding-bottom:1rem}.nav-mobile__heading{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity));display:flex;flex-direction:column;justify-content:flex-end;height:5rem;font-size:1.5rem;line-height:2rem;padding-bottom:.75rem;padding-left:1rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:100%}.nav-mobile__heading__title{display:flex;justify-content:space-between;padding-right:1rem}.nav-mobile__heading__close{display:block;padding-right:.75rem}@media (min-width:768px){.nav-mobile__heading__close{display:none}}.nav-mobile__links__level1{padding-left:3rem}.nav-mobile__links__level2{padding-left:1rem}.hero-bg{display:none;margin-top:-21rem;width:100%}@media (min-width:768px){.hero-bg{display:block}}.hero-bg-mobile{display:block;margin-top:-16rem;width:100%}@media (min-width:768px){.hero-bg-mobile{display:none}}.hero{width:100%}@media (min-width:640px){.hero{max-width:640px}}@media (min-width:768px){.hero{max-width:768px}}@media (min-width:1024px){.hero{max-width:1024px}}@media (min-width:1280px){.hero{max-width:1280px}}@media (min-width:1536px){.hero{max-width:1536px}}.hero{margin-left:auto;margin-right:auto;margin-top:3rem;padding-left:1rem;padding-right:1rem;padding-bottom:6rem}@media (min-width:768px){.hero{margin-top:6rem}}.hero-title-wrapper{justify-content:space-between;position:relative}.hero-title{font-size:1.875rem;line-height:2.25rem}@media (min-width:768px){.hero-title{font-size:2.25rem;line-height:2.5rem;line-height:1.625}}@media (min-width:1024px){.hero-title{font-size:3rem;line-height:1}}.hero-button{display:flex;margin-bottom:3rem;padding-top:1.25rem}@media (min-width:768px){.hero-button{padding-top:2.5rem}}.hero-button__content{--tw-border-opacity:1;border-color:rgba(210,199,91,var(--tw-border-opacity));width:13rem}.hero-button__split{--tw-bg-opacity:1;background-color:rgba(18,18,18,var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.feature-button{display:none;justify-content:center;margin-bottom:3rem}@media (min-width:768px){.feature-button{display:flex;padding-top:1.5rem}}.feature-button__content{--tw-border-opacity:1;border-color:rgba(196,91,40,var(--tw-border-opacity));width:24rem}.feature-button__split{--tw-bg-opacity:1;background-color:rgba(241,226,85,var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.split-button{background-color:transparent;border-radius:.75rem;border-style:solid;border-width:2px;display:flex;height:3rem;position:relative}.split-button__button{display:flex;position:absolute;left:-.5rem;top:-.75rem}.split-button__split1{border-radius:.75rem;display:inline-flex;align-items:center;font-weight:500;height:3rem;margin-right:.5rem;padding-top:.75rem;padding-bottom:.75rem;padding-left:1.5rem;padding-right:1.5rem}.split-button__split2{border-radius:.75rem;display:inline-flex;align-items:center;font-weight:500;height:3rem;font-size:1.125rem;line-height:1.75rem;padding-top:.75rem;padding-bottom:.75rem;padding-left:.75rem;padding-right:.75rem;--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.transition{width:100%}.feature__section{width:full;margin-top:-.5rem;background-color:#e37743}.feature__wrapper{width:100%}@media (min-width:640px){.feature__wrapper{max-width:640px}}@media (min-width:768px){.feature__wrapper{max-width:768px}}@media (min-width:1024px){.feature__wrapper{max-width:1024px}}@media (min-width:1280px){.feature__wrapper{max-width:1280px}}@media (min-width:1536px){.feature__wrapper{max-width:1536px}}.feature__wrapper{margin-left:auto;margin-right:auto;padding-left:0;padding-right:0;padding-top:3rem}@media (min-width:768px){.feature__wrapper{padding-left:1rem;padding-right:1rem}}.feature__title{font-size:1.875rem;line-height:2.25rem;margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem;width:100%}@media (min-width:768px){.feature__title{font-size:2.25rem;line-height:2.5rem;padding-left:0;padding-right:0;text-align:center;width:60%}}.feature__cards{display:flex;flex-wrap:wrap;justify-content:center;padding-top:3rem}.feature__card{display:flex;justify-content:center;padding-bottom:1.5rem;width:100%}@media (min-width:768px){.feature__card{margin-bottom:3.5rem;width:50%}}@media (min-width:1024px){.feature__card{width:33.333333%}}.feature__card__content{--tw-bg-opacity:1;background-color:rgba(208,79,34,var(--tw-bg-opacity));border-radius:0;padding-top:1rem;padding-bottom:1rem;padding-left:2rem;padding-right:2rem;width:100%}@media (min-width:768px){.feature__card__content{border-radius:1.5rem;height:20rem;width:20rem}}.feature__card__heading{display:inline-flex;align-items:center;padding-bottom:1.25rem}.feature__card__title{font-size:1.875rem;line-height:2.25rem;margin-left:1rem;margin-bottom:-.5rem}.feature__card__description{font-size:1.125rem;line-height:1.75rem}.components__section{--tw-bg-opacity:1;background-color:rgba(58,52,83,var(--tw-bg-opacity));flex-direction:column;align-items:center;margin-top:-.5rem;padding-top:3rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:100%}.component{width:100%}@media (min-width:640px){.component{max-width:640px}}@media (min-width:768px){.component{max-width:768px}}@media (min-width:1024px){.component{max-width:1024px}}@media (min-width:1280px){.component{max-width:1280px}}@media (min-width:1536px){.component{max-width:1536px}}.component{display:flex;flex-wrap:wrap;align-items:center;margin-left:auto;margin-right:auto;margin-bottom:3rem;padding-left:1rem;padding-right:1rem}.component__info{padding-right:0;padding-bottom:1rem;width:100%}@media (min-width:1024px){.component__info{padding-bottom:0;padding-right:2rem;width:33.333333%}}.component__info__title{padding-bottom:.75rem}.component__example{width:100%}@media (min-width:1024px){.component__example{width:66.666667%}}.component__example__code{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity));border-radius:.75rem;height:12rem}footer{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity));margin-top:-.5rem}.footer__wrapper{width:100%}@media (min-width:640px){.footer__wrapper{max-width:640px}}@media (min-width:768px){.footer__wrapper{max-width:768px}}@media (min-width:1024px){.footer__wrapper{max-width:1024px}}@media (min-width:1280px){.footer__wrapper{max-width:1280px}}@media (min-width:1536px){.footer__wrapper{max-width:1536px}}.footer__wrapper{margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem}.footer__nav{display:flex;flex-direction:column;justify-content:space-between;line-height:2rem;padding-top:3rem;width:100%;gap:2rem}@media (min-width:768px){.footer__nav{flex-direction:row;width:40%;gap:0}}.footer__nav__category{--tw-text-opacity:1;color:rgba(91,56,233,var(--tw-text-opacity))}.footer__nav__links{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));text-transform:uppercase}.footer_copyright{font-size:.875rem;line-height:1.25rem;padding-top:3rem;padding-bottom:3rem;--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.documentation{--tw-bg-opacity:1;background-color:rgba(250,247,230,var(--tw-bg-opacity));--tw-border-opacity:1;border-color:rgba(217,206,95,var(--tw-border-opacity));border-style:solid;line-height:2.25rem}.mobile-mode{background-color:#ede171!important}.top-border{border-top-width:4px}.sidebar{display:flex}.side-nav>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.side-nav{--tw-bg-opacity:1;background-color:rgba(242,239,217,var(--tw-bg-opacity));display:none;flex-direction:column;padding-left:3.5rem;padding-top:5rem;position:relative;width:21rem;z-index:0}@media (min-width:768px){.side-nav{display:flex}}.side-nav__expand>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.side-nav__expand__button{border-radius:.75rem;cursor:pointer;display:flex;align-items:center;height:3.5rem;font-size:1.125rem;line-height:1.75rem;padding-left:1rem;padding-right:1rem}.side-nav__expand__button:hover{--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.side-nav__expand__button{width:100%}.side-nav__link:hover{--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.side-nav__expand__icon{margin-left:auto}.side-nav__expand__list{padding-left:2rem}.side-nav__expand__list2{padding-left:1.5rem}.docs{width:100%}@media (min-width:640px){.docs{max-width:640px}}@media (min-width:768px){.docs{max-width:768px}}@media (min-width:1024px){.docs{max-width:1024px}}@media (min-width:1280px){.docs{max-width:1280px}}@media (min-width:1536px){.docs{max-width:1536px}}.docs{margin-left:auto;margin-right:auto;margin-left:0;margin-bottom:6rem;padding-left:1rem;padding-right:1rem;padding-top:0}@media (min-width:768px){.docs{margin-left:2rem;padding-top:8rem}}.separator{--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-style:solid;border-top-width:2px;margin-top:2.5rem;margin-bottom:2.5rem}.code-block{--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-style:solid;border-width:2px;margin-top:2.5rem;margin-bottom:2.5rem;width:100%}.code-block__content{margin:1.5rem}.bullet-list{margin-top:.5rem;margin-bottom:.5rem;margin-left:1rem}.bullet-list__callout>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.bullet-list__callout{display:flex}code.doc{overflow-x:auto;background-color:transparent;--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-width:2px;display:block;margin-top:1rem;margin-bottom:1rem;padding:1rem}.admon{border-style:dashed;border-width:2px;margin-top:2.5rem;margin-bottom:2.5rem;width:100%}.admon-warning{--tw-border-opacity:1;border-color:rgba(220,38,38,var(--tw-border-opacity))}.admon__button{border-radius:9999px;display:flex;align-items:center;justify-content:center;height:2rem;margin-left:1rem;margin-top:-1rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:8rem;gap:.5rem}.admon__button-warning{--tw-bg-opacity:1;background-color:rgba(220,38,38,var(--tw-bg-opacity))}.admon__button-note{--tw-bg-opacity:1;background-color:rgba(30,58,138,var(--tw-bg-opacity))}.admon-note{--tw-border-opacity:1;border-color:rgba(30,58,138,var(--tw-border-opacity))}.secondary-nav{margin-bottom:1.5rem}@media (min-width:768px){.secondary-nav{display:none}}.secondary-nav__content{--tw-border-opacity:1;border-color:rgba(210,199,91,var(--tw-border-opacity));width:10rem}.secondary-nav__split{--tw-bg-opacity:1;background-color:rgba(18,18,18,var(--tw-bg-opacity));border-radius:.75rem;display:inline-flex;align-items:center;justify-content:center;font-weight:500;height:3rem;padding-left:.5rem;padding-right:.5rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:9rem}.secondary-nav__split>p{margin-right:6px}@media (min-width:768px){.md\:flex-row{flex-direction:row}.md\:ml-8{margin-left:2rem}.md\:pt-32{padding-top:8rem}.md\:w-2\/5{width:40%}.md\:gap-0{gap:0}} \ No newline at end of file +@font-face{font-family:Adelle;src:url(/assets/Adelle-Regular.woff2) format('woff2');font-weight:400;font-style:normal}@font-face{font-family:AdelleBold;src:url(/assets/Adelle-Bold.woff2) format('woff2');font-weight:400;font-style:normal}/*! modern-normalize v1.0.0 | MIT License | https://github.com/sindresorhus/modern-normalize */*,::after,::before{box-sizing:border-box}:root{-moz-tab-size:4;-o-tab-size:4;tab-size:4}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}body{font-family:system-ui,-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji'}hr{height:0;color:inherit}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Consolas,'Liberation Mono',Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],button{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}button{background-color:transparent;background-image:none}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}fieldset{margin:0;padding:0}ol,ul{list-style:none;margin:0;padding:0}html{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";line-height:1.5}body{font-family:inherit;line-height:inherit}*,::after,::before{box-sizing:border-box;border-width:0;border-style:solid;border-color:currentColor}hr{border-top-width:1px}img{border-style:solid}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#a1a1aa}input:-ms-input-placeholder,textarea:-ms-input-placeholder{opacity:1;color:#a1a1aa}input::placeholder,textarea::placeholder{opacity:1;color:#a1a1aa}button{cursor:pointer}table{border-collapse:collapse}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,optgroup,select,textarea{padding:0;line-height:inherit;color:inherit}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.rounded{border-radius:.25rem}.flex{display:flex}.table{display:table}.table-row{display:table-row}.grid{display:grid}.hidden{display:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.flex-1{flex:1 1 0%}.font-bold{font-family:AdelleBold,Verdana}.font-bold{font-weight:700}.h-32{height:8rem}.text-xs{font-size:.75rem;line-height:1rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.leading-8{line-height:2rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-0{margin-left:0}.mr-4{margin-right:1rem}.mb-4{margin-bottom:1rem}.mb-24{margin-bottom:6rem}.max-w-sm{max-width:24rem}.min-h-screen{min-height:100vh}.opacity-0{opacity:0}.opacity-100{opacity:1}.p-2{padding:.5rem}.p-6{padding:1.5rem}.p-10{padding:2.5rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.px-10{padding-left:2.5rem;padding-right:2.5rem}.py-16{padding-top:4rem;padding-bottom:4rem}.pt-0{padding-top:0}.pt-12{padding-top:3rem}.fixed{position:fixed}.sticky{position:sticky}.top-0{top:0}.right-0{right:0}.bottom-0{bottom:0}*{--tw-shadow:0 0 #0000}*{--tw-ring-inset:var(--tw-empty, );/*!*//*!*/--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59, 130, 246, 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000}.text-white{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.w-full{width:100%}.gap-8{gap:2rem}.gap-10{gap:2.5rem}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.transform{--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;transform:translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-90{--tw-scale-x:.9;--tw-scale-y:.9}.scale-100{--tw-scale-x:1;--tw-scale-y:1}.transition{transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:150ms}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}@-webkit-keyframes spin{to{transform:rotate(360deg)}}@keyframes spin{to{transform:rotate(360deg)}}@-webkit-keyframes ping{100%,75%{transform:scale(2);opacity:0}}@keyframes ping{100%,75%{transform:scale(2);opacity:0}}@-webkit-keyframes pulse{50%{opacity:.5}}@keyframes pulse{50%{opacity:.5}}@-webkit-keyframes bounce{0%,100%{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@keyframes bounce{0%,100%{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}#preamble>.sectionbody>.paragraph>p{padding:1rem 0}.listingblock{padding:2rem 0;overflow-x:scroll}.listingblock>.content>.highlight>code{border:2px solid #e2e0d5;background-color:transparent;padding:15px 10px}div.admonitionblock{position:relative;margin:3rem 0}div.admonitionblock>table>tbody>tr>td.icon{border-radius:18px;color:#fff;display:flex;height:30px;justify-content:flex-start;left:5px;padding:0 1.5rem;position:absolute;top:-15px;width:135px}div.admonitionblock>table>tbody>tr>td.content{padding:20px 10px 15px 1rem}div.admonitionblock.caution{border:2px dashed #ac2a34}div.admonitionblock.caution>table>tbody>tr>td.icon{background:#ac2a34}div.admonitionblock.caution>table>tbody>tr>td.content{color:#ac2a34}div.admonitionblock.note{border:2px dashed #0f406b}div.admonitionblock.note>table>tbody>tr>td.icon{background:#0f406b}div.admonitionblock.note>table>tbody>tr>td.content{color:#0f406b}div.admonitionblock.warning{border:2px dashed #e37743}div.admonitionblock.warning>table>tbody>tr>td.icon{background:#e37743}div.admonitionblock.warning>table>tbody>tr>td.content{color:#e37743}h1,h2,h3,h4,h5{padding:1rem 0}h1{font-size:1.875rem}h2{font-size:1.375rem}h3{font-size:1.5rem}@media (min-width:768px){h1{font-size:3.75rem}h2{font-size:2.625rem}h3{font-size:1.325rem}}.split-button:active{transform:translate(2px,2px)}.started-header{background-image:url(/assets/get_started_header_bg.d41c50d2.svg);height:96px;background-position:center;background-repeat:no-repeat;background-size:cover;margin-bottom:3rem;position:relative;z-index:50}@media (min-width:768px){.started-header{margin-bottom:-3.5rem}}@media (min-width:768px){.started-header{background-image:url(/assets/get_started_header_bg.05d59513.svg);height:156px}}body{--tw-bg-opacity:1;background-color:rgba(237,225,113,var(--tw-bg-opacity));--tw-border-opacity:1;border-color:rgba(217,206,95,var(--tw-border-opacity));border-style:solid;font-family:Adelle,ui-serif}.nav{width:100%}@media (min-width:640px){.nav{max-width:640px}}@media (min-width:768px){.nav{max-width:768px}}@media (min-width:1024px){.nav{max-width:1024px}}@media (min-width:1280px){.nav{max-width:1280px}}@media (min-width:1536px){.nav{max-width:1536px}}.nav{display:flex;justify-content:space-between;margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem;padding-top:2rem;position:relative;width:100%}.nav__logo{width:75%}@media (min-width:768px){.nav__logo{width:18rem}}.nav__menu-icon{display:block;padding-right:.75rem;width:2.5rem}@media (min-width:768px){.nav__menu-icon{display:none}}.nav__links{display:none;position:absolute;right:0;text-transform:uppercase;gap:1.25rem}@media (min-width:768px){.nav__links{display:flex}}.nav__link{display:flex;flex-direction:column;justify-content:center;position:relative;text-align:center;width:7rem}.nav__link-underline{position:absolute;top:1.25rem}.nav__link__underline-docs{padding-top:.5rem;position:absolute;top:1.25rem}.nav-mobile{padding-top:3rem}@media (min-width:640px){.nav-mobile{display:none}}.nav-mobile__wrapper>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.nav-mobile__wrapper{padding-top:.5rem;padding-bottom:1rem}.nav-mobile__link{border-color:transparent;display:block;font-size:1.875rem;line-height:2.25rem;padding-top:.5rem;padding-bottom:.5rem;padding-right:1rem;padding-left:1rem;--tw-text-opacity:1;color:rgba(18,18,18,var(--tw-text-opacity))}.nav-mobile__links>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.nav-mobile__links{padding-top:.5rem;padding-bottom:1rem}.nav-mobile__heading{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity));display:flex;flex-direction:column;justify-content:flex-end;height:5rem;font-size:1.5rem;line-height:2rem;padding-bottom:.75rem;padding-left:1rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:100%}.nav-mobile__heading__title{display:flex;justify-content:space-between;padding-right:1rem}.nav-mobile__heading__close{display:block;padding-right:.75rem}@media (min-width:768px){.nav-mobile__heading__close{display:none}}.nav-mobile__links__level1{padding-left:3rem}.nav-mobile__links__level2{padding-left:1rem}.hero-bg{display:none;margin-top:-21rem;width:100%}@media (min-width:768px){.hero-bg{display:block}}.hero-bg-mobile{display:block;margin-top:-16rem;width:100%}@media (min-width:768px){.hero-bg-mobile{display:none}}.hero{width:100%}@media (min-width:640px){.hero{max-width:640px}}@media (min-width:768px){.hero{max-width:768px}}@media (min-width:1024px){.hero{max-width:1024px}}@media (min-width:1280px){.hero{max-width:1280px}}@media (min-width:1536px){.hero{max-width:1536px}}.hero{margin-left:auto;margin-right:auto;margin-top:3rem;padding-left:1rem;padding-right:1rem;padding-bottom:6rem}@media (min-width:768px){.hero{margin-top:6rem}}.hero-title-wrapper{justify-content:space-between;position:relative}.hero-title{font-size:1.875rem;line-height:2.25rem}@media (min-width:768px){.hero-title{font-size:2.25rem;line-height:2.5rem;line-height:1.625}}@media (min-width:1024px){.hero-title{font-size:3rem;line-height:1}}.hero-button{display:flex;margin-bottom:3rem;padding-top:1.25rem}@media (min-width:768px){.hero-button{padding-top:2.5rem}}.hero-button__content{--tw-border-opacity:1;border-color:rgba(210,199,91,var(--tw-border-opacity));width:13rem}.hero-button__split{--tw-bg-opacity:1;background-color:rgba(18,18,18,var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.feature-button{display:none;justify-content:center;margin-bottom:3rem}@media (min-width:768px){.feature-button{display:flex;padding-top:1.5rem}}.feature-button__content{--tw-border-opacity:1;border-color:rgba(196,91,40,var(--tw-border-opacity));width:24rem}.feature-button__split{--tw-bg-opacity:1;background-color:rgba(241,226,85,var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.split-button{background-color:transparent;border-radius:.75rem;border-style:solid;border-width:2px;display:flex;height:3rem;position:relative}.split-button__button{display:flex;position:absolute;left:-.5rem;top:-.75rem}.split-button__split1{border-radius:.75rem;display:inline-flex;align-items:center;font-weight:500;height:3rem;margin-right:.5rem;padding-top:.75rem;padding-bottom:.75rem;padding-left:1.5rem;padding-right:1.5rem}.split-button__split2{border-radius:.75rem;display:inline-flex;align-items:center;font-weight:500;height:3rem;font-size:1.125rem;line-height:1.75rem;padding-top:.75rem;padding-bottom:.75rem;padding-left:.75rem;padding-right:.75rem;--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.transition{width:100%}.feature__section{width:full;margin-top:-.5rem;background-color:#e37743}.feature__wrapper{width:100%}@media (min-width:640px){.feature__wrapper{max-width:640px}}@media (min-width:768px){.feature__wrapper{max-width:768px}}@media (min-width:1024px){.feature__wrapper{max-width:1024px}}@media (min-width:1280px){.feature__wrapper{max-width:1280px}}@media (min-width:1536px){.feature__wrapper{max-width:1536px}}.feature__wrapper{margin-left:auto;margin-right:auto;padding-left:0;padding-right:0;padding-top:3rem}@media (min-width:768px){.feature__wrapper{padding-left:1rem;padding-right:1rem}}.feature__title{font-size:1.875rem;line-height:2.25rem;margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem;width:100%}@media (min-width:768px){.feature__title{font-size:2.25rem;line-height:2.5rem;padding-left:0;padding-right:0;text-align:center;width:60%}}.feature__cards{display:flex;flex-wrap:wrap;justify-content:center;padding-top:3rem}.feature__card{display:flex;justify-content:center;padding-bottom:1.5rem;width:100%}@media (min-width:768px){.feature__card{margin-bottom:3.5rem;width:50%}}@media (min-width:1024px){.feature__card{width:33.333333%}}.feature__card__content{--tw-bg-opacity:1;background-color:rgba(208,79,34,var(--tw-bg-opacity));border-radius:0;padding-top:1rem;padding-bottom:1rem;padding-left:2rem;padding-right:2rem;width:100%}@media (min-width:768px){.feature__card__content{border-radius:1.5rem;height:20rem;width:20rem}}.feature__card__heading{display:inline-flex;align-items:center;padding-bottom:1.25rem}.feature__card__title{font-size:1.875rem;line-height:2.25rem;margin-left:1rem;margin-bottom:-.5rem}.feature__card__description{font-size:1.125rem;line-height:1.75rem}.components__section{--tw-bg-opacity:1;background-color:rgba(58,52,83,var(--tw-bg-opacity));flex-direction:column;align-items:center;margin-top:-.5rem;padding-top:3rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:100%}.component{width:100%}@media (min-width:640px){.component{max-width:640px}}@media (min-width:768px){.component{max-width:768px}}@media (min-width:1024px){.component{max-width:1024px}}@media (min-width:1280px){.component{max-width:1280px}}@media (min-width:1536px){.component{max-width:1536px}}.component{display:flex;flex-wrap:wrap;align-items:center;margin-left:auto;margin-right:auto;margin-bottom:3rem;padding-left:1rem;padding-right:1rem}.component__info{padding-right:0;padding-bottom:1rem;width:100%}@media (min-width:1024px){.component__info{padding-bottom:0;padding-right:2rem;width:33.333333%}}.component__info__title{padding-bottom:.75rem}.component__example{width:100%}@media (min-width:1024px){.component__example{width:66.666667%}}.component__example__code{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity));border-radius:.75rem;height:12rem}footer{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity));margin-top:-.5rem}.footer__wrapper{width:100%}@media (min-width:640px){.footer__wrapper{max-width:640px}}@media (min-width:768px){.footer__wrapper{max-width:768px}}@media (min-width:1024px){.footer__wrapper{max-width:1024px}}@media (min-width:1280px){.footer__wrapper{max-width:1280px}}@media (min-width:1536px){.footer__wrapper{max-width:1536px}}.footer__wrapper{margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem}.footer__nav{display:flex;flex-direction:column;justify-content:space-between;line-height:2rem;padding-top:3rem;width:100%;gap:2rem}@media (min-width:768px){.footer__nav{flex-direction:row;width:40%;gap:0}}.footer__nav__category{--tw-text-opacity:1;color:rgba(91,56,233,var(--tw-text-opacity))}.footer__nav__links{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));text-transform:uppercase}.footer_copyright{font-size:.875rem;line-height:1.25rem;padding-top:3rem;padding-bottom:3rem;--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.documentation{--tw-bg-opacity:1;background-color:rgba(250,247,230,var(--tw-bg-opacity));--tw-border-opacity:1;border-color:rgba(217,206,95,var(--tw-border-opacity));border-style:solid;line-height:2.25rem}.mobile-mode{background-color:#ede171!important}.top-border{border-top-width:4px}.sidebar{display:flex}.side-nav>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.side-nav{--tw-bg-opacity:1;background-color:rgba(242,239,217,var(--tw-bg-opacity));display:none;flex-direction:column;padding-left:3.5rem;padding-top:5rem;position:relative;width:21rem;z-index:0}@media (min-width:768px){.side-nav{display:flex}}.side-nav__expand>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.side-nav__expand__button{border-radius:.75rem;cursor:pointer;display:flex;align-items:center;height:3.5rem;font-size:1.125rem;line-height:1.75rem;padding-left:1rem;padding-right:1rem}.side-nav__expand__button:hover{--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.side-nav__expand__button{width:100%}.side-nav__link:hover{--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.side-nav__expand__icon{margin-left:auto}.side-nav__expand__list{padding-left:2rem}.side-nav__expand__list2{padding-left:1.5rem}.docs{width:100%}@media (min-width:640px){.docs{max-width:640px}}@media (min-width:768px){.docs{max-width:768px}}@media (min-width:1024px){.docs{max-width:1024px}}@media (min-width:1280px){.docs{max-width:1280px}}@media (min-width:1536px){.docs{max-width:1536px}}.docs{margin-left:auto;margin-right:auto;margin-left:0;margin-bottom:6rem;padding-left:1rem;padding-right:1rem;padding-top:0}@media (min-width:768px){.docs{margin-left:2rem;padding-top:8rem}}.separator{--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-style:solid;border-top-width:2px;margin-top:2.5rem;margin-bottom:2.5rem}.code-block{--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-style:solid;border-width:2px;margin-top:2.5rem;margin-bottom:2.5rem;width:100%}.code-block__content{margin:1.5rem}.bullet-list{margin-top:.5rem;margin-bottom:.5rem;margin-left:1rem}.bullet-list__callout>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.bullet-list__callout{display:flex}code.doc{overflow-x:auto;background-color:transparent;--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-width:2px;display:block;margin-top:1rem;margin-bottom:1rem;padding:1rem}.admon{border-style:dashed;border-width:2px;margin-top:2.5rem;margin-bottom:2.5rem;width:100%}.admon-warning{--tw-border-opacity:1;border-color:rgba(220,38,38,var(--tw-border-opacity))}.admon__button{border-radius:9999px;display:flex;align-items:center;justify-content:center;height:2rem;margin-left:1rem;margin-top:-1rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:8rem;gap:.5rem}.admon__button-warning{--tw-bg-opacity:1;background-color:rgba(220,38,38,var(--tw-bg-opacity))}.admon__button-note{--tw-bg-opacity:1;background-color:rgba(30,58,138,var(--tw-bg-opacity))}.admon-note{--tw-border-opacity:1;border-color:rgba(30,58,138,var(--tw-border-opacity))}.secondary-nav{margin-bottom:1.5rem}@media (min-width:768px){.secondary-nav{display:none}}.secondary-nav__content{--tw-border-opacity:1;border-color:rgba(210,199,91,var(--tw-border-opacity));width:10rem}.secondary-nav__split{--tw-bg-opacity:1;background-color:rgba(18,18,18,var(--tw-bg-opacity));border-radius:.75rem;display:inline-flex;align-items:center;justify-content:center;font-weight:500;height:3rem;padding-left:.5rem;padding-right:.5rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:9rem}.secondary-nav__split>p{margin-right:6px}@media (min-width:768px){.md\:flex-row{flex-direction:row}.md\:ml-8{margin-left:2rem}.md\:pt-32{padding-top:8rem}.md\:w-2\/5{width:40%}.md\:gap-0{gap:0}} diff --git a/website/package.yaml b/website/package.yaml index ad7c998e..5836cac5 100644 --- a/website/package.yaml +++ b/website/package.yaml @@ -77,13 +77,21 @@ executables: - Shpadoinkle.Website.Partials.Footer - Shpadoinkle.Website.Page.Home - Shpadoinkle.Website.Page.Documentation - - Shpadoinkle.Website.Routes - - Shpadoinkle.Website.Types - Shpadoinkle.Website.Types.Nav - Shpadoinkle.Website.Types.Home - Shpadoinkle.Website.Types.Hoogle + - Shpadoinkle.Website.Types.Hoogle.API + - Shpadoinkle.Website.Types.Hoogle.Target - Shpadoinkle.Website.Types.Example + - Shpadoinkle.Website.Types.Frontend - Shpadoinkle.Website.Types.ExampleState + - Shpadoinkle.Website.Types.Effects.Example + - Shpadoinkle.Website.Types.Effects.Hooglable + - Shpadoinkle.Website.Types.Effects.Swan + - Shpadoinkle.Website.Types.Routes.GettingStarted + - Shpadoinkle.Website.Types.Routes.Tutorial + - Shpadoinkle.Website.Types.Routes.Packages + - Shpadoinkle.Website.Types.Routes - Shpadoinkle.Website.Style - Shpadoinkle.Website.View source-dirs: . -- GitLab From e48cd939f031e92038cf0433b32b7455f64af0d6 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 12 Apr 2021 17:40:35 -0600 Subject: [PATCH 029/116] routing --- website/Shpadoinkle/Website/Page/Home.hs | 42 ++++++----- website/Shpadoinkle/Website/Partials/Nav.hs | 7 +- .../Shpadoinkle/Website/Partials/Template.hs | 3 +- website/Shpadoinkle/Website/Types/Nav.hs | 20 +++++- website/Shpadoinkle/Website/Types/Routes.hs | 70 +++++++++++++++++-- website/Shpadoinkle/Website/View.hs | 2 +- 6 files changed, 113 insertions(+), 31 deletions(-) diff --git a/website/Shpadoinkle/Website/Page/Home.hs b/website/Shpadoinkle/Website/Page/Home.hs index fb52a722..f9ecd8a0 100644 --- a/website/Shpadoinkle/Website/Page/Home.hs +++ b/website/Shpadoinkle/Website/Page/Home.hs @@ -1,19 +1,27 @@ -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} module Shpadoinkle.Website.Page.Home where -import Data.Text (Text) -import Prelude hiding (div, span) -import Shpadoinkle.Html as H -import Shpadoinkle.Html.TH.AssetLink -import Shpadoinkle.Website.Style as Style -import Shpadoinkle.Website.Types.Home (Home) +import Data.Text (Text) +import Prelude hiding (div, + span) +import Shpadoinkle.Html as H +import Shpadoinkle.Html.TH.AssetLink (assetLink) +import Shpadoinkle.Router (MonadJSM, + navigate) +import Shpadoinkle.Website.Style as Style +import Shpadoinkle.Website.Types.Home (Home) +import Shpadoinkle.Website.Types.Routes (Route (RGettingStarted), + SPA) +import Shpadoinkle.Website.Types.Routes.GettingStarted (Route (RGSIndex)) -view :: Home -> [Html m Home] +view :: MonadJSM m => Home -> [Html m Home] view _ = [hero', featureSection, componentsSection] @@ -25,7 +33,7 @@ hero' :: Html m a hero' = div_ [ div [ class' hero ] [ div [ class' hero_title_wrapper ] $ heroTitle <$> - [ "A new Haskell UI" + [ "A new Functional UI" , "programming" , "paradigm" ] @@ -67,7 +75,7 @@ featureCard fc = div [ class' feature__card ] ] -featureSection :: Html m a +featureSection :: forall m a. MonadJSM m => Html m a featureSection = div [ class' feature__section ] [ div [ class' feature__wrapper ] [ p [ class' feature__title ] @@ -76,7 +84,7 @@ featureSection = div [ class' feature__section ] [ featureCard $ FeatureCard { card'icon = $(assetLink "/assets/fast_icon.svg") , card'title = "Fast" - , card'description = "Shpadoinkle is fast because it does little work. The renderer is modular, so you can always benefit from the latest advances in virtual DOM rendering." + , card'description = "Shpadoinkle does little work. The renderer is modular, so you can always benefit from the latest advances in virtual DOM rendering." } , featureCard $ FeatureCard { card'icon = $(assetLink "/assets/declarative_icon.svg") @@ -86,23 +94,23 @@ featureSection = div [ class' feature__section ] , featureCard $ FeatureCard { card'icon = $(assetLink "/assets/composable_icon.svg") , card'title = "Composable" - , card'description = "With Shpadoinkle, there is no need to associate model update code with DOM locations. This avoids elaborate passing of messages and payloads. Components are highly composable." + , card'description = "Shpadoinkle avoids elaborate passing of messages and payloads. Components are highly composable with Lenses" } , featureCard $ FeatureCard { card'icon = $(assetLink "/assets/reliable_icon.svg") , card'title = "Reliable" - , card'description = "Shpadoinkle UIs are composed of components with no side-effects. So, runtime errors are exceedingly rare. Code is easy to test because model updates are pure functions." + , card'description = "Shpadoinkle UIs are composed of components with no side-effects. Runtime errors are exceedingly rare. Code is easy to test because model updates are pure functions." } , featureCard $ FeatureCard { card'icon = $(assetLink "/assets/lorem_ipsum_logo.svg") - , card'title = "Lorem Ipsum" - , card'description = "Shpadoinkle UIs are composed of components with no side-effects. So, runtime errors are exceedingly rare. Code is easy to test because model updates are pure functions." + , card'title = "Type Safe" + , card'description = "Shpadoinkle facilities type safe UI code. Everything from client server communication with Servant, to compile time checked asset paths, is designed with types in mind." } ] ] , div [ class' feature_button ] [ div [ class' $ split_button <> feature_button__content ] - [ button [ class' split_button__button, type' "button" ] + [ button [ class' split_button__button, type' "button", onClickM_ . navigate @(SPA m) $ RGettingStarted RGSIndex ] [ span [ class' $ split_button__split1 <> feature_button__split ] [ "Checkout the getting started guide" ] , span [ class' $ split_button__split2 <> feature_button__split ] diff --git a/website/Shpadoinkle/Website/Partials/Nav.hs b/website/Shpadoinkle/Website/Partials/Nav.hs index d2617a02..178a182b 100644 --- a/website/Shpadoinkle/Website/Partials/Nav.hs +++ b/website/Shpadoinkle/Website/Partials/Nav.hs @@ -7,6 +7,7 @@ module Shpadoinkle.Website.Partials.Nav (view) where import Prelude hiding (div) +import Shpadoinkle (MonadJSM) import Shpadoinkle.Html as H import Shpadoinkle.Html.TH.AssetLink import Shpadoinkle.Website.Style as Style @@ -24,9 +25,9 @@ toActive = \case _ -> Nothing -navLinkDesktop :: Frontend -> Nav -> Html m a +navLinkDesktop :: MonadJSM m => Frontend -> Nav -> Html m a navLinkDesktop fe n = li [ class' nav__link ] $ - a [ href "getstarted.html" ] [ text $ humanize n ] + a [ onClickM_ $ goTo n ] [ text $ humanize n ] : [ img' [ class' nav__link_underline, src $(assetLink "/assets/nav_line.svg") ] | toActive fe == Just n ] @@ -34,7 +35,7 @@ navLinkMobile :: Nav -> Html m a navLinkMobile n = a [ class' nav_mobile__link, href "#" ] [ text $ humanize n ] -view :: Frontend -> [Html m a] +view :: MonadJSM m => Frontend -> [Html m a] view r = [ H.nav [ class' Style.nav ] [ a [ href "/" ] diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index af50eede..1f39428f 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -10,6 +10,7 @@ module Shpadoinkle.Website.Partials.Template import Data.Text (Text) import Prelude hiding (div) +import Shpadoinkle (MonadJSM) import Shpadoinkle.Html import Shpadoinkle.Html.TH.AssetLink (assetLink) import Shpadoinkle.Router (toHydration) @@ -51,7 +52,7 @@ template ev fe content' = html [ lang "en" ] ] -wrapper :: Frontend -> [Html m a] -> [Html m a] +wrapper :: MonadJSM m => Frontend -> [Html m a] -> [Html m a] wrapper fe content' = Nav.view fe <> [ h "main" [] content' , Footer.view diff --git a/website/Shpadoinkle/Website/Types/Nav.hs b/website/Shpadoinkle/Website/Types/Nav.hs index e2a8eeb6..aaaab15a 100644 --- a/website/Shpadoinkle/Website/Types/Nav.hs +++ b/website/Shpadoinkle/Website/Types/Nav.hs @@ -1,11 +1,19 @@ -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} module Shpadoinkle.Website.Types.Nav where -import Shpadoinkle.Widgets.Types (Humanize (..)) +import Shpadoinkle (MonadJSM) +import Shpadoinkle.Router (navigate) +import Shpadoinkle.Website.Types.Routes (Route (RGettingStarted, RHome, RSandbox, RTutorial), + SPA) +import Shpadoinkle.Website.Types.Routes.GettingStarted (Route (RGSIndex)) +import Shpadoinkle.Website.Types.Routes.Tutorial (Route (RTIndex)) +import Shpadoinkle.Widgets.Types (Humanize (..)) data Nav @@ -24,3 +32,9 @@ instance Humanize Nav where NSandbox -> "Sandbox" +goTo :: forall m. MonadJSM m => Nav -> m () +goTo = navigate @(SPA m) . \case + NHome -> RHome + NGettingStarted -> RGettingStarted RGSIndex + NTutorial -> RTutorial RTIndex + NSandbox -> RSandbox diff --git a/website/Shpadoinkle/Website/Types/Routes.hs b/website/Shpadoinkle/Website/Types/Routes.hs index 5fb5b73b..f5e6607a 100644 --- a/website/Shpadoinkle/Website/Types/Routes.hs +++ b/website/Shpadoinkle/Website/Types/Routes.hs @@ -1,8 +1,13 @@ -{-# LANGUAGE AllowAmbiguousTypes #-} -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeOperators #-} module Shpadoinkle.Website.Types.Routes where @@ -10,11 +15,14 @@ module Shpadoinkle.Website.Types.Routes where import Data.Aeson (FromJSON, ToJSON) +import Data.Proxy (Proxy (Proxy)) import GHC.Generics (Generic) import Servant.API (type (:<|>) (..), type (:>)) import Shpadoinkle (NFData) -import Shpadoinkle.Router (View, +import Shpadoinkle.Router (Redirect (..), + Routed (..), + View, mapUnions, type (:>>)) import Shpadoinkle.Website.Types.Frontend (Frontend) @@ -40,6 +48,7 @@ type SPA m :<|> "getting-started" :> GettingStarted.SPA m Frontend :<|> "packages" :> Packages.SPA m Frontend :<|> "tutorial" :> Tutorial.SPA m Frontend + :<|> "sandbox" :> View m Frontend :<|> "404" :> View m Frontend :<|> View m Frontend @@ -51,5 +60,54 @@ routes :<|> mapUnions RGettingStarted GettingStarted.routes :<|> mapUnions RPackages Packages.routes :<|> mapUnions RTutorial Tutorial.routes + :<|> RSandbox :<|> RFourOhFour :<|> RHome + + +instance Routed (SPA m) Route where + redirect = \case + RHome -> + Redirect (Proxy @("home" :> View m Frontend)) id + + RConcepts -> + Redirect (Proxy @("concepts" :> View m Frontend)) id + + RGettingStarted GettingStarted.RGSIndex -> + Redirect (Proxy @("getting-started" :> View m Frontend)) id + RGettingStarted GettingStarted.RGSAddingToYourProject -> + Redirect (Proxy @("getting-started" :> "adding-to-your-project" :> View m Frontend)) id + RGettingStarted GettingStarted.RGSExtendAnExample -> + Redirect (Proxy @("getting-started" :> "extend-an-example" :> View m Frontend)) id + + RPackages Packages.RPIndex -> + Redirect (Proxy @("packages" :> View m Frontend)) id + RPackages Packages.RPCore -> + Redirect (Proxy @("packages" :> "core" :> View m Frontend)) id + RPackages Packages.RPConsole -> + Redirect (Proxy @("packages" :> "console" :> View m Frontend)) id + RPackages Packages.RPBackends -> + Redirect (Proxy @("packages" :> "backends" :> View m Frontend)) id + RPackages Packages.RPHtml -> + Redirect (Proxy @("packages" :> "html" :> View m Frontend)) id + RPackages Packages.RPLens -> + Redirect (Proxy @("packages" :> "lens" :> View m Frontend)) id + RPackages Packages.RPRouter -> + Redirect (Proxy @("packages" :> "router" :> View m Frontend)) id + RPackages Packages.RPWidgets -> + Redirect (Proxy @("packages" :> "widgets" :> View m Frontend)) id + + RTutorial Tutorial.RTIndex -> + Redirect (Proxy @("tutorial" :> View m Frontend)) id + RTutorial Tutorial.RTCalculator -> + Redirect (Proxy @("tutorial" :> "calculator" :> View m Frontend)) id + RTutorial Tutorial.RTImmediateExecution -> + Redirect (Proxy @("tutorial" :> "immediate-execution" :> View m Frontend)) id + RTutorial Tutorial.RTComposing -> + Redirect (Proxy @("tutorial" :> "composing" :> View m Frontend)) id + + RSandbox -> + Redirect (Proxy @("sandbox" :> View m Frontend)) id + + RFourOhFour -> + Redirect (Proxy @("404" :> View m Frontend)) id diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index 787177b6..a1183703 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -43,7 +43,7 @@ fourOhFour :: Html m a fourOhFour = h2_ [ "404" ] -view :: (ExampleEffects m) => Frontend -> Html m Frontend +view :: (MonadJSM m, ExampleEffects m) => Frontend -> Html m Frontend view fe = div_ . wrapper fe $ case fe of MHome x -> onSum #_MHome <$> Home.view x MConcepts -> [ Documentation.concepts ] -- GitLab From f9a070d35a5a4e5117f9bf8f9a2e5ef48d43299f Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 12 Apr 2021 18:37:00 -0600 Subject: [PATCH 030/116] grr ghcjs --- developer-tools/package.yaml | 1 - html/Shpadoinkle/Html/TH/AssetLink.hs | 11 ++++++----- html/package.yaml | 2 +- nix/overlay-shpadoinkle.nix | 4 ++++ website/Shpadoinkle/Website/View.hs | 2 +- website/default.nix | 2 +- 6 files changed, 13 insertions(+), 9 deletions(-) diff --git a/developer-tools/package.yaml b/developer-tools/package.yaml index 0f4d7747..817f7a75 100644 --- a/developer-tools/package.yaml +++ b/developer-tools/package.yaml @@ -71,7 +71,6 @@ executables: - -fwarn-tabs - -fwarn-incomplete-record-updates - -fwarn-identities - - -dedupe - -O2 main: Main.hs other-modules: diff --git a/html/Shpadoinkle/Html/TH/AssetLink.hs b/html/Shpadoinkle/Html/TH/AssetLink.hs index 26f3b095..0d712c6d 100644 --- a/html/Shpadoinkle/Html/TH/AssetLink.hs +++ b/html/Shpadoinkle/Html/TH/AssetLink.hs @@ -2,10 +2,12 @@ module Shpadoinkle.Html.TH.AssetLink where import Control.Monad (unless) -import Crypto.Hash (Digest, SHA1 (..), hash) -import Data.ByteString as B +import qualified Crypto.Hash.SHA1 as SHA1 +import Data.ByteString (readFile) +import Data.ByteString.Char8 (unpack) import Language.Haskell.TH.Syntax (Exp (LitE), Lit (StringL), Q, runIO) +import Prelude hiding (readFile) import System.Directory (doesFileExist) @@ -18,6 +20,5 @@ assetLinkWithBase base fp' = runIO $ do let fp = base <> fp' exists <- doesFileExist $ "." <> fp unless exists . fail $ "No asset found at " <> fp - c <- B.readFile $ "." <> fp - let h = hash c :: Digest SHA1 - return . LitE . StringL $ fp <> "?_=" <> show h + c <- readFile $ "." <> fp + return . LitE . StringL $ fp <> "?_=" <> unpack (SHA1.hash c) diff --git a/html/package.yaml b/html/package.yaml index c7560ede..988d4113 100644 --- a/html/package.yaml +++ b/html/package.yaml @@ -34,7 +34,7 @@ dependencies: - bytestring - containers >= 0.6.0 && < 0.7 - compactable - - cryptonite + - cryptohash-sha1 - directory - text >= 1.2.3 && < 1.3 - time diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index 5b0f9e60..75932da9 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -152,6 +152,10 @@ in { websiteOverride = x: x.overrideAttrs (old: { buildInputs = old.buildInputs ++ [ super.asciidoctor ]; + prePatch = '' + ${old.prePatch} + cp -r ${../website/docs} docs + ''; }); hpkgs = { diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index a1183703..d9b06d0d 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -43,7 +43,7 @@ fourOhFour :: Html m a fourOhFour = h2_ [ "404" ] -view :: (MonadJSM m, ExampleEffects m) => Frontend -> Html m Frontend +view :: MonadJSM m => Frontend -> Html m Frontend view fe = div_ . wrapper fe $ case fe of MHome x -> onSum #_MHome <$> Home.view x MConcepts -> [ Documentation.concepts ] diff --git a/website/default.nix b/website/default.nix index 4ae02b31..71876c94 100644 --- a/website/default.nix +++ b/website/default.nix @@ -15,7 +15,7 @@ in LANG = "C.UTF-8"; } '' mkdir $out - ln -s ${./static} $out/static + ln -s ${./assets} $out/assets ln -s ${opti pkgsJS.haskell.packages.${util.compilerjs}.Shpadoinkle-website}/bin/run.jsexe/${file} $out/all.min.js ${pkgs.haskell.packages.${compiler}.Shpadoinkle-website}/bin/disembodied -o $out '' -- GitLab From 0ddd8af99c33d3091f6d29041ec7339782d51c46 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Tue, 13 Apr 2021 14:58:46 -0600 Subject: [PATCH 031/116] no stonks --- disembodied/Shpadoinkle/Disembodied.hs | 118 +++++++++--------- disembodied/Shpadoinkle/Disembodied/Sample.hs | 20 ++- html/Shpadoinkle/Html/TH/AssetLink.hs | 7 +- html/package.yaml | 3 +- nix/overlay-shpadoinkle.nix | 10 ++ router/Shpadoinkle/Router.hs | 47 +++++-- website/Shpadoinkle/Website/Disembodied.hs | 44 +++---- website/package.yaml | 23 +++- 8 files changed, 156 insertions(+), 116 deletions(-) diff --git a/disembodied/Shpadoinkle/Disembodied.hs b/disembodied/Shpadoinkle/Disembodied.hs index fdac8d92..f30f5dbd 100644 --- a/disembodied/Shpadoinkle/Disembodied.hs +++ b/disembodied/Shpadoinkle/Disembodied.hs @@ -47,38 +47,38 @@ import Shpadoinkle.Router (HTML, View) -- Site takes a context `ctx` which is universal for the static site. -- This is useful for storing commonly used valus like the site name, -- site url, copyright date, ect. -data Site ctx where +data Site where -- | A path segment in the URI SPath :: String -- ^ The current URI path segment - -> Site ctx + -> Site -- ^ The site to be rendered at the path - -> Site ctx + -> Site -- | Html to be rendered as @index.html@ SIndex - :: forall m a ctx - . (ctx -> Html m a) + :: forall m a + . Html m a -- ^ Given a context, how can we render a page? - -> Site ctx + -> Site -- | Capture is the one Servant combinator that can be meaningful in static -- site generation, and only if we can generate all possible instances. SCapture :: (FromHttpApiData x, ToHttpApiData x, Bounded x, Enum x) - => (x -> Site ctx) + => (x -> Site) -- ^ Given a context, provide the remaining site to be generated - -> Site ctx + -> Site -- | Branch the site at a given point in generation. - SChoice :: Site ctx -> Site ctx -> Site ctx + SChoice :: Site -> Site -> Site -- | Type class induction for building the site out of a specification -class Disembodied ctx a where +class Disembodied a where -- | A type family to represent the relationship between a Servant API -- and the 'Html' views to render. -- @@ -90,105 +90,103 @@ class Disembodied ctx a where -- site = const (h1_ [ text "about" ]) -- :\<|\> const (h1_ [ text "home" ]) -- @ - type SiteSpec ctx a :: Type + type SiteSpec a :: Type -- | Construct the site structure out of the associated API - buildSite :: SiteSpec ctx a -> Site ctx + buildSite :: SiteSpec a -> Site -instance (Disembodied ctx x, Disembodied ctx y) - => Disembodied ctx (x :<|> y) where +instance (Disembodied x, Disembodied y) + => Disembodied (x :<|> y) where - type SiteSpec ctx (x :<|> y) = SiteSpec ctx x :<|> SiteSpec ctx y + type SiteSpec (x :<|> y) = SiteSpec x :<|> SiteSpec y - buildSite :: SiteSpec ctx x :<|> SiteSpec ctx y -> Site ctx - buildSite (x :<|> y) = SChoice (buildSite @ctx @x x) (buildSite @ctx @y y) + buildSite :: SiteSpec x :<|> SiteSpec y -> Site + buildSite (x :<|> y) = SChoice (buildSite @x x) (buildSite @y y) {-# INLINABLE buildSite #-} -instance (Disembodied ctx sub, KnownSymbol path) - => Disembodied ctx (path :> sub) where +instance (Disembodied sub, KnownSymbol path) + => Disembodied (path :> sub) where - type SiteSpec ctx (path :> sub) = SiteSpec ctx sub + type SiteSpec (path :> sub) = SiteSpec sub - buildSite :: SiteSpec ctx sub -> Site ctx - buildSite = SPath (symbolVal (Proxy @path)) . buildSite @ctx @sub + buildSite :: SiteSpec sub -> Site + buildSite = SPath (symbolVal (Proxy @path)) . buildSite @sub {-# INLINABLE buildSite #-} -instance (Disembodied ctx sub, FromHttpApiData x, ToHttpApiData x, Bounded x, Enum x) - => Disembodied ctx (Capture sym x :> sub) where +instance (Disembodied sub, FromHttpApiData x, ToHttpApiData x, Bounded x, Enum x) + => Disembodied (Capture sym x :> sub) where - type SiteSpec ctx (Capture sym x :> sub) = x -> SiteSpec ctx sub + type SiteSpec (Capture sym x :> sub) = x -> SiteSpec sub - buildSite :: (x -> SiteSpec ctx sub) -> Site ctx - buildSite = SCapture . (buildSite @ctx @sub .) + buildSite :: (x -> SiteSpec sub) -> Site + buildSite = SCapture . (buildSite @sub .) {-# INLINABLE buildSite #-} -instance Disembodied ctx sub - => Disembodied ctx (QueryParam sym x :> sub) where +instance Disembodied sub + => Disembodied (QueryParam sym x :> sub) where - type SiteSpec ctx (QueryParam sym x :> sub) = Maybe x -> SiteSpec ctx sub + type SiteSpec (QueryParam sym x :> sub) = Maybe x -> SiteSpec sub - buildSite :: (Maybe x -> SiteSpec ctx sub) -> Site ctx - buildSite f = buildSite @ctx @sub $ f Nothing + buildSite :: (Maybe x -> SiteSpec sub) -> Site + buildSite f = buildSite @sub $ f Nothing {-# INLINABLE buildSite #-} -instance Disembodied ctx sub - => Disembodied ctx (QueryParam' ms sym x :> sub) where +instance Disembodied sub + => Disembodied (QueryParam' ms sym x :> sub) where - type SiteSpec ctx (QueryParam' ms sym x :> sub) = Maybe x -> SiteSpec ctx sub + type SiteSpec (QueryParam' ms sym x :> sub) = Maybe x -> SiteSpec sub - buildSite :: (Maybe x -> SiteSpec ctx sub) -> Site ctx - buildSite f = buildSite @ctx @sub $ f Nothing + buildSite :: (Maybe x -> SiteSpec sub) -> Site + buildSite f = buildSite @sub $ f Nothing {-# INLINABLE buildSite #-} -instance Disembodied ctx sub - => Disembodied ctx (QueryParams sym x :> sub) where +instance Disembodied sub + => Disembodied (QueryParams sym x :> sub) where - type SiteSpec ctx (QueryParams sym x :> sub) = [x] -> SiteSpec ctx sub + type SiteSpec (QueryParams sym x :> sub) = [x] -> SiteSpec sub - buildSite :: ([x] -> SiteSpec ctx sub) -> Site ctx - buildSite f = buildSite @ctx @sub $ f [] + buildSite :: ([x] -> SiteSpec sub) -> Site + buildSite f = buildSite @sub $ f [] {-# INLINABLE buildSite #-} -instance Disembodied ctx sub - => Disembodied ctx (QueryFlag sym :> sub) where +instance Disembodied sub + => Disembodied (QueryFlag sym :> sub) where - type SiteSpec ctx (QueryFlag sym :> sub) = Bool -> SiteSpec ctx sub + type SiteSpec (QueryFlag sym :> sub) = Bool -> SiteSpec sub - buildSite :: (Bool -> SiteSpec ctx sub) -> Site ctx - buildSite f = buildSite @ctx @sub $ f False + buildSite :: (Bool -> SiteSpec sub) -> Site + buildSite f = buildSite @sub $ f False {-# INLINABLE buildSite #-} -instance Disembodied ctx (f '[HTML] (Html m a)) where +instance Disembodied (f '[HTML] (Html m a)) where - type SiteSpec ctx (f '[HTML] (Html m a)) = ctx -> Html m a + type SiteSpec (f '[HTML] (Html m a)) = Html m a - buildSite :: (ctx -> Html m a) -> Site ctx + buildSite :: Html m a -> Site buildSite = SIndex {-# INLINABLE buildSite #-} -instance Disembodied ctx (View m a) where +instance Disembodied (View m a) where - type SiteSpec ctx (View m a) = ctx -> Html m a + type SiteSpec (View m a) = Html m a - buildSite :: (ctx -> Html m a) -> Site ctx + buildSite :: Html m a -> Site buildSite = SIndex {-# INLINABLE buildSite #-} -- | Actually write the site to disk. Branches are written in parallel. writeSite - :: forall layout ctx. Disembodied ctx layout + :: forall layout. Disembodied layout => FilePath -- ^ Out path - -> ctx - -- ^ Universal context for the static site. - -> SiteSpec ctx layout + -> SiteSpec layout -- ^ Specification for the pages of the site relative to a Servant API. -> IO () -writeSite fs ctx layout = go fs $ buildSite @ctx @layout layout where +writeSite fs layout = go fs $ buildSite @layout layout where - go :: FilePath -> Site ctx -> IO () - go curr (SIndex page) = T.writeFile (curr "index" <.> "html") . renderStatic $ page ctx + go :: FilePath -> Site -> IO () + go curr (SIndex page) = T.writeFile (curr "index" <.> "html") $ renderStatic page go curr (SChoice x y) = void $ go curr x `concurrently` go curr y go curr (SCapture f) = forConcurrently_ [ minBound .. maxBound ] $ \c -> go curr $ SPath (unpack $ toUrlPiece c) $ f c diff --git a/disembodied/Shpadoinkle/Disembodied/Sample.hs b/disembodied/Shpadoinkle/Disembodied/Sample.hs index a1ef57e7..dce459a8 100644 --- a/disembodied/Shpadoinkle/Disembodied/Sample.hs +++ b/disembodied/Shpadoinkle/Disembodied/Sample.hs @@ -9,10 +9,9 @@ module Shpadoinkle.Disembodied.Sample where -import Data.Text (Text) import Servant.API -import Shpadoinkle (Html, JSM, MonadJSM, text) +import Shpadoinkle (Html, JSM, MonadJSM) import Shpadoinkle.Disembodied (Disembodied (SiteSpec), writeSite) import Shpadoinkle.Html (button, h1_, onClick) import Shpadoinkle.Router (View) @@ -23,14 +22,9 @@ type Pages m :<|> View m () -newtype Context = Context - { siteName :: Text } - - -about :: MonadJSM m => Context -> Html m Int -about ctx = - h1_ [ text $ "about us at " <> siteName ctx - , button +about :: MonadJSM m => Html m Int +about = + h1_ [ button [ onClick (+ 1) ] [ "Increment" ] ] @@ -40,9 +34,9 @@ home :: Monad m => Html m a home = h1_ [ "home" ] -site :: MonadJSM m => SiteSpec Context (Pages m) -site = about :<|> const home +site :: MonadJSM m => SiteSpec (Pages m) +site = about :<|> home makeSite :: IO () -makeSite = writeSite @(Pages JSM) "" (Context "Sample") site +makeSite = writeSite @(Pages JSM) "" site diff --git a/html/Shpadoinkle/Html/TH/AssetLink.hs b/html/Shpadoinkle/Html/TH/AssetLink.hs index 0d712c6d..4eaaa3f3 100644 --- a/html/Shpadoinkle/Html/TH/AssetLink.hs +++ b/html/Shpadoinkle/Html/TH/AssetLink.hs @@ -1,8 +1,8 @@ module Shpadoinkle.Html.TH.AssetLink where +import qualified Bitcoin.Hash as Stonks import Control.Monad (unless) -import qualified Crypto.Hash.SHA1 as SHA1 import Data.ByteString (readFile) import Data.ByteString.Char8 (unpack) import Language.Haskell.TH.Syntax (Exp (LitE), Lit (StringL), Q, @@ -17,8 +17,9 @@ assetLink = assetLinkWithBase "" assetLinkWithBase :: FilePath -> FilePath -> Q Exp assetLinkWithBase base fp' = runIO $ do - let fp = base <> fp' + let fp = base <> fp' + hash = take 20 . unpack . Stonks.check32 exists <- doesFileExist $ "." <> fp unless exists . fail $ "No asset found at " <> fp c <- readFile $ "." <> fp - return . LitE . StringL $ fp <> "?_=" <> unpack (SHA1.hash c) + return . LitE . StringL $ fp <> "?_=" <> hash c diff --git a/html/package.yaml b/html/package.yaml index 988d4113..69d620d7 100644 --- a/html/package.yaml +++ b/html/package.yaml @@ -31,10 +31,11 @@ extra-source-files: dependencies: - base >= 4.12.0 && < 4.16 + - base16-bytestring - bytestring - containers >= 0.6.0 && < 0.7 - compactable - - cryptohash-sha1 + - bitcoin-hash - directory - text >= 1.2.3 && < 1.3 - time diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index 75932da9..18d5fc28 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -10,6 +10,13 @@ chrome-rev = "71336116f3f78d3bb1f499bf4b88efcd8738a9cf"; + bitcoin-hash = builtins.fetchGit + { url = https://gitlab.com/k0001/hs-bitcoin-hash.git; + rev = "b0e85f463be0b23937a98acb16d7e4ffbca005a8"; + ref = "master"; + }; + + jsaddle-src = builtins.fetchGit { url = https://github.com/ghcjs/jsaddle.git; rev = "97273656e28790ab6e35c827f8086cf47bfbedca"; @@ -155,6 +162,7 @@ in { prePatch = '' ${old.prePatch} cp -r ${../website/docs} docs + chmod -R +rw docs ''; }); @@ -177,6 +185,7 @@ in { Shpadoinkle-examples = call "Shpadoinkle-examples" ../examples; Shpadoinkle-isreal = call "Shpadoinkle-isreal" ../isreal; + bitcoin-hash = hsuper.callPackage (import "${bitcoin-hash}/bitcoin-hash/pkg-${if isJS then "ghcjs" else "ghc"}.nix") {}; ease = hself.callCabal2nix "ease" ease {}; ghcjs-base-stub = hself.callCabal2nix "ghcjs-base-stub" ghcjs-base-stub-src {}; hpack = if isJS then super.haskell.packages.${compiler}.hpack else hsuper.hpack; @@ -211,6 +220,7 @@ in { "criterion" "megaparsec" "lens" + "ListLike" "http-types" "text-short" "silently" diff --git a/router/Shpadoinkle/Router.hs b/router/Shpadoinkle/Router.hs index 72911275..6e61568d 100644 --- a/router/Shpadoinkle/Router.hs +++ b/router/Shpadoinkle/Router.hs @@ -3,6 +3,7 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE ExtendedDefaultRules #-} +{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE InstanceSigs #-} @@ -42,7 +43,7 @@ module Shpadoinkle.Router ( -- * Re-Exports , Raw, S.RawM', S.RawM, MonadJSM, HasLink(..) -- * Sub route utilities - , MapSwitch (..) + , TraverseUnions (..), mapUnions ) where @@ -53,6 +54,7 @@ import Control.Monad.IO.Class (MonadIO (liftIO)) import Data.Aeson (FromJSON, ToJSON, decode, encode) import Data.ByteString.Lazy (fromStrict, toStrict) +import Data.Functor.Identity (Identity (..)) import Data.Kind (Type) import Data.Maybe (isJust) import Data.Proxy (Proxy (..)) @@ -507,22 +509,47 @@ instance HasLink (View m a) where type family SwitchOutput layout b :: Type where + SwitchOutput ((a :<|> as) :<|> r) b = (b :<|> SwitchOutput as b) :<|> SwitchOutput r b SwitchOutput (a :<|> r) b = b :<|> SwitchOutput r b SwitchOutput a b = b -type family SwitchInput layout b :: Type where - SwitchInput (a :<|> r) b = a - SwitchInput a b = a +type family SwitchInput layout :: Type where + SwitchInput ((a :<|> as) :<|> r) = a + SwitchInput (a :<|> r) = a + SwitchInput a = a -class MapSwitch layout b where - mapUnions :: (SwitchInput layout b -> b) -> layout -> SwitchOutput layout b +class TraverseUnions m layout b where + traverseUnions :: (SwitchInput layout -> m b) -> layout -> m (SwitchOutput layout b) +instance + ( Applicative m + , TraverseUnions m r b + , TraverseUnions m as b + , SwitchInput r ~ a + , SwitchInput as ~ a) + => TraverseUnions m ((a :<|> as) :<|> r) b where + traverseUnions f ((a :<|> as) :<|> r) = (\a' as' r' -> (a' :<|> as') :<|> r') + <$> f a + <*> traverseUnions @m @as @b f as + <*> traverseUnions @m @r @b f r -instance (MapSwitch r b, SwitchInput r b ~ a) => MapSwitch (a :<|> r) b where - mapUnions f (a :<|> r) = f a :<|> mapUnions @r @b f r +instance + {-# OVERLAPPABLE #-} + ( Applicative m + , TraverseUnions m r b + , SwitchInput (a :<|> r) ~ a + , SwitchInput r ~ a + , SwitchOutput (a :<|> r) b ~ (b :<|> SwitchOutput r b)) + => TraverseUnions m (a :<|> r) b where + traverseUnions f (a :<|> r) = (:<|>) <$> f a <*> traverseUnions @m @r @b f r -instance {-# OVERLAPPABLE #-} (SwitchOutput a b ~ b, SwitchInput a b ~ a) => MapSwitch a b where - mapUnions f a = f a +instance {-# OVERLAPPABLE #-} (SwitchOutput a b ~ b, SwitchInput a ~ a) + => TraverseUnions m a b where + traverseUnions f a = f a + + +mapUnions :: TraverseUnions Identity a b => (SwitchInput a -> b) -> a -> SwitchOutput a b +mapUnions f = runIdentity . traverseUnions (Identity . f) diff --git a/website/Shpadoinkle/Website/Disembodied.hs b/website/Shpadoinkle/Website/Disembodied.hs index 8d49598c..98e3f16e 100644 --- a/website/Shpadoinkle/Website/Disembodied.hs +++ b/website/Shpadoinkle/Website/Disembodied.hs @@ -14,39 +14,31 @@ module Main where -import Control.Lens (Prism', re, (^.)) import Control.Monad.IO.Class import Control.Monad.Reader -import Data.Generics.Labels () -import Servant.API (type (:<|>) ((:<|>))) -import System.Environment (getArgs) +import Data.Generics.Labels () +import Shpadoinkle (JSM, MonadJSM, + TVar) +import Shpadoinkle.Disembodied (Disembodied (SiteSpec), + writeSite) +import Shpadoinkle.Isreal.Types (Code) +import Shpadoinkle.Router (traverseUnions) +import Shpadoinkle.Website.Types.Effects.Hooglable (Hooglable) +import Shpadoinkle.Website.Types.Effects.Swan (Swan) +import Shpadoinkle.Website.Types.Routes +import Shpadoinkle.Website.View as View +import System.Environment (getArgs) -import Shpadoinkle (Html, JSM, MonadJSM, TVar) -import Shpadoinkle.Disembodied (Disembodied (SiteSpec), writeSite) -import Shpadoinkle.Isreal.Types (Code, SnowToken) -import Shpadoinkle.Lens (onSum) -import Shpadoinkle.Run (Env (Prod)) - -import Shpadoinkle.Website.Types -import Shpadoinkle.Website.View newtype Noop a = Noop (JSM a) deriving newtype (Functor, Applicative, Monad, MonadJSM, MonadIO) deriving anyclass (Hooglable, Swan, MonadReader (TVar (Maybe Code))) -wrap :: Monad m => Prism' Frontend a -> (a -> Html m a) -> a -> b -> Html m Frontend -wrap l v x = const $ template Prod (x ^. re l) (l `onSum` v x) - - -site :: Hooglable m => ExampleEffects m => MonadJSM m => Examples SnowToken -> SiteSpec () (SPA m) -site token = wrap #_MHome home (emptyHome token) - :<|> const concepts - :<|> (const gsIndex :<|> const gsAddingToYourProject :<|> const gsExtendAnExample) - :<|> (const rpIndex :<|> const rpCore :<|> const rpConsole :<|> const rpBackends :<|> const rpHtml :<|> const rpLens :<|> const rpRouter :<|> const rpWidgets) - :<|> (const rtIndex :<|> const rtCalculator :<|> const rtImmediateExecution :<|> const rtComposing) - :<|> const fourOhFour - :<|> wrap #_MHome home (emptyHome token) +getStarts + :: MonadJSM m + => IO (SiteSpec (SPA m)) +getStarts = traverseUnions (fmap view . start) routes main :: IO () @@ -56,5 +48,5 @@ main = do [ "--out", out' ] -> out' [ "-o", out' ] -> out' _ -> error "You must pass --out or -o" - token <- genExampleTokens - writeSite @ (SPA Noop) out () (site token) + site <- getStarts + writeSite @ (SPA Noop) out site diff --git a/website/package.yaml b/website/package.yaml index 5836cac5..0f2af505 100644 --- a/website/package.yaml +++ b/website/package.yaml @@ -30,7 +30,6 @@ ghcjs-options: - -fwarn-tabs - -fwarn-incomplete-record-updates - -fwarn-identities - - -dedupe - -O2 @@ -118,10 +117,28 @@ executables: buildable: True main: Shpadoinkle/Website/Disembodied.hs other-modules: - - Shpadoinkle.Website.Types - - Shpadoinkle.Website.Types.Hoogle - Shpadoinkle.Website.Style + - Shpadoinkle.Website.Page.Home + - Shpadoinkle.Website.Page.Documentation + - Shpadoinkle.Website.Partials.Template + - Shpadoinkle.Website.Types.Effects.Example + - Shpadoinkle.Website.Types.Effects.Hooglable + - Shpadoinkle.Website.Types.Effects.Swan + - Shpadoinkle.Website.Types.Frontend + - Shpadoinkle.Website.Types.Home + - Shpadoinkle.Website.Types.Routes - Shpadoinkle.Website.View + - Shpadoinkle.Website.Component.LiveExample + - Shpadoinkle.Website.Partials.Footer + - Shpadoinkle.Website.Partials.Nav + - Shpadoinkle.Website.Types.Example + - Shpadoinkle.Website.Types.ExampleState + - Shpadoinkle.Website.Types.Hoogle + - Shpadoinkle.Website.Types.Hoogle.Target + - Shpadoinkle.Website.Types.Nav + - Shpadoinkle.Website.Types.Routes.GettingStarted + - Shpadoinkle.Website.Types.Routes.Packages + - Shpadoinkle.Website.Types.Routes.Tutorial source-dirs: . dependencies: - Shpadoinkle-disembodied -- GitLab From 395ed4da37199fd93ad73283075b54dfe9b95b3f Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Tue, 13 Apr 2021 15:03:06 -0600 Subject: [PATCH 032/116] no stonks --- html/Shpadoinkle/Html/TH/AssetLink.hs | 16 +++++++++------- html/package.yaml | 3 +-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/html/Shpadoinkle/Html/TH/AssetLink.hs b/html/Shpadoinkle/Html/TH/AssetLink.hs index 4eaaa3f3..47125160 100644 --- a/html/Shpadoinkle/Html/TH/AssetLink.hs +++ b/html/Shpadoinkle/Html/TH/AssetLink.hs @@ -1,14 +1,13 @@ module Shpadoinkle.Html.TH.AssetLink where -import qualified Bitcoin.Hash as Stonks import Control.Monad (unless) -import Data.ByteString (readFile) -import Data.ByteString.Char8 (unpack) import Language.Haskell.TH.Syntax (Exp (LitE), Lit (StringL), Q, runIO) import Prelude hiding (readFile) import System.Directory (doesFileExist) +import System.Exit (ExitCode (..)) +import System.Process (readProcessWithExitCode) assetLink :: FilePath -> Q Exp @@ -17,9 +16,12 @@ assetLink = assetLinkWithBase "" assetLinkWithBase :: FilePath -> FilePath -> Q Exp assetLinkWithBase base fp' = runIO $ do - let fp = base <> fp' - hash = take 20 . unpack . Stonks.check32 + let fp = base <> fp' + exists <- doesFileExist $ "." <> fp unless exists . fail $ "No asset found at " <> fp - c <- readFile $ "." <> fp - return . LitE . StringL $ fp <> "?_=" <> hash c + + out@(exit, hash, _) <- readProcessWithExitCode "sha1sum" [fp] "" + case exit of + ExitSuccess -> LitE . StringL $ fp <> "?_=" <> take 20 hash + ExitFailure _ -> error $ show out diff --git a/html/package.yaml b/html/package.yaml index 69d620d7..52c50a95 100644 --- a/html/package.yaml +++ b/html/package.yaml @@ -31,11 +31,9 @@ extra-source-files: dependencies: - base >= 4.12.0 && < 4.16 - - base16-bytestring - bytestring - containers >= 0.6.0 && < 0.7 - compactable - - bitcoin-hash - directory - text >= 1.2.3 && < 1.3 - time @@ -47,6 +45,7 @@ dependencies: - jsaddle >= 0.9.7 && < 0.20 - raw-strings-qq - lens + - process - Shpadoinkle -- GitLab From 068922752f16a7ae5450adbc9d5b7808c30c9eaa Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Tue, 13 Apr 2021 20:42:23 -0600 Subject: [PATCH 033/116] compiled --- html/Shpadoinkle/Html/TH/AssetLink.hs | 4 +- .../Shpadoinkle/Website/Page/Documentation.hs | 37 +++--- website/Shpadoinkle/Website/Page/Home.hs | 24 ++-- .../Shpadoinkle/Website/Partials/Footer.hs | 4 +- website/Shpadoinkle/Website/Partials/Nav.hs | 10 +- .../Shpadoinkle/Website/Partials/Template.hs | 4 +- website/Shpadoinkle/Website/Style.hs | 109 +++++++++++++++++- website/Shpadoinkle/Website/Types/Home.hs | 8 +- 8 files changed, 145 insertions(+), 55 deletions(-) diff --git a/html/Shpadoinkle/Html/TH/AssetLink.hs b/html/Shpadoinkle/Html/TH/AssetLink.hs index 47125160..f89a74ee 100644 --- a/html/Shpadoinkle/Html/TH/AssetLink.hs +++ b/html/Shpadoinkle/Html/TH/AssetLink.hs @@ -21,7 +21,7 @@ assetLinkWithBase base fp' = runIO $ do exists <- doesFileExist $ "." <> fp unless exists . fail $ "No asset found at " <> fp - out@(exit, hash, _) <- readProcessWithExitCode "sha1sum" [fp] "" + out@(exit, hash, _) <- readProcessWithExitCode "sha1sum" ["." <> fp] "" case exit of - ExitSuccess -> LitE . StringL $ fp <> "?_=" <> take 20 hash + ExitSuccess -> return . LitE . StringL $ fp <> "?_=" <> take 20 hash ExitFailure _ -> error $ show out diff --git a/website/Shpadoinkle/Website/Page/Documentation.hs b/website/Shpadoinkle/Website/Page/Documentation.hs index 3d549412..a8dbdfae 100644 --- a/website/Shpadoinkle/Website/Page/Documentation.hs +++ b/website/Shpadoinkle/Website/Page/Documentation.hs @@ -1,13 +1,10 @@ -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE LambdaCase #-} module Shpadoinkle.Website.Page.Documentation where -import Data.Text import Shpadoinkle.Html -import Shpadoinkle.Template.TH import qualified Shpadoinkle.Website.Types.Routes.GettingStarted as GettingStarted import qualified Shpadoinkle.Website.Types.Routes.Packages as Packages import qualified Shpadoinkle.Website.Types.Routes.Tutorial as Tutorial @@ -17,22 +14,22 @@ concepts , gsIndex, gsAddingToYourProject, gsExtendAnExample , rpIndex, rpCore, rpConsole, rpBackends, rpHtml, rpLens, rpRouter, rpWidgets , rtIndex, rtCalculator, rtImmediateExecution, rtComposing :: Html m a -concepts = div_ $(embedAsciidoc "./docs/concepts.adoc") -gsIndex = div_ $(embedAsciidoc "./docs/getting-started/index.adoc") -gsAddingToYourProject = div_ $(embedAsciidoc "./docs/getting-started/adding-to-your-project.adoc") -gsExtendAnExample = div_ $(embedAsciidoc "./docs/getting-started/extend-an-example.adoc") -rpIndex = div_ $(embedAsciidoc "./docs/packages/index.adoc") -rpCore = div_ $(embedAsciidoc "./docs/packages/core.adoc") -rpConsole = div_ $(embedAsciidoc "./docs/packages/console.adoc") -rpBackends = div_ $(embedAsciidoc "./docs/packages/backends.adoc") -rpHtml = div_ $(embedAsciidoc "./docs/packages/html.adoc") -rpLens = div_ $(embedAsciidoc "./docs/packages/lens.adoc") -rpRouter = div_ $(embedAsciidoc "./docs/packages/router.adoc") -rpWidgets = div_ $(embedAsciidoc "./docs/packages/widgets.adoc") -rtIndex = div_ $(embedAsciidoc "./docs/tutorial/index.adoc") -rtCalculator = div_ $(embedAsciidoc "./docs/tutorial/calculator.adoc") -rtImmediateExecution = div_ $(embedAsciidoc "./docs/tutorial/immediate-execution.adoc") -rtComposing = div_ $(embedAsciidoc "./docs/tutorial/composing.adoc") +concepts = div_ [] +gsIndex = div_ [] +gsAddingToYourProject = div_ [] +gsExtendAnExample = div_ [] +rpIndex = div_ [] +rpCore = div_ [] +rpConsole = div_ [] +rpBackends = div_ [] +rpHtml = div_ [] +rpLens = div_ [] +rpRouter = div_ [] +rpWidgets = div_ [] +rtIndex = div_ [] +rtCalculator = div_ [] +rtImmediateExecution = div_ [] +rtComposing = div_ [] gettingStarted :: GettingStarted.Route -> Html m a diff --git a/website/Shpadoinkle/Website/Page/Home.hs b/website/Shpadoinkle/Website/Page/Home.hs index f9ecd8a0..594e49e5 100644 --- a/website/Shpadoinkle/Website/Page/Home.hs +++ b/website/Shpadoinkle/Website/Page/Home.hs @@ -1,6 +1,5 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} @@ -11,7 +10,6 @@ import Data.Text (Text) import Prelude hiding (div, span) import Shpadoinkle.Html as H -import Shpadoinkle.Html.TH.AssetLink (assetLink) import Shpadoinkle.Router (MonadJSM, navigate) import Shpadoinkle.Website.Style as Style @@ -43,14 +41,14 @@ hero' = div_ [ span [ class' $ split_button__split1 <> hero_button__split ] [ "Get Started" ] , span [ class' $ split_button__split2 <> hero_button__split ] - [ img' [ alt "Right arrow", src $(assetLink "/assets/right-arrow-white.svg") ] + [ img' [ alt "Right arrow", src "/assets/right-arrow-white.svg" ] ] ] ] ] ] - , img' [ alt "desert cactus", class' hero_bg, src $(assetLink "/assets/hero_image.svg") ] - , img' [ alt "desert cactus", class' hero_bg_mobile, src $(assetLink "/assets/mobile/hero_image.svg") ] + , img' [ alt "desert cactus", class' hero_bg, src "/assets/hero_image.svg" ] + , img' [ alt "desert cactus", class' hero_bg_mobile, src "/assets/mobile/hero_image.svg" ] ] @@ -82,27 +80,27 @@ featureSection = div [ class' feature__section ] [ "A programming model for declarative, high performance user interface." ] , div [ class' feature__cards ] [ featureCard $ FeatureCard - { card'icon = $(assetLink "/assets/fast_icon.svg") + { card'icon = "/assets/fast_icon.svg" , card'title = "Fast" , card'description = "Shpadoinkle does little work. The renderer is modular, so you can always benefit from the latest advances in virtual DOM rendering." } , featureCard $ FeatureCard - { card'icon = $(assetLink "/assets/declarative_icon.svg") + { card'icon = "/assets/declarative_icon.svg" , card'title = "Declarative" , card'description = "Your Shpadoinkle code is high-level. You need not worry about low-level details, causality, or when DOM nodes get replaced." } , featureCard $ FeatureCard - { card'icon = $(assetLink "/assets/composable_icon.svg") + { card'icon = "/assets/composable_icon.svg" , card'title = "Composable" , card'description = "Shpadoinkle avoids elaborate passing of messages and payloads. Components are highly composable with Lenses" } , featureCard $ FeatureCard - { card'icon = $(assetLink "/assets/reliable_icon.svg") + { card'icon = "/assets/reliable_icon.svg" , card'title = "Reliable" , card'description = "Shpadoinkle UIs are composed of components with no side-effects. Runtime errors are exceedingly rare. Code is easy to test because model updates are pure functions." } , featureCard $ FeatureCard - { card'icon = $(assetLink "/assets/lorem_ipsum_logo.svg") + { card'icon = "/assets/lorem_ipsum_logo.svg" , card'title = "Type Safe" , card'description = "Shpadoinkle facilities type safe UI code. Everything from client server communication with Servant, to compile time checked asset paths, is designed with types in mind." } @@ -114,12 +112,12 @@ featureSection = div [ class' feature__section ] [ span [ class' $ split_button__split1 <> feature_button__split ] [ "Checkout the getting started guide" ] , span [ class' $ split_button__split2 <> feature_button__split ] - [ img' [ alt "Right arrow", src $(assetLink "/assets/right-arrow.svg") ] + [ img' [ alt "Right arrow", src "/assets/right-arrow.svg" ] ] ] ] ] - , img' [ alt "background transition", class' transition, src $(assetLink "/assets/orange_purple_transition.svg") ] + , img' [ alt "background transition", class' transition, src "/assets/orange_purple_transition.svg" ] ] @@ -158,5 +156,5 @@ componentsSection = div [ class' components__section ] [ div' "component__example__code" ] ] - , img' [ alt "background transition", className "transition", src "assets/purple_black_transition.svg" ] + , img' [ alt "background transition", className "transition", src "/assets/purple_black_transition.svg" ] ] diff --git a/website/Shpadoinkle/Website/Partials/Footer.hs b/website/Shpadoinkle/Website/Partials/Footer.hs index 0fea4b0b..7c651f02 100644 --- a/website/Shpadoinkle/Website/Partials/Footer.hs +++ b/website/Shpadoinkle/Website/Partials/Footer.hs @@ -1,5 +1,4 @@ {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TemplateHaskell #-} module Shpadoinkle.Website.Partials.Footer (view) where @@ -9,7 +8,6 @@ import Prelude hiding (div) import Shpadoinkle.Html (Html, a, alt, class', div, div_, footer_, href, img', li_, nav_, newTab, p, src, text, ul) -import Shpadoinkle.Html.TH.AssetLink (assetLink) import Shpadoinkle.Website.Style as Style import Shpadoinkle.Website.Types.Nav (Nav) import Shpadoinkle.Widgets.Types (humanize) @@ -27,7 +25,7 @@ view = footer_ [ div [ class' footer__wrapper ] [ a [ href "/" ] - [ img' [ alt "shpadoinkle logo", src $(assetLink "/assets/try_shpadoinkle_footer_logo.svg") ] + [ img' [ alt "shpadoinkle logo", src "/assets/try_shpadoinkle_footer_logo.svg" ] ] , div [ class' $ flex <> flex_col <> justify_between <> w_full <> gap_8 <> pt_12 <> leading_8 <> "md:flex-row" <> "md:w-2/5" <> "md:gap-0" ] [ div_ diff --git a/website/Shpadoinkle/Website/Partials/Nav.hs b/website/Shpadoinkle/Website/Partials/Nav.hs index 178a182b..b53d88d9 100644 --- a/website/Shpadoinkle/Website/Partials/Nav.hs +++ b/website/Shpadoinkle/Website/Partials/Nav.hs @@ -1,6 +1,5 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TemplateHaskell #-} module Shpadoinkle.Website.Partials.Nav (view) where @@ -9,7 +8,6 @@ module Shpadoinkle.Website.Partials.Nav (view) where import Prelude hiding (div) import Shpadoinkle (MonadJSM) import Shpadoinkle.Html as H -import Shpadoinkle.Html.TH.AssetLink import Shpadoinkle.Website.Style as Style import Shpadoinkle.Website.Types.Frontend import Shpadoinkle.Website.Types.Nav @@ -28,7 +26,7 @@ toActive = \case navLinkDesktop :: MonadJSM m => Frontend -> Nav -> Html m a navLinkDesktop fe n = li [ class' nav__link ] $ a [ onClickM_ $ goTo n ] [ text $ humanize n ] - : [ img' [ class' nav__link_underline, src $(assetLink "/assets/nav_line.svg") ] | toActive fe == Just n ] + : [ img' [ class' nav__link_underline, src "/assets/nav_line.svg" ] | toActive fe == Just n ] navLinkMobile :: Nav -> Html m a @@ -39,10 +37,10 @@ view :: MonadJSM m => Frontend -> [Html m a] view r = [ H.nav [ class' Style.nav ] [ a [ href "/" ] - [ img' [ class' nav__logo, src $(assetLink "/assets/landing_logo.svg") ] + [ img' [ class' nav__logo, src "/assets/landing_logo.svg" ] ] - , img' [ class' nav__menu_icon, src $(assetLink "/assets/menu_icon.svg") ] - , img' [ class' nav__menu_icon, src $(assetLink "/assets/menu_close_icon.svg") ] + , img' [ class' nav__menu_icon, src "/assets/menu_icon.svg" ] + , img' [ class' nav__menu_icon, src "/assets/menu_close_icon.svg" ] , ul [ class' nav__links ] $ navLinkDesktop r <$> [minBound..maxBound] ] , H.nav [ class' nav_mobile ] diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index 1f39428f..e9692091 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -1,5 +1,4 @@ {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TemplateHaskell #-} module Shpadoinkle.Website.Partials.Template @@ -12,7 +11,6 @@ import Data.Text (Text) import Prelude hiding (div) import Shpadoinkle (MonadJSM) import Shpadoinkle.Html -import Shpadoinkle.Html.TH.AssetLink (assetLink) import Shpadoinkle.Router (toHydration) import Shpadoinkle.Run (Env (..), entrypoint) import qualified Shpadoinkle.Website.Partials.Footer as Footer @@ -35,7 +33,7 @@ headView ev fe = head_ ] , link' [ rel "stylesheet" - , href $(assetLink "/assets/style.css") + , href "/assets/style.css" ] , toHydration fe , h "title" [] [ "Shpadoinkle" ] diff --git a/website/Shpadoinkle/Website/Style.hs b/website/Shpadoinkle/Website/Style.hs index 1000cc63..3454358f 100644 --- a/website/Shpadoinkle/Website/Style.hs +++ b/website/Shpadoinkle/Website/Style.hs @@ -1,11 +1,114 @@ {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TemplateHaskell #-} module Shpadoinkle.Website.Style where -import Shpadoinkle.Html.TH.CSS +import Shpadoinkle.Html +{-# ANN module ("HLint: ignore Use camelCase" :: String) #-} -$(extractNamespace "./assets/style.css") +footer__wrapper + , flex + , flex_col + , justify_between + , w_full + , gap_8 + , pt_12 + , leading_8 + , footer__nav__category + , footer__nav__links + , footer_copyright + , hero_title + , hero + , hero_title_wrapper + , hero_button + , split_button + , hero_button__content + , split_button__button + , split_button__split1 + , split_button__split2 + , hero_bg + , hero_bg_mobile + , feature__card + , feature__card__content + , feature__card__heading + , hero_button__split + , feature__card__title + , feature__card__description + , feature__section + , feature__wrapper + , feature__title + , feature__cards + , feature_button + , feature_button__content + , feature_button__split + , transition + , components__section + , component + , component__info + , component__info__title + , component__example + , component__example__code + , nav + , nav__link + , nav__link_underline + , nav_mobile__link + , nav__logo + , nav__menu_icon + , nav__links + , nav_mobile + , nav_mobile__wrapper + :: ClassList + +footer__wrapper = "footer__wrapper" +flex = "flex" +flex_col = "flex_col" +justify_between = "justify_between" +w_full = "w_full" +gap_8 = "gap_8" +pt_12 = "pt_12" +leading_8 = "leading_8" +footer__nav__category = "footer__nav__category" +footer__nav__links = "footer__nav__links" +footer_copyright = "footer_copyright" +hero_title = "hero_title" +hero = "hero" +hero_title_wrapper = "hero_title_wrapper" +hero_button = "hero_button" +split_button = "split_button" +hero_button__content = "hero_button__content" +split_button__button = "split_button__button" +split_button__split1 = "split_button__split1" +split_button__split2 = "split_button__split2" +hero_bg = "" +hero_bg_mobile = "" +feature__card = "" +feature__card__content = "" +feature__card__heading = "" +hero_button__split = "" +feature__card__title = "" +feature__card__description = "" +feature__section = "" +feature__wrapper = "" +feature__title = "" +feature__cards = "" +feature_button = "" +feature_button__content = "" +feature_button__split = "" +transition = "" +components__section = "" +component = "" +component__info = "" +component__info__title = "" +component__example = "" +component__example__code = "" +nav = "" +nav__link = "" +nav__link_underline = "" +nav_mobile__link = "" +nav__logo = "" +nav__menu_icon = "" +nav__links = "" +nav_mobile = "" +nav_mobile__wrapper = "" diff --git a/website/Shpadoinkle/Website/Types/Home.hs b/website/Shpadoinkle/Website/Types/Home.hs index 2ae1eaf6..c2b3f04f 100644 --- a/website/Shpadoinkle/Website/Types/Home.hs +++ b/website/Shpadoinkle/Website/Types/Home.hs @@ -2,7 +2,7 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE OverloadedLabels #-} -{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE OverloadedStrings #-} module Shpadoinkle.Website.Types.Home where @@ -10,8 +10,6 @@ module Shpadoinkle.Website.Types.Home where import Control.Lens ((^.)) import Data.Aeson (FromJSON, ToJSON) -import Data.ByteString.Lazy (fromStrict) -import Data.FileEmbed (embedFile) import Data.Generics.Labels () import GHC.Generics (Generic) import Shpadoinkle (NFData) @@ -49,8 +47,8 @@ emptyHome st = Home mempty $ Examples helloWorldExample :: Code -helloWorldExample = Code $ fromStrict $(embedFile "./hello-world.example") +helloWorldExample = Code "" counterExample :: Code -counterExample = Code $ fromStrict $(embedFile "./counter.example") +counterExample = Code "" -- GitLab From f87c7d7e862d0fc460c492afff100896a14cd794 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Tue, 13 Apr 2021 20:42:36 -0600 Subject: [PATCH 034/116] compiled --- website/Shpadoinkle/Website/Style.hs | 111 +-------------------------- 1 file changed, 4 insertions(+), 107 deletions(-) diff --git a/website/Shpadoinkle/Website/Style.hs b/website/Shpadoinkle/Website/Style.hs index 3454358f..2856e63f 100644 --- a/website/Shpadoinkle/Website/Style.hs +++ b/website/Shpadoinkle/Website/Style.hs @@ -1,114 +1,11 @@ {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} -module Shpadoinkle.Website.Style where +module Shpadoinkle.Marketing.Style where -import Shpadoinkle.Html +import Shpadoinkle.Html.TH.CSS -{-# ANN module ("HLint: ignore Use camelCase" :: String) #-} -footer__wrapper - , flex - , flex_col - , justify_between - , w_full - , gap_8 - , pt_12 - , leading_8 - , footer__nav__category - , footer__nav__links - , footer_copyright - , hero_title - , hero - , hero_title_wrapper - , hero_button - , split_button - , hero_button__content - , split_button__button - , split_button__split1 - , split_button__split2 - , hero_bg - , hero_bg_mobile - , feature__card - , feature__card__content - , feature__card__heading - , hero_button__split - , feature__card__title - , feature__card__description - , feature__section - , feature__wrapper - , feature__title - , feature__cards - , feature_button - , feature_button__content - , feature_button__split - , transition - , components__section - , component - , component__info - , component__info__title - , component__example - , component__example__code - , nav - , nav__link - , nav__link_underline - , nav_mobile__link - , nav__logo - , nav__menu_icon - , nav__links - , nav_mobile - , nav_mobile__wrapper - :: ClassList - -footer__wrapper = "footer__wrapper" -flex = "flex" -flex_col = "flex_col" -justify_between = "justify_between" -w_full = "w_full" -gap_8 = "gap_8" -pt_12 = "pt_12" -leading_8 = "leading_8" -footer__nav__category = "footer__nav__category" -footer__nav__links = "footer__nav__links" -footer_copyright = "footer_copyright" -hero_title = "hero_title" -hero = "hero" -hero_title_wrapper = "hero_title_wrapper" -hero_button = "hero_button" -split_button = "split_button" -hero_button__content = "hero_button__content" -split_button__button = "split_button__button" -split_button__split1 = "split_button__split1" -split_button__split2 = "split_button__split2" -hero_bg = "" -hero_bg_mobile = "" -feature__card = "" -feature__card__content = "" -feature__card__heading = "" -hero_button__split = "" -feature__card__title = "" -feature__card__description = "" -feature__section = "" -feature__wrapper = "" -feature__title = "" -feature__cards = "" -feature_button = "" -feature_button__content = "" -feature_button__split = "" -transition = "" -components__section = "" -component = "" -component__info = "" -component__info__title = "" -component__example = "" -component__example__code = "" -nav = "" -nav__link = "" -nav__link_underline = "" -nav_mobile__link = "" -nav__logo = "" -nav__menu_icon = "" -nav__links = "" -nav_mobile = "" -nav_mobile__wrapper = "" +$(extractNamespace "./static/style.css") -- GitLab From 7668c6b4234806a63c6cf30b01df072dd9acc11d Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Wed, 14 Apr 2021 10:07:14 -0600 Subject: [PATCH 035/116] stupidness --- html/Shpadoinkle/Html/TH/AssetLink.hs | 2 +- nix/overlay-shpadoinkle.nix | 11 +++++- template/Shpadoinkle/Template/TH.hs | 35 ++++++++++++++---- template/package.yaml | 3 +- .../Shpadoinkle/Website/Page/Documentation.hs | 37 ++++++++++--------- website/Shpadoinkle/Website/Page/Home.hs | 24 ++++++------ .../Shpadoinkle/Website/Partials/Footer.hs | 4 +- website/Shpadoinkle/Website/Partials/Nav.hs | 10 +++-- .../Shpadoinkle/Website/Partials/Template.hs | 4 +- website/Shpadoinkle/Website/Style.hs | 4 +- 10 files changed, 88 insertions(+), 46 deletions(-) diff --git a/html/Shpadoinkle/Html/TH/AssetLink.hs b/html/Shpadoinkle/Html/TH/AssetLink.hs index f89a74ee..5bb96d9c 100644 --- a/html/Shpadoinkle/Html/TH/AssetLink.hs +++ b/html/Shpadoinkle/Html/TH/AssetLink.hs @@ -24,4 +24,4 @@ assetLinkWithBase base fp' = runIO $ do out@(exit, hash, _) <- readProcessWithExitCode "sha1sum" ["." <> fp] "" case exit of ExitSuccess -> return . LitE . StringL $ fp <> "?_=" <> take 20 hash - ExitFailure _ -> error $ show out + ExitFailure _ -> fail $ show out diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index 18d5fc28..901a5c90 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -137,7 +137,16 @@ in { }) {}).google-chrome; haskell = super.haskell // - { packages = super.haskell.packages // + { # compiler = super.haskell.compiler // { + # ghcjs86 = super.haskell.compiler.ghcjs86.overrideAttrs (old: { + # buildPhase = '' + # echo "HERE\n\n\n\n" + # cp ${../thrunner.js} lib/boot/data/thrunner.js + # ${old.buildPhase} + # ''; + # }); + # }; + packages = super.haskell.packages // { "${util.compilerjs}" = with super.haskell.lib; super.haskell.packages.${util.compilerjs}.override (old: { diff --git a/template/Shpadoinkle/Template/TH.hs b/template/Shpadoinkle/Template/TH.hs index 2a858a88..2d8d10e5 100644 --- a/template/Shpadoinkle/Template/TH.hs +++ b/template/Shpadoinkle/Template/TH.hs @@ -5,26 +5,47 @@ module Shpadoinkle.Template.TH where +import Control.Monad (unless, when) import Data.Text (Text, cons, pack, replace, unpack) import Data.Text.IO import Language.Haskell.TH.Syntax import Prelude hiding (readFile) +import System.Directory (doesFileExist, removeFile) import System.Exit (ExitCode (..)) -import System.Process.Text (readProcessWithExitCode) +import System.Process (proc, + readCreateProcessWithExitCode) import Text.HTML.Parser (Attr (..), Token (..), parseTokens) +data CleanUp = CleanUp | LeaveFile deriving Eq + + embedAsciidoc :: FilePath -> Q Exp -embedAsciidoc path = do - out@(exit, _, _) <- runIO $ readProcessWithExitCode "asciidoctor" ["-s", path ] "" +embedAsciidoc asciiPath = do + let htmlPath = unpack $ replace ".adoc" ".html" $ pack asciiPath + out@(exit, _, _) <- runIO $ do + doesAscii <- doesFileExist asciiPath + _ <- unless doesAscii . fail $ "Document not found at " <> asciiPath + doesHtml <- doesFileExist htmlPath + when doesHtml $ removeFile htmlPath + readCreateProcessWithExitCode (proc "asciidoctor" [ "-s", asciiPath ]) "" case exit of - ExitSuccess -> embedHtml $ unpack $ replace ".adoc" ".html" $ pack path - ExitFailure _ -> error $ show out + ExitSuccess -> embedHtml' CleanUp htmlPath + ExitFailure _ -> fail $ show out embedHtml :: FilePath -> Q Exp -embedHtml path = do - ts <- runIO $ parseTokens <$> readFile path +embedHtml = embedHtml' LeaveFile + + +embedHtml' :: CleanUp -> FilePath -> Q Exp +embedHtml' clean htmlPath = do + ts <- runIO $ do + doesHtml <- doesFileExist htmlPath + _ <- unless doesHtml . fail $ "Html not found at " <> htmlPath + ts' <- parseTokens <$> readFile htmlPath + when (clean == CleanUp) $ removeFile htmlPath + return ts' pure . ListE $ tokenToExp ts diff --git a/template/package.yaml b/template/package.yaml index 139d790a..2a2a072c 100644 --- a/template/package.yaml +++ b/template/package.yaml @@ -32,7 +32,8 @@ dependencies: - text >= 1.2.3 && < 1.3 - template-haskell - html-parse - - process-extras + - process + - directory - Shpadoinkle - Shpadoinkle-backend-static diff --git a/website/Shpadoinkle/Website/Page/Documentation.hs b/website/Shpadoinkle/Website/Page/Documentation.hs index a8dbdfae..d6b5845c 100644 --- a/website/Shpadoinkle/Website/Page/Documentation.hs +++ b/website/Shpadoinkle/Website/Page/Documentation.hs @@ -1,10 +1,13 @@ -{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE TemplateHaskell #-} module Shpadoinkle.Website.Page.Documentation where +import Data.Text import Shpadoinkle.Html +import Shpadoinkle.Template.TH import qualified Shpadoinkle.Website.Types.Routes.GettingStarted as GettingStarted import qualified Shpadoinkle.Website.Types.Routes.Packages as Packages import qualified Shpadoinkle.Website.Types.Routes.Tutorial as Tutorial @@ -14,22 +17,22 @@ concepts , gsIndex, gsAddingToYourProject, gsExtendAnExample , rpIndex, rpCore, rpConsole, rpBackends, rpHtml, rpLens, rpRouter, rpWidgets , rtIndex, rtCalculator, rtImmediateExecution, rtComposing :: Html m a -concepts = div_ [] -gsIndex = div_ [] -gsAddingToYourProject = div_ [] -gsExtendAnExample = div_ [] -rpIndex = div_ [] -rpCore = div_ [] -rpConsole = div_ [] -rpBackends = div_ [] -rpHtml = div_ [] -rpLens = div_ [] -rpRouter = div_ [] -rpWidgets = div_ [] -rtIndex = div_ [] -rtCalculator = div_ [] -rtImmediateExecution = div_ [] -rtComposing = div_ [] +concepts = div_ [] -- $(embedAsciidoc "./docs/concepts.adoc") +gsIndex = div_ $(embedAsciidoc "./docs/getting-started/index.adoc") +gsAddingToYourProject = div_ [] -- $(embedAsciidoc "./docs/getting-started/adding-to-your-project.adoc") +gsExtendAnExample = div_ [] -- $(embedAsciidoc "./docs/getting-started/extend-an-example.adoc") +rpIndex = div_ [] -- $(embedAsciidoc "./docs/packages/index.adoc") +rpCore = div_ [] -- $(embedAsciidoc "./docs/packages/core.adoc") +rpConsole = div_ [] -- $(embedAsciidoc "./docs/packages/console.adoc") +rpBackends = div_ [] -- $(embedAsciidoc "./docs/packages/backends.adoc") +rpHtml = div_ [] -- $(embedAsciidoc "./docs/packages/html.adoc") +rpLens = div_ [] -- $(embedAsciidoc "./docs/packages/lens.adoc") +rpRouter = div_ [] -- $(embedAsciidoc "./docs/packages/router.adoc") +rpWidgets = div_ [] -- $(embedAsciidoc "./docs/packages/widgets.adoc") +rtIndex = div_ [] -- $(embedAsciidoc "./docs/tutorial/index.adoc") +rtCalculator = div_ [] -- $(embedAsciidoc "./docs/tutorial/calculator.adoc") +rtImmediateExecution = div_ [] -- $(embedAsciidoc "./docs/tutorial/immediate-execution.adoc") +rtComposing = div_ [] -- $(embedAsciidoc "./docs/tutorial/composing.adoc") gettingStarted :: GettingStarted.Route -> Html m a diff --git a/website/Shpadoinkle/Website/Page/Home.hs b/website/Shpadoinkle/Website/Page/Home.hs index 594e49e5..c429d4c8 100644 --- a/website/Shpadoinkle/Website/Page/Home.hs +++ b/website/Shpadoinkle/Website/Page/Home.hs @@ -1,5 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} @@ -10,6 +11,7 @@ import Data.Text (Text) import Prelude hiding (div, span) import Shpadoinkle.Html as H +import Shpadoinkle.Html.TH.AssetLink (assetLink) import Shpadoinkle.Router (MonadJSM, navigate) import Shpadoinkle.Website.Style as Style @@ -41,14 +43,14 @@ hero' = div_ [ span [ class' $ split_button__split1 <> hero_button__split ] [ "Get Started" ] , span [ class' $ split_button__split2 <> hero_button__split ] - [ img' [ alt "Right arrow", src "/assets/right-arrow-white.svg" ] + [ img' [ alt "Right arrow", src $(assetLink "/assets/right-arrow-white.svg") ] ] ] ] ] ] - , img' [ alt "desert cactus", class' hero_bg, src "/assets/hero_image.svg" ] - , img' [ alt "desert cactus", class' hero_bg_mobile, src "/assets/mobile/hero_image.svg" ] + , img' [ alt "desert cactus", class' hero_bg, src $(assetLink "/assets/hero_image.svg") ] + , img' [ alt "desert cactus", class' hero_bg_mobile, src $(assetLink "/assets/mobile/hero_image.svg") ] ] @@ -80,27 +82,27 @@ featureSection = div [ class' feature__section ] [ "A programming model for declarative, high performance user interface." ] , div [ class' feature__cards ] [ featureCard $ FeatureCard - { card'icon = "/assets/fast_icon.svg" + { card'icon = $(assetLink "/assets/fast_icon.svg") , card'title = "Fast" , card'description = "Shpadoinkle does little work. The renderer is modular, so you can always benefit from the latest advances in virtual DOM rendering." } , featureCard $ FeatureCard - { card'icon = "/assets/declarative_icon.svg" + { card'icon = $(assetLink "/assets/declarative_icon.svg") , card'title = "Declarative" , card'description = "Your Shpadoinkle code is high-level. You need not worry about low-level details, causality, or when DOM nodes get replaced." } , featureCard $ FeatureCard - { card'icon = "/assets/composable_icon.svg" + { card'icon = $(assetLink "/assets/composable_icon.svg") , card'title = "Composable" , card'description = "Shpadoinkle avoids elaborate passing of messages and payloads. Components are highly composable with Lenses" } , featureCard $ FeatureCard - { card'icon = "/assets/reliable_icon.svg" + { card'icon = $(assetLink "/assets/reliable_icon.svg") , card'title = "Reliable" , card'description = "Shpadoinkle UIs are composed of components with no side-effects. Runtime errors are exceedingly rare. Code is easy to test because model updates are pure functions." } , featureCard $ FeatureCard - { card'icon = "/assets/lorem_ipsum_logo.svg" + { card'icon = $(assetLink "/assets/lorem_ipsum_logo.svg") , card'title = "Type Safe" , card'description = "Shpadoinkle facilities type safe UI code. Everything from client server communication with Servant, to compile time checked asset paths, is designed with types in mind." } @@ -112,12 +114,12 @@ featureSection = div [ class' feature__section ] [ span [ class' $ split_button__split1 <> feature_button__split ] [ "Checkout the getting started guide" ] , span [ class' $ split_button__split2 <> feature_button__split ] - [ img' [ alt "Right arrow", src "/assets/right-arrow.svg" ] + [ img' [ alt "Right arrow", src $(assetLink "/assets/right-arrow.svg") ] ] ] ] ] - , img' [ alt "background transition", class' transition, src "/assets/orange_purple_transition.svg" ] + , img' [ alt "background transition", class' transition, src $(assetLink "/assets/orange_purple_transition.svg") ] ] @@ -156,5 +158,5 @@ componentsSection = div [ class' components__section ] [ div' "component__example__code" ] ] - , img' [ alt "background transition", className "transition", src "/assets/purple_black_transition.svg" ] + , img' [ alt "background transition", className "transition", src $(assetLink "/assets/purple_black_transition.svg") ] ] diff --git a/website/Shpadoinkle/Website/Partials/Footer.hs b/website/Shpadoinkle/Website/Partials/Footer.hs index 7c651f02..0fea4b0b 100644 --- a/website/Shpadoinkle/Website/Partials/Footer.hs +++ b/website/Shpadoinkle/Website/Partials/Footer.hs @@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} module Shpadoinkle.Website.Partials.Footer (view) where @@ -8,6 +9,7 @@ import Prelude hiding (div) import Shpadoinkle.Html (Html, a, alt, class', div, div_, footer_, href, img', li_, nav_, newTab, p, src, text, ul) +import Shpadoinkle.Html.TH.AssetLink (assetLink) import Shpadoinkle.Website.Style as Style import Shpadoinkle.Website.Types.Nav (Nav) import Shpadoinkle.Widgets.Types (humanize) @@ -25,7 +27,7 @@ view = footer_ [ div [ class' footer__wrapper ] [ a [ href "/" ] - [ img' [ alt "shpadoinkle logo", src "/assets/try_shpadoinkle_footer_logo.svg" ] + [ img' [ alt "shpadoinkle logo", src $(assetLink "/assets/try_shpadoinkle_footer_logo.svg") ] ] , div [ class' $ flex <> flex_col <> justify_between <> w_full <> gap_8 <> pt_12 <> leading_8 <> "md:flex-row" <> "md:w-2/5" <> "md:gap-0" ] [ div_ diff --git a/website/Shpadoinkle/Website/Partials/Nav.hs b/website/Shpadoinkle/Website/Partials/Nav.hs index b53d88d9..178a182b 100644 --- a/website/Shpadoinkle/Website/Partials/Nav.hs +++ b/website/Shpadoinkle/Website/Partials/Nav.hs @@ -1,5 +1,6 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} module Shpadoinkle.Website.Partials.Nav (view) where @@ -8,6 +9,7 @@ module Shpadoinkle.Website.Partials.Nav (view) where import Prelude hiding (div) import Shpadoinkle (MonadJSM) import Shpadoinkle.Html as H +import Shpadoinkle.Html.TH.AssetLink import Shpadoinkle.Website.Style as Style import Shpadoinkle.Website.Types.Frontend import Shpadoinkle.Website.Types.Nav @@ -26,7 +28,7 @@ toActive = \case navLinkDesktop :: MonadJSM m => Frontend -> Nav -> Html m a navLinkDesktop fe n = li [ class' nav__link ] $ a [ onClickM_ $ goTo n ] [ text $ humanize n ] - : [ img' [ class' nav__link_underline, src "/assets/nav_line.svg" ] | toActive fe == Just n ] + : [ img' [ class' nav__link_underline, src $(assetLink "/assets/nav_line.svg") ] | toActive fe == Just n ] navLinkMobile :: Nav -> Html m a @@ -37,10 +39,10 @@ view :: MonadJSM m => Frontend -> [Html m a] view r = [ H.nav [ class' Style.nav ] [ a [ href "/" ] - [ img' [ class' nav__logo, src "/assets/landing_logo.svg" ] + [ img' [ class' nav__logo, src $(assetLink "/assets/landing_logo.svg") ] ] - , img' [ class' nav__menu_icon, src "/assets/menu_icon.svg" ] - , img' [ class' nav__menu_icon, src "/assets/menu_close_icon.svg" ] + , img' [ class' nav__menu_icon, src $(assetLink "/assets/menu_icon.svg") ] + , img' [ class' nav__menu_icon, src $(assetLink "/assets/menu_close_icon.svg") ] , ul [ class' nav__links ] $ navLinkDesktop r <$> [minBound..maxBound] ] , H.nav [ class' nav_mobile ] diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index e9692091..1f39428f 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} module Shpadoinkle.Website.Partials.Template @@ -11,6 +12,7 @@ import Data.Text (Text) import Prelude hiding (div) import Shpadoinkle (MonadJSM) import Shpadoinkle.Html +import Shpadoinkle.Html.TH.AssetLink (assetLink) import Shpadoinkle.Router (toHydration) import Shpadoinkle.Run (Env (..), entrypoint) import qualified Shpadoinkle.Website.Partials.Footer as Footer @@ -33,7 +35,7 @@ headView ev fe = head_ ] , link' [ rel "stylesheet" - , href "/assets/style.css" + , href $(assetLink "/assets/style.css") ] , toHydration fe , h "title" [] [ "Shpadoinkle" ] diff --git a/website/Shpadoinkle/Website/Style.hs b/website/Shpadoinkle/Website/Style.hs index 2856e63f..1000cc63 100644 --- a/website/Shpadoinkle/Website/Style.hs +++ b/website/Shpadoinkle/Website/Style.hs @@ -2,10 +2,10 @@ {-# LANGUAGE TemplateHaskell #-} -module Shpadoinkle.Marketing.Style where +module Shpadoinkle.Website.Style where import Shpadoinkle.Html.TH.CSS -$(extractNamespace "./static/style.css") +$(extractNamespace "./assets/style.css") -- GitLab From b2f3a53a0298ba080175e622fe9ac19997a9e589 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Wed, 14 Apr 2021 10:10:15 -0600 Subject: [PATCH 036/116] leave ghcjs hack comment around --- nix/overlay-shpadoinkle.nix | 2 -- 1 file changed, 2 deletions(-) diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index 901a5c90..2bcacfcb 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -140,8 +140,6 @@ in { { # compiler = super.haskell.compiler // { # ghcjs86 = super.haskell.compiler.ghcjs86.overrideAttrs (old: { # buildPhase = '' - # echo "HERE\n\n\n\n" - # cp ${../thrunner.js} lib/boot/data/thrunner.js # ${old.buildPhase} # ''; # }); -- GitLab From 59b6e4c2ed08a3f4e801f2cf3dc1c4e198a1002d Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Wed, 14 Apr 2021 10:19:17 -0600 Subject: [PATCH 037/116] yay things work for prod build --- website/Shpadoinkle/Website/Disembodied.hs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/website/Shpadoinkle/Website/Disembodied.hs b/website/Shpadoinkle/Website/Disembodied.hs index 98e3f16e..942165ac 100644 --- a/website/Shpadoinkle/Website/Disembodied.hs +++ b/website/Shpadoinkle/Website/Disembodied.hs @@ -5,7 +5,6 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -fno-warn-missing-methods #-} @@ -23,9 +22,11 @@ import Shpadoinkle.Disembodied (Disembodied (SiteS writeSite) import Shpadoinkle.Isreal.Types (Code) import Shpadoinkle.Router (traverseUnions) +import Shpadoinkle.Run (Env (Prod)) +import Shpadoinkle.Website.Partials.Template (template) import Shpadoinkle.Website.Types.Effects.Hooglable (Hooglable) import Shpadoinkle.Website.Types.Effects.Swan (Swan) -import Shpadoinkle.Website.Types.Routes +import Shpadoinkle.Website.Types.Routes (SPA, routes) import Shpadoinkle.Website.View as View import System.Environment (getArgs) @@ -38,7 +39,7 @@ newtype Noop a = Noop (JSM a) getStarts :: MonadJSM m => IO (SiteSpec (SPA m)) -getStarts = traverseUnions (fmap view . start) routes +getStarts = traverseUnions (fmap (\fe -> template Prod fe $ view fe) . start) routes main :: IO () -- GitLab From 2e265bdba8e37abc133760be5742aa61c716c36e Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 15 Apr 2021 14:36:39 -0600 Subject: [PATCH 038/116] home page is pretty much there, with examples and copy --- html/Shpadoinkle/Html/TH/CSS.hs | 10 +- html/Shpadoinkle/Html/TH/CSSTest.hs | 1 + html/sample.css | 4 + nix/base.nix | 6 +- .../Website/Component/LiveExample.hs | 24 +-- website/Shpadoinkle/Website/Disembodied.hs | 8 +- website/Shpadoinkle/Website/Page/Home.hs | 148 ++++++++++-------- .../Shpadoinkle/Website/Partials/Footer.hs | 14 +- .../Shpadoinkle/Website/Partials/Template.hs | 4 + website/Shpadoinkle/Website/Run.hs | 7 +- .../Website/Types/Effects/Example.hs | 7 +- website/Shpadoinkle/Website/Types/Home.hs | 26 +-- website/Shpadoinkle/Website/View.hs | 32 ++-- website/assets/style.css | 8 + website/example.template.top | 4 +- website/todo.example | 21 +++ 16 files changed, 207 insertions(+), 117 deletions(-) create mode 100644 website/todo.example diff --git a/html/Shpadoinkle/Html/TH/CSS.hs b/html/Shpadoinkle/Html/TH/CSS.hs index 7222e192..535172a8 100644 --- a/html/Shpadoinkle/Html/TH/CSS.hs +++ b/html/Shpadoinkle/Html/TH/CSS.hs @@ -18,6 +18,7 @@ module Shpadoinkle.Html.TH.CSS import Control.Compactable import Data.ByteString.Lazy as BS (ByteString) import qualified Data.ByteString.Lazy.Char8 as BS +import Data.Char (toLower) import Data.Containers.ListUtils (nubOrd) import qualified Data.Set as Set import Data.String (IsString) @@ -108,12 +109,17 @@ toClassDec n' = let sanitize :: String -> String -sanitize = (=<<) $ \case +sanitize = lowerFirst . (=<<) (\case '/' -> "''" '-' -> "_" ':' -> "'" '>' -> "GT" - x -> pure x + x -> pure x) + + +lowerFirst :: String -> String +lowerFirst (x:xs) = toLower x : xs +lowerFirst x = x selectors :: IsString s => s diff --git a/html/Shpadoinkle/Html/TH/CSSTest.hs b/html/Shpadoinkle/Html/TH/CSSTest.hs index 997ad10b..21858f31 100644 --- a/html/Shpadoinkle/Html/TH/CSSTest.hs +++ b/html/Shpadoinkle/Html/TH/CSSTest.hs @@ -8,6 +8,7 @@ module Shpadoinkle.Html.TH.CSSTest , bar , foo , txt_rt + , woah ) where diff --git a/html/sample.css b/html/sample.css index 58348984..880fd96e 100644 --- a/html/sample.css +++ b/html/sample.css @@ -10,6 +10,10 @@ background: #123233; } +.Woah { + height: auto; +} + @media print (min-width:200px){ .bar{ width: #EFEFEF; diff --git a/nix/base.nix b/nix/base.nix index e20affd0..5283e725 100644 --- a/nix/base.nix +++ b/nix/base.nix @@ -28,7 +28,7 @@ let ghcTools = with haskell.packages.${compiler}; - [ cabal-install ghcid hpack pkgs.stylish-haskell pkgs.hlint ]; + [ easy-hls cabal-install ghcid hpack pkgs.stylish-haskell pkgs.hlint ]; cannibal = if optimizeJS then util.doCannibalize else id; @@ -37,8 +37,8 @@ let easy-hls = pkgs.callPackage (pkgs.fetchFromGitHub { owner = "jkachmar"; repo = "easy-hls-nix"; - rev = "9338014a947276812e5aa8ea570e48e01909c8b7"; - sha256 = "1v85p85c68vcazhzx9yzbc06aymy81riz42lxry67gfysdsm9dqh"; + rev = "291cf77f512a7121bb6801cde35ee1e8b7287f91"; + sha256 = "1bvbcp9zwmh53sm16ycp8phhc6vzc72a71sf0bvyjgfbn6zp68bc"; }) { ghcVersions = [ "8.6.5" ]; }; diff --git a/website/Shpadoinkle/Website/Component/LiveExample.hs b/website/Shpadoinkle/Website/Component/LiveExample.hs index 936cd331..b3831fb8 100644 --- a/website/Shpadoinkle/Website/Component/LiveExample.hs +++ b/website/Shpadoinkle/Website/Component/LiveExample.hs @@ -2,6 +2,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-} @@ -11,7 +12,7 @@ module Shpadoinkle.Website.Component.LiveExample where import Control.Lens ((%~), (.~), (^.)) -import Control.Monad.Reader (MonadReader (ask)) +import Control.Monad.Reader (asks) import Data.ByteString.Lazy as BS (fromStrict) import Data.FileEmbed (embedFile) import Data.Generics.Labels () @@ -49,17 +50,18 @@ import Shpadoinkle.Website.Types.Effects.Example (ExampleEffects, Swan (..)) import Shpadoinkle.Website.Types.Example (Example (..)) import Shpadoinkle.Website.Types.ExampleState (ExampleState (..)) +import Shpadoinkle.Website.Types.Home (ExampleLens) default (Text) -example :: (MonadJSM m, ExampleEffects m) => Example -> Html m Example -example (Example cc token nonce state') = div "example" +example :: (MonadJSM m, ExampleEffects m) => ExampleLens -> Example -> Html m Example +example lmutex (Example cc token nonce state') = div "example" [ div [ className "mirror-wrap" , onInput $ const $ #state .~ ELoading ] - [ mapC (mappend compileExample . onRecord #inputHaskell) $ mirror cc ] + [ mapC (mappend (compileExample lmutex) . onRecord #inputHaskell) $ mirror cc ] , case state' of EReady -> iframe [ src $ "https://isreal.shpadoinkle.org/" @@ -87,9 +89,9 @@ topOffset = subtract 1 $ BS.fromStrict $(embedFile "./example.template.top") -compileExample :: (MonadJSM m, ExampleEffects m) => Continuation m Example -compileExample = kleisli $ \(Example cc token nonce _) -> do - mutex <- ask +compileExample :: (MonadJSM m, ExampleEffects m) => ExampleLens -> Continuation m Example +compileExample lmutex = kleisli $ \(Example cc token nonce _) -> do + mutex <- asks (^. lmutex) cur <- readTVarIO mutex atomically $ writeTVar mutex $ Just cc case cur of @@ -107,10 +109,9 @@ compileExample = kleisli $ \(Example cc token nonce _) -> do res' <- compile token (nonce + 1) $ exampleTemplate cc' ret res' 2 where - ret res (n :: SnowNonce) = - return . pur $ (#snowNonce %~ (+ n)) . case res of - Left e -> #state .~ EError e - Right _ -> #state .~ EReady + ret res (n :: SnowNonce) = return . pur $ (#snowNonce %~ (+ n)) . case res of + Left e -> #state .~ EError e + Right _ -> #state .~ EReady errorMessages :: CompileError -> Html m a @@ -130,6 +131,7 @@ mirrorCfg :: Code -> JSM Object mirrorCfg (Code cc) = do cfg <- obj (cfg <# "mode") "haskell" + (cfg <# "theme") "darcula" (cfg <# "value") $ toStrict $ decodeUtf8 cc (cfg <# "indentUnit") (4 :: Int) (cfg <# "matchBrackets") True diff --git a/website/Shpadoinkle/Website/Disembodied.hs b/website/Shpadoinkle/Website/Disembodied.hs index 942165ac..1bec70e1 100644 --- a/website/Shpadoinkle/Website/Disembodied.hs +++ b/website/Shpadoinkle/Website/Disembodied.hs @@ -24,8 +24,10 @@ import Shpadoinkle.Isreal.Types (Code) import Shpadoinkle.Router (traverseUnions) import Shpadoinkle.Run (Env (Prod)) import Shpadoinkle.Website.Partials.Template (template) +import Shpadoinkle.Website.Types.Effects.Example (ExampleEffects) import Shpadoinkle.Website.Types.Effects.Hooglable (Hooglable) import Shpadoinkle.Website.Types.Effects.Swan (Swan) +import Shpadoinkle.Website.Types.Home (Examples) import Shpadoinkle.Website.Types.Routes (SPA, routes) import Shpadoinkle.Website.View as View import System.Environment (getArgs) @@ -33,12 +35,10 @@ import System.Environment (getArgs) newtype Noop a = Noop (JSM a) deriving newtype (Functor, Applicative, Monad, MonadJSM, MonadIO) - deriving anyclass (Hooglable, Swan, MonadReader (TVar (Maybe Code))) + deriving anyclass (Hooglable, Swan, MonadReader (Examples (TVar (Maybe Code)))) -getStarts - :: MonadJSM m - => IO (SiteSpec (SPA m)) +getStarts :: (MonadJSM m, ExampleEffects m) => IO (SiteSpec (SPA m)) getStarts = traverseUnions (fmap (\fe -> template Prod fe $ view fe) . start) routes diff --git a/website/Shpadoinkle/Website/Page/Home.hs b/website/Shpadoinkle/Website/Page/Home.hs index c429d4c8..3b7a86c3 100644 --- a/website/Shpadoinkle/Website/Page/Home.hs +++ b/website/Shpadoinkle/Website/Page/Home.hs @@ -1,4 +1,8 @@ +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} @@ -7,22 +11,33 @@ module Shpadoinkle.Website.Page.Home where +import Control.Lens ((^.)) +import Data.Generics.Labels () import Data.Text (Text) +import GHC.Generics (Generic) import Prelude hiding (div, span) -import Shpadoinkle.Html as H +import Shpadoinkle.Html as H hiding + (title) import Shpadoinkle.Html.TH.AssetLink (assetLink) +import Shpadoinkle.Lens (onRecord, + onRecordEndo) import Shpadoinkle.Router (MonadJSM, navigate) +import Shpadoinkle.Website.Component.LiveExample as Live (example) import Shpadoinkle.Website.Style as Style -import Shpadoinkle.Website.Types.Home (Home) +import Shpadoinkle.Website.Types.Effects.Example (ExampleEffects) +import Shpadoinkle.Website.Types.Example (Example) +import Shpadoinkle.Website.Types.Home (ExampleLens, + Examples, + Home) import Shpadoinkle.Website.Types.Routes (Route (RGettingStarted), SPA) import Shpadoinkle.Website.Types.Routes.GettingStarted (Route (RGSIndex)) -view :: MonadJSM m => Home -> [Html m Home] -view _ = [hero', featureSection, componentsSection] +view :: (MonadJSM m, ExampleEffects m) => Home -> [Html m Home] +view x = [ hero', featureSection, onRecordEndo #examples componentsSection x ] heroTitle :: Text -> Html m a @@ -75,38 +90,42 @@ featureCard fc = div [ class' feature__card ] ] +features :: [FeatureCard] +features = + [ FeatureCard + { card'icon = $(assetLink "/assets/fast_icon.svg") + , card'title = "Fast" + , card'description = "Shpadoinkle does little work. The renderer is modular, so you can always benefit from the latest advances in virtual DOM rendering." + } + , FeatureCard + { card'icon = $(assetLink "/assets/declarative_icon.svg") + , card'title = "Declarative" + , card'description = "Your Shpadoinkle code is high-level. You need not worry about low-level details, causality, or when DOM nodes get replaced." + } + , FeatureCard + { card'icon = $(assetLink "/assets/composable_icon.svg") + , card'title = "Composable" + , card'description = "Shpadoinkle avoids elaborate passing of messages and payloads. Components are highly composable with Lenses" + } + , FeatureCard + { card'icon = $(assetLink "/assets/reliable_icon.svg") + , card'title = "Reliable" + , card'description = "Shpadoinkle UIs are composed of components with no side-effects. Runtime errors are exceedingly rare. Code is easy to test because model updates are pure functions." + } + , FeatureCard + { card'icon = $(assetLink "/assets/lorem_ipsum_logo.svg") + , card'title = "Type Safe" + , card'description = "Shpadoinkle facilities type safe UI code. Everything from client server communication with Servant, to compile time checked asset paths, is designed with types in mind." + } + ] + + featureSection :: forall m a. MonadJSM m => Html m a featureSection = div [ class' feature__section ] [ div [ class' feature__wrapper ] [ p [ class' feature__title ] [ "A programming model for declarative, high performance user interface." ] - , div [ class' feature__cards ] - [ featureCard $ FeatureCard - { card'icon = $(assetLink "/assets/fast_icon.svg") - , card'title = "Fast" - , card'description = "Shpadoinkle does little work. The renderer is modular, so you can always benefit from the latest advances in virtual DOM rendering." - } - , featureCard $ FeatureCard - { card'icon = $(assetLink "/assets/declarative_icon.svg") - , card'title = "Declarative" - , card'description = "Your Shpadoinkle code is high-level. You need not worry about low-level details, causality, or when DOM nodes get replaced." - } - , featureCard $ FeatureCard - { card'icon = $(assetLink "/assets/composable_icon.svg") - , card'title = "Composable" - , card'description = "Shpadoinkle avoids elaborate passing of messages and payloads. Components are highly composable with Lenses" - } - , featureCard $ FeatureCard - { card'icon = $(assetLink "/assets/reliable_icon.svg") - , card'title = "Reliable" - , card'description = "Shpadoinkle UIs are composed of components with no side-effects. Runtime errors are exceedingly rare. Code is easy to test because model updates are pure functions." - } - , featureCard $ FeatureCard - { card'icon = $(assetLink "/assets/lorem_ipsum_logo.svg") - , card'title = "Type Safe" - , card'description = "Shpadoinkle facilities type safe UI code. Everything from client server communication with Servant, to compile time checked asset paths, is designed with types in mind." - } - ] + , div [ class' feature__cards ] $ featureCard <$> features ] , div [ class' feature_button ] [ div [ class' $ split_button <> feature_button__content ] @@ -123,40 +142,41 @@ featureSection = div [ class' feature__section ] ] -componentsSection :: Html m a -componentsSection = div [ class' components__section ] - [ div [ class' component ] - [ div [ class' component__info ] - [ h2 [ class' component__info__title ] - [ "A Component" ] - , p_ - [ "A Shpadoinkle component ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. " ] - ] - , div [ class' component__example ] - [ div' [ class' component__example__code ] - ] - ] - , div "component" - [ div "component__info" - [ h2 "component__info__title" - [ "A Component" ] - , p "component__info__description" - [ text "A Shpadoinkle component ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. " ] - ] - , div "component__example" - [ div' "component__example__code" - ] +data ComponentExample = ComponentExample + { title :: Text + , description :: Text + } deriving Generic + + +componentExample + :: (MonadJSM m, ExampleEffects m) + => ExampleLens -> Examples Example -> ComponentExample -> Html m (Examples Example) +componentExample l exs cex = onRecord l $ div [ class' component ] + [ div [ class' component__info ] + [ h2 [ class' component__info__title ] + [ text $ cex ^. #title ] + , p_ + [ text $ cex ^. #description ] ] - , div "component" - [ div "component__info" - [ h2 "component__info__title" - [ "A Component" ] - , p "component__info__description" - [ "A Shpadoinkle component ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. " ] - ] - , div "component__example" - [ div' "component__example__code" - ] + , div [ class' component__example ] + [ div [ class' component__example__code ] [ example l (exs ^. l) ] ] + ] + + +componentsSection :: (MonadJSM m, ExampleEffects m) => Examples Example -> Html m (Examples Example) +componentsSection exs = div [ class' components__section ] + [ componentExample #helloWorld exs $ ComponentExample + { title = "Hello World" + , description = "Shpadoinkle components are expressed as functions, no classes, inheritance, or JSX style new syntax. Purity of view logic is guaranteed by the type system." + } + , componentExample #counter exs $ ComponentExample + { title = "State is easy" + , description = "Components are simply functions that accept a state argument to render. Event listeners provide updates to the state. No props management, digest cycle, hooks, dispatch, or other such ceremony." + } + , componentExample #todo exs $ ComponentExample + { title = "An Application" + , description = "Using event handlers, and pure functions we can compose applications without any further abstraction." + } , img' [ alt "background transition", className "transition", src $(assetLink "/assets/purple_black_transition.svg") ] ] diff --git a/website/Shpadoinkle/Website/Partials/Footer.hs b/website/Shpadoinkle/Website/Partials/Footer.hs index 0fea4b0b..ade3ea7f 100644 --- a/website/Shpadoinkle/Website/Partials/Footer.hs +++ b/website/Shpadoinkle/Website/Partials/Footer.hs @@ -6,27 +6,29 @@ module Shpadoinkle.Website.Partials.Footer (view) where import Prelude hiding (div) +import Shpadoinkle (MonadJSM) import Shpadoinkle.Html (Html, a, alt, class', div, div_, footer_, href, img', li_, nav_, - newTab, p, src, text, ul) + newTab, onClickM_, p, src, text, + ul) import Shpadoinkle.Html.TH.AssetLink (assetLink) import Shpadoinkle.Website.Style as Style -import Shpadoinkle.Website.Types.Nav (Nav) +import Shpadoinkle.Website.Types.Nav (Nav (NHome), goTo) import Shpadoinkle.Widgets.Types (humanize) -footerLink :: Nav -> Html m a +footerLink :: MonadJSM m => Nav -> Html m a footerLink n = li_ - [ a [ href "/" ] + [ a [ onClickM_ $ goTo n ] [ text $ humanize n ] ] -view :: Html m a +view :: MonadJSM m => Html m a view = footer_ [ div [ class' footer__wrapper ] - [ a [ href "/" ] + [ a [ onClickM_ $ goTo NHome ] [ img' [ alt "shpadoinkle logo", src $(assetLink "/assets/try_shpadoinkle_footer_logo.svg") ] ] , div [ class' $ flex <> flex_col <> justify_between <> w_full <> gap_8 <> pt_12 <> leading_8 <> "md:flex-row" <> "md:w-2/5" <> "md:gap-0" ] diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index 1f39428f..97542186 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -33,6 +33,10 @@ headView ev fe = head_ [ rel "stylesheet" , href $ codemirrorCDN "codemirror.min.css" ] + , link' + [ rel "stylesheet" + , href $ codemirrorCDN "theme/darcula.min.css" + ] , link' [ rel "stylesheet" , href $(assetLink "/assets/style.css") diff --git a/website/Shpadoinkle/Website/Run.hs b/website/Shpadoinkle/Website/Run.hs index 0c02116b..1b378d86 100644 --- a/website/Shpadoinkle/Website/Run.hs +++ b/website/Shpadoinkle/Website/Run.hs @@ -61,6 +61,7 @@ import Shpadoinkle.DeveloperTools import Shpadoinkle.Website.Types.Effects.Hooglable (Hooglable (..)) import Shpadoinkle.Website.Types.Effects.Swan (Swan (..)) import Shpadoinkle.Website.Types.Frontend (Frontend) +import Shpadoinkle.Website.Types.Home (Examples (..)) import Shpadoinkle.Website.Types.Hoogle.API as Hoogle import Shpadoinkle.Website.Types.Hoogle.Target (Target) import Shpadoinkle.Website.Types.Routes (Route (RFourOhFour), @@ -75,8 +76,8 @@ import Shpadoinkle.Website.View (start, startJS, #endif -newtype App a = App { runApp :: ReaderT (TVar (Maybe Code)) JSM a } - deriving (Functor, Applicative, Monad, MonadIO, MonadThrow, MonadReader (TVar (Maybe Code))) +newtype App a = App { runApp :: ReaderT (Examples (TVar (Maybe Code))) JSM a } + deriving (Functor, Applicative, Monad, MonadIO, MonadThrow, MonadReader (Examples (TVar (Maybe Code)))) #ifndef ghcjs_HOST_OS deriving (MonadJSM) #endif @@ -113,7 +114,7 @@ findTargetsM (Search s) = client (Proxy @ HoogleAPI) (Just "json") (Just s) (Jus app :: JSM () app = do - mutex <- newTVarIO Nothing + mutex <- Examples <$> newTVarIO Nothing <*> newTVarIO Nothing <*> newTVarIO Nothing model :: TVar Frontend <- newTVarIO =<< runReaderT (runApp (start RFourOhFour)) mutex withDeveloperTools model fullPageSPA' @(SPA JSM) (flip runReaderT mutex . runApp) runSnabbdom model (withHydration startJS) view stage diff --git a/website/Shpadoinkle/Website/Types/Effects/Example.hs b/website/Shpadoinkle/Website/Types/Effects/Example.hs index 3e86af7b..d60eca8c 100644 --- a/website/Shpadoinkle/Website/Types/Effects/Example.hs +++ b/website/Shpadoinkle/Website/Types/Effects/Example.hs @@ -9,10 +9,11 @@ module Shpadoinkle.Website.Types.Effects.Example ) where -import Control.Monad.Reader +import Control.Monad.Reader (MonadReader) import Shpadoinkle.Isreal.Types (Code) -import Shpadoinkle.Website.Types.Effects.Swan +import Shpadoinkle.Website.Types.Effects.Swan (Swan (..)) +import Shpadoinkle.Website.Types.Home (Examples) import UnliftIO.STM (TVar) -type ExampleEffects m = (MonadReader (TVar (Maybe Code)) m, Swan m) +type ExampleEffects m = (MonadReader (Examples (TVar (Maybe Code))) m, Swan m) diff --git a/website/Shpadoinkle/Website/Types/Home.hs b/website/Shpadoinkle/Website/Types/Home.hs index c2b3f04f..8b1bb8ca 100644 --- a/website/Shpadoinkle/Website/Types/Home.hs +++ b/website/Shpadoinkle/Website/Types/Home.hs @@ -2,14 +2,17 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE OverloadedLabels #-} -{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE TemplateHaskell #-} module Shpadoinkle.Website.Types.Home where -import Control.Lens ((^.)) +import Control.Lens (Lens', (^.)) import Data.Aeson (FromJSON, ToJSON) +import Data.ByteString.Lazy (fromStrict) +import Data.FileEmbed (embedFile) import Data.Generics.Labels () import GHC.Generics (Generic) import Shpadoinkle (NFData) @@ -30,25 +33,28 @@ data Home = Home data Examples a = Examples { helloWorld :: a , counter :: a + , todo :: a } deriving stock (Eq, Ord, Show, Read, Generic) deriving anyclass (FromJSON, ToJSON, NFData) +type ExampleLens = forall a. Lens' (Examples a) a + + emptyExample :: Code -> SnowToken -> Example emptyExample cc st = Example cc st 0 ELoading emptyHome :: Examples SnowToken -> Home emptyHome st = Home mempty $ Examples - { helloWorld = emptyExample helloWorldExample (st ^. #helloWorld) - , counter = emptyExample counterExample (st ^. #counter) + { helloWorld = emptyExample helloWorldExample $ st ^. #helloWorld + , counter = emptyExample counterExample $ st ^. #counter + , todo = emptyExample todoExample $ st ^. #todo } -helloWorldExample :: Code -helloWorldExample = Code "" - - -counterExample :: Code -counterExample = Code "" +helloWorldExample, counterExample, todoExample :: Code +helloWorldExample = Code $ fromStrict $(embedFile "./hello-world.example") +counterExample = Code $ fromStrict $(embedFile "./counter.example") +todoExample = Code $ fromStrict $(embedFile "./todo.example") diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index d9b06d0d..920fa5d5 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -1,9 +1,11 @@ +{-# LANGUAGE ApplicativeDo #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE ExtendedDefaultRules #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-} @@ -27,9 +29,11 @@ import qualified Shpadoinkle.Website.Page.Documentation as Documentation import qualified Shpadoinkle.Website.Page.Home as Home import Shpadoinkle.Website.Partials.Template import Shpadoinkle.Website.Types.Effects.Example +import Shpadoinkle.Website.Types.Example (Example) import Shpadoinkle.Website.Types.Frontend import Shpadoinkle.Website.Types.Home import Shpadoinkle.Website.Types.Routes +import UnliftIO.Async (Concurrently (..)) default (Text) @@ -43,7 +47,7 @@ fourOhFour :: Html m a fourOhFour = h2_ [ "404" ] -view :: MonadJSM m => Frontend -> Html m Frontend +view :: (MonadJSM m, ExampleEffects m) => Frontend -> Html m Frontend view fe = div_ . wrapper fe $ case fe of MHome x -> onSum #_MHome <$> Home.view x MConcepts -> [ Documentation.concepts ] @@ -54,17 +58,25 @@ view fe = div_ . wrapper fe $ case fe of MFourOhFour -> [ fourOhFour ] -compileExamples :: MonadJSM m => ExampleEffects m => Home -> m Home -compileExamples home' = do - hw <- runContinuation compileExample $ home' ^. #examples . #helloWorld - c <- runContinuation compileExample $ home' ^. #examples . #counter - return $ home' - & #examples . #helloWorld %~ hw - & #examples . #counter %~ c +compileExamples :: forall m. (MonadUnliftIO m, MonadJSM m, ExampleEffects m) => Home -> m Home +compileExamples home' = runConcurrently $ do + hw <- compileWith #helloWorld + c <- compileWith #counter + t <- compileWith #todo + return $ home' & #examples . #helloWorld %~ hw + & #examples . #counter %~ c + & #examples . #todo %~ t + where + compileWith :: ExampleLens -> Concurrently m (Example -> Example) + compileWith l = Concurrently $ do + runContinuation (compileExample l) $ home' ^. #examples . l genExampleTokens :: MonadIO m => m (Examples SnowToken) -genExampleTokens = liftIO $ Examples <$> genSnowToken <*> genSnowToken +genExampleTokens = liftIO $ do + e <- Examples <$> genSnowToken <*> genSnowToken <*> genSnowToken + print e + return e start :: MonadIO m => Route -> m Frontend @@ -78,7 +90,7 @@ start = \case RSandbox -> pure MSandbox -startJS :: MonadJSM m => ExampleEffects m => Route -> m Frontend +startJS :: (MonadUnliftIO m, MonadJSM m, ExampleEffects m) => Route -> m Frontend startJS r = do fe <- start r case fe of diff --git a/website/assets/style.css b/website/assets/style.css index afc61bc8..112d922e 100644 --- a/website/assets/style.css +++ b/website/assets/style.css @@ -1 +1,9 @@ @font-face{font-family:Adelle;src:url(/assets/Adelle-Regular.woff2) format('woff2');font-weight:400;font-style:normal}@font-face{font-family:AdelleBold;src:url(/assets/Adelle-Bold.woff2) format('woff2');font-weight:400;font-style:normal}/*! modern-normalize v1.0.0 | MIT License | https://github.com/sindresorhus/modern-normalize */*,::after,::before{box-sizing:border-box}:root{-moz-tab-size:4;-o-tab-size:4;tab-size:4}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}body{font-family:system-ui,-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji'}hr{height:0;color:inherit}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Consolas,'Liberation Mono',Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],button{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}button{background-color:transparent;background-image:none}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}fieldset{margin:0;padding:0}ol,ul{list-style:none;margin:0;padding:0}html{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";line-height:1.5}body{font-family:inherit;line-height:inherit}*,::after,::before{box-sizing:border-box;border-width:0;border-style:solid;border-color:currentColor}hr{border-top-width:1px}img{border-style:solid}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#a1a1aa}input:-ms-input-placeholder,textarea:-ms-input-placeholder{opacity:1;color:#a1a1aa}input::placeholder,textarea::placeholder{opacity:1;color:#a1a1aa}button{cursor:pointer}table{border-collapse:collapse}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,optgroup,select,textarea{padding:0;line-height:inherit;color:inherit}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.rounded{border-radius:.25rem}.flex{display:flex}.table{display:table}.table-row{display:table-row}.grid{display:grid}.hidden{display:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.flex-1{flex:1 1 0%}.font-bold{font-family:AdelleBold,Verdana}.font-bold{font-weight:700}.h-32{height:8rem}.text-xs{font-size:.75rem;line-height:1rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.leading-8{line-height:2rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-0{margin-left:0}.mr-4{margin-right:1rem}.mb-4{margin-bottom:1rem}.mb-24{margin-bottom:6rem}.max-w-sm{max-width:24rem}.min-h-screen{min-height:100vh}.opacity-0{opacity:0}.opacity-100{opacity:1}.p-2{padding:.5rem}.p-6{padding:1.5rem}.p-10{padding:2.5rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.px-10{padding-left:2.5rem;padding-right:2.5rem}.py-16{padding-top:4rem;padding-bottom:4rem}.pt-0{padding-top:0}.pt-12{padding-top:3rem}.fixed{position:fixed}.sticky{position:sticky}.top-0{top:0}.right-0{right:0}.bottom-0{bottom:0}*{--tw-shadow:0 0 #0000}*{--tw-ring-inset:var(--tw-empty, );/*!*//*!*/--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59, 130, 246, 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000}.text-white{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.w-full{width:100%}.gap-8{gap:2rem}.gap-10{gap:2.5rem}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.transform{--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;transform:translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-90{--tw-scale-x:.9;--tw-scale-y:.9}.scale-100{--tw-scale-x:1;--tw-scale-y:1}.transition{transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:150ms}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}@-webkit-keyframes spin{to{transform:rotate(360deg)}}@keyframes spin{to{transform:rotate(360deg)}}@-webkit-keyframes ping{100%,75%{transform:scale(2);opacity:0}}@keyframes ping{100%,75%{transform:scale(2);opacity:0}}@-webkit-keyframes pulse{50%{opacity:.5}}@keyframes pulse{50%{opacity:.5}}@-webkit-keyframes bounce{0%,100%{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@keyframes bounce{0%,100%{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}#preamble>.sectionbody>.paragraph>p{padding:1rem 0}.listingblock{padding:2rem 0;overflow-x:scroll}.listingblock>.content>.highlight>code{border:2px solid #e2e0d5;background-color:transparent;padding:15px 10px}div.admonitionblock{position:relative;margin:3rem 0}div.admonitionblock>table>tbody>tr>td.icon{border-radius:18px;color:#fff;display:flex;height:30px;justify-content:flex-start;left:5px;padding:0 1.5rem;position:absolute;top:-15px;width:135px}div.admonitionblock>table>tbody>tr>td.content{padding:20px 10px 15px 1rem}div.admonitionblock.caution{border:2px dashed #ac2a34}div.admonitionblock.caution>table>tbody>tr>td.icon{background:#ac2a34}div.admonitionblock.caution>table>tbody>tr>td.content{color:#ac2a34}div.admonitionblock.note{border:2px dashed #0f406b}div.admonitionblock.note>table>tbody>tr>td.icon{background:#0f406b}div.admonitionblock.note>table>tbody>tr>td.content{color:#0f406b}div.admonitionblock.warning{border:2px dashed #e37743}div.admonitionblock.warning>table>tbody>tr>td.icon{background:#e37743}div.admonitionblock.warning>table>tbody>tr>td.content{color:#e37743}h1,h2,h3,h4,h5{padding:1rem 0}h1{font-size:1.875rem}h2{font-size:1.375rem}h3{font-size:1.5rem}@media (min-width:768px){h1{font-size:3.75rem}h2{font-size:2.625rem}h3{font-size:1.325rem}}.split-button:active{transform:translate(2px,2px)}.started-header{background-image:url(/assets/get_started_header_bg.d41c50d2.svg);height:96px;background-position:center;background-repeat:no-repeat;background-size:cover;margin-bottom:3rem;position:relative;z-index:50}@media (min-width:768px){.started-header{margin-bottom:-3.5rem}}@media (min-width:768px){.started-header{background-image:url(/assets/get_started_header_bg.05d59513.svg);height:156px}}body{--tw-bg-opacity:1;background-color:rgba(237,225,113,var(--tw-bg-opacity));--tw-border-opacity:1;border-color:rgba(217,206,95,var(--tw-border-opacity));border-style:solid;font-family:Adelle,ui-serif}.nav{width:100%}@media (min-width:640px){.nav{max-width:640px}}@media (min-width:768px){.nav{max-width:768px}}@media (min-width:1024px){.nav{max-width:1024px}}@media (min-width:1280px){.nav{max-width:1280px}}@media (min-width:1536px){.nav{max-width:1536px}}.nav{display:flex;justify-content:space-between;margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem;padding-top:2rem;position:relative;width:100%}.nav__logo{width:75%}@media (min-width:768px){.nav__logo{width:18rem}}.nav__menu-icon{display:block;padding-right:.75rem;width:2.5rem}@media (min-width:768px){.nav__menu-icon{display:none}}.nav__links{display:none;position:absolute;right:0;text-transform:uppercase;gap:1.25rem}@media (min-width:768px){.nav__links{display:flex}}.nav__link{display:flex;flex-direction:column;justify-content:center;position:relative;text-align:center;width:7rem}.nav__link-underline{position:absolute;top:1.25rem}.nav__link__underline-docs{padding-top:.5rem;position:absolute;top:1.25rem}.nav-mobile{padding-top:3rem}@media (min-width:640px){.nav-mobile{display:none}}.nav-mobile__wrapper>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.nav-mobile__wrapper{padding-top:.5rem;padding-bottom:1rem}.nav-mobile__link{border-color:transparent;display:block;font-size:1.875rem;line-height:2.25rem;padding-top:.5rem;padding-bottom:.5rem;padding-right:1rem;padding-left:1rem;--tw-text-opacity:1;color:rgba(18,18,18,var(--tw-text-opacity))}.nav-mobile__links>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.nav-mobile__links{padding-top:.5rem;padding-bottom:1rem}.nav-mobile__heading{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity));display:flex;flex-direction:column;justify-content:flex-end;height:5rem;font-size:1.5rem;line-height:2rem;padding-bottom:.75rem;padding-left:1rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:100%}.nav-mobile__heading__title{display:flex;justify-content:space-between;padding-right:1rem}.nav-mobile__heading__close{display:block;padding-right:.75rem}@media (min-width:768px){.nav-mobile__heading__close{display:none}}.nav-mobile__links__level1{padding-left:3rem}.nav-mobile__links__level2{padding-left:1rem}.hero-bg{display:none;margin-top:-21rem;width:100%}@media (min-width:768px){.hero-bg{display:block}}.hero-bg-mobile{display:block;margin-top:-16rem;width:100%}@media (min-width:768px){.hero-bg-mobile{display:none}}.hero{width:100%}@media (min-width:640px){.hero{max-width:640px}}@media (min-width:768px){.hero{max-width:768px}}@media (min-width:1024px){.hero{max-width:1024px}}@media (min-width:1280px){.hero{max-width:1280px}}@media (min-width:1536px){.hero{max-width:1536px}}.hero{margin-left:auto;margin-right:auto;margin-top:3rem;padding-left:1rem;padding-right:1rem;padding-bottom:6rem}@media (min-width:768px){.hero{margin-top:6rem}}.hero-title-wrapper{justify-content:space-between;position:relative}.hero-title{font-size:1.875rem;line-height:2.25rem}@media (min-width:768px){.hero-title{font-size:2.25rem;line-height:2.5rem;line-height:1.625}}@media (min-width:1024px){.hero-title{font-size:3rem;line-height:1}}.hero-button{display:flex;margin-bottom:3rem;padding-top:1.25rem}@media (min-width:768px){.hero-button{padding-top:2.5rem}}.hero-button__content{--tw-border-opacity:1;border-color:rgba(210,199,91,var(--tw-border-opacity));width:13rem}.hero-button__split{--tw-bg-opacity:1;background-color:rgba(18,18,18,var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.feature-button{display:none;justify-content:center;margin-bottom:3rem}@media (min-width:768px){.feature-button{display:flex;padding-top:1.5rem}}.feature-button__content{--tw-border-opacity:1;border-color:rgba(196,91,40,var(--tw-border-opacity));width:24rem}.feature-button__split{--tw-bg-opacity:1;background-color:rgba(241,226,85,var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.split-button{background-color:transparent;border-radius:.75rem;border-style:solid;border-width:2px;display:flex;height:3rem;position:relative}.split-button__button{display:flex;position:absolute;left:-.5rem;top:-.75rem}.split-button__split1{border-radius:.75rem;display:inline-flex;align-items:center;font-weight:500;height:3rem;margin-right:.5rem;padding-top:.75rem;padding-bottom:.75rem;padding-left:1.5rem;padding-right:1.5rem}.split-button__split2{border-radius:.75rem;display:inline-flex;align-items:center;font-weight:500;height:3rem;font-size:1.125rem;line-height:1.75rem;padding-top:.75rem;padding-bottom:.75rem;padding-left:.75rem;padding-right:.75rem;--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.transition{width:100%}.feature__section{width:full;margin-top:-.5rem;background-color:#e37743}.feature__wrapper{width:100%}@media (min-width:640px){.feature__wrapper{max-width:640px}}@media (min-width:768px){.feature__wrapper{max-width:768px}}@media (min-width:1024px){.feature__wrapper{max-width:1024px}}@media (min-width:1280px){.feature__wrapper{max-width:1280px}}@media (min-width:1536px){.feature__wrapper{max-width:1536px}}.feature__wrapper{margin-left:auto;margin-right:auto;padding-left:0;padding-right:0;padding-top:3rem}@media (min-width:768px){.feature__wrapper{padding-left:1rem;padding-right:1rem}}.feature__title{font-size:1.875rem;line-height:2.25rem;margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem;width:100%}@media (min-width:768px){.feature__title{font-size:2.25rem;line-height:2.5rem;padding-left:0;padding-right:0;text-align:center;width:60%}}.feature__cards{display:flex;flex-wrap:wrap;justify-content:center;padding-top:3rem}.feature__card{display:flex;justify-content:center;padding-bottom:1.5rem;width:100%}@media (min-width:768px){.feature__card{margin-bottom:3.5rem;width:50%}}@media (min-width:1024px){.feature__card{width:33.333333%}}.feature__card__content{--tw-bg-opacity:1;background-color:rgba(208,79,34,var(--tw-bg-opacity));border-radius:0;padding-top:1rem;padding-bottom:1rem;padding-left:2rem;padding-right:2rem;width:100%}@media (min-width:768px){.feature__card__content{border-radius:1.5rem;height:20rem;width:20rem}}.feature__card__heading{display:inline-flex;align-items:center;padding-bottom:1.25rem}.feature__card__title{font-size:1.875rem;line-height:2.25rem;margin-left:1rem;margin-bottom:-.5rem}.feature__card__description{font-size:1.125rem;line-height:1.75rem}.components__section{--tw-bg-opacity:1;background-color:rgba(58,52,83,var(--tw-bg-opacity));flex-direction:column;align-items:center;margin-top:-.5rem;padding-top:3rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:100%}.component{width:100%}@media (min-width:640px){.component{max-width:640px}}@media (min-width:768px){.component{max-width:768px}}@media (min-width:1024px){.component{max-width:1024px}}@media (min-width:1280px){.component{max-width:1280px}}@media (min-width:1536px){.component{max-width:1536px}}.component{display:flex;flex-wrap:wrap;align-items:center;margin-left:auto;margin-right:auto;margin-bottom:3rem;padding-left:1rem;padding-right:1rem}.component__info{padding-right:0;padding-bottom:1rem;width:100%}@media (min-width:1024px){.component__info{padding-bottom:0;padding-right:2rem;width:33.333333%}}.component__info__title{padding-bottom:.75rem}.component__example{width:100%}@media (min-width:1024px){.component__example{width:66.666667%}}.component__example__code{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity));border-radius:.75rem;height:12rem}footer{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity));margin-top:-.5rem}.footer__wrapper{width:100%}@media (min-width:640px){.footer__wrapper{max-width:640px}}@media (min-width:768px){.footer__wrapper{max-width:768px}}@media (min-width:1024px){.footer__wrapper{max-width:1024px}}@media (min-width:1280px){.footer__wrapper{max-width:1280px}}@media (min-width:1536px){.footer__wrapper{max-width:1536px}}.footer__wrapper{margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem}.footer__nav{display:flex;flex-direction:column;justify-content:space-between;line-height:2rem;padding-top:3rem;width:100%;gap:2rem}@media (min-width:768px){.footer__nav{flex-direction:row;width:40%;gap:0}}.footer__nav__category{--tw-text-opacity:1;color:rgba(91,56,233,var(--tw-text-opacity))}.footer__nav__links{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));text-transform:uppercase}.footer_copyright{font-size:.875rem;line-height:1.25rem;padding-top:3rem;padding-bottom:3rem;--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.documentation{--tw-bg-opacity:1;background-color:rgba(250,247,230,var(--tw-bg-opacity));--tw-border-opacity:1;border-color:rgba(217,206,95,var(--tw-border-opacity));border-style:solid;line-height:2.25rem}.mobile-mode{background-color:#ede171!important}.top-border{border-top-width:4px}.sidebar{display:flex}.side-nav>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.side-nav{--tw-bg-opacity:1;background-color:rgba(242,239,217,var(--tw-bg-opacity));display:none;flex-direction:column;padding-left:3.5rem;padding-top:5rem;position:relative;width:21rem;z-index:0}@media (min-width:768px){.side-nav{display:flex}}.side-nav__expand>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.side-nav__expand__button{border-radius:.75rem;cursor:pointer;display:flex;align-items:center;height:3.5rem;font-size:1.125rem;line-height:1.75rem;padding-left:1rem;padding-right:1rem}.side-nav__expand__button:hover{--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.side-nav__expand__button{width:100%}.side-nav__link:hover{--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.side-nav__expand__icon{margin-left:auto}.side-nav__expand__list{padding-left:2rem}.side-nav__expand__list2{padding-left:1.5rem}.docs{width:100%}@media (min-width:640px){.docs{max-width:640px}}@media (min-width:768px){.docs{max-width:768px}}@media (min-width:1024px){.docs{max-width:1024px}}@media (min-width:1280px){.docs{max-width:1280px}}@media (min-width:1536px){.docs{max-width:1536px}}.docs{margin-left:auto;margin-right:auto;margin-left:0;margin-bottom:6rem;padding-left:1rem;padding-right:1rem;padding-top:0}@media (min-width:768px){.docs{margin-left:2rem;padding-top:8rem}}.separator{--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-style:solid;border-top-width:2px;margin-top:2.5rem;margin-bottom:2.5rem}.code-block{--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-style:solid;border-width:2px;margin-top:2.5rem;margin-bottom:2.5rem;width:100%}.code-block__content{margin:1.5rem}.bullet-list{margin-top:.5rem;margin-bottom:.5rem;margin-left:1rem}.bullet-list__callout>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.bullet-list__callout{display:flex}code.doc{overflow-x:auto;background-color:transparent;--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-width:2px;display:block;margin-top:1rem;margin-bottom:1rem;padding:1rem}.admon{border-style:dashed;border-width:2px;margin-top:2.5rem;margin-bottom:2.5rem;width:100%}.admon-warning{--tw-border-opacity:1;border-color:rgba(220,38,38,var(--tw-border-opacity))}.admon__button{border-radius:9999px;display:flex;align-items:center;justify-content:center;height:2rem;margin-left:1rem;margin-top:-1rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:8rem;gap:.5rem}.admon__button-warning{--tw-bg-opacity:1;background-color:rgba(220,38,38,var(--tw-bg-opacity))}.admon__button-note{--tw-bg-opacity:1;background-color:rgba(30,58,138,var(--tw-bg-opacity))}.admon-note{--tw-border-opacity:1;border-color:rgba(30,58,138,var(--tw-border-opacity))}.secondary-nav{margin-bottom:1.5rem}@media (min-width:768px){.secondary-nav{display:none}}.secondary-nav__content{--tw-border-opacity:1;border-color:rgba(210,199,91,var(--tw-border-opacity));width:10rem}.secondary-nav__split{--tw-bg-opacity:1;background-color:rgba(18,18,18,var(--tw-bg-opacity));border-radius:.75rem;display:inline-flex;align-items:center;justify-content:center;font-weight:500;height:3rem;padding-left:.5rem;padding-right:.5rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:9rem}.secondary-nav__split>p{margin-right:6px}@media (min-width:768px){.md\:flex-row{flex-direction:row}.md\:ml-8{margin-left:2rem}.md\:pt-32{padding-top:8rem}.md\:w-2\/5{width:40%}.md\:gap-0{gap:0}} + +.CodeMirror { + height: auto; +} + +.component__example__code { + overflow: hidden; +} diff --git a/website/example.template.top b/website/example.template.top index bae49935..ca9654af 100644 --- a/website/example.template.top +++ b/website/example.template.top @@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE DeriveAnyClass #-} @@ -14,7 +15,8 @@ module Main where -import Data.Text +import Control.Lens ((%~), (&), (^.), (.~)) +import Data.Text hiding (length) import Data.Generics.Labels () import Prelude hiding (span, div) import GHC.Generics diff --git a/website/todo.example b/website/todo.example new file mode 100644 index 00000000..74c0c7e5 --- /dev/null +++ b/website/todo.example @@ -0,0 +1,21 @@ +data State = State + { input :: Text + , items :: [Text] + } deriving (Eq, Generic, NFData) + +appendItem state = state + & #items %~ (++ [state ^. #input]) + & #input .~ "" + +view state = div_ + [ h3_ [ "TODO" ] + , ul_ $ (\item -> li_ [ text item ]) <$> (state ^. #items) + , form [ onSubmit appendItem ] + [ label [ for' "new-todo" ] [ "What needs to be done?" ] + , br'_ + , input' [ id' "new-todo", onInput (#input .~), value $ state ^. #input ] + , button_ [ text $ "Add #" <> pack (show $ length (state ^. #items) + 1) ] + ] + ] + +app = simple runSnabbdom (State "" []) view getBody -- GitLab From c19eed5cb3def9f70a79a58dcf16930a72c01511 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 15 Apr 2021 15:54:22 -0600 Subject: [PATCH 039/116] don't wait on compile to render --- website/Shpadoinkle/Website/Disembodied.hs | 5 +++- .../Shpadoinkle/Website/Partials/Footer.hs | 28 ++++++++++--------- .../Shpadoinkle/Website/Partials/Template.hs | 25 +++++++++-------- website/Shpadoinkle/Website/Run.hs | 9 ++++-- website/Shpadoinkle/Website/View.hs | 17 ++++++----- website/package.yaml | 2 ++ 6 files changed, 50 insertions(+), 36 deletions(-) diff --git a/website/Shpadoinkle/Website/Disembodied.hs b/website/Shpadoinkle/Website/Disembodied.hs index 1bec70e1..41badd64 100644 --- a/website/Shpadoinkle/Website/Disembodied.hs +++ b/website/Shpadoinkle/Website/Disembodied.hs @@ -24,6 +24,7 @@ import Shpadoinkle.Isreal.Types (Code) import Shpadoinkle.Router (traverseUnions) import Shpadoinkle.Run (Env (Prod)) import Shpadoinkle.Website.Partials.Template (template) +import Shpadoinkle.Website.Types.CurrentYear (getCurrentYear) import Shpadoinkle.Website.Types.Effects.Example (ExampleEffects) import Shpadoinkle.Website.Types.Effects.Hooglable (Hooglable) import Shpadoinkle.Website.Types.Effects.Swan (Swan) @@ -39,7 +40,9 @@ newtype Noop a = Noop (JSM a) getStarts :: (MonadJSM m, ExampleEffects m) => IO (SiteSpec (SPA m)) -getStarts = traverseUnions (fmap (\fe -> template Prod fe $ view fe) . start) routes +getStarts = do + cy <- getCurrentYear + traverseUnions (fmap (\fe -> template Prod fe $ view cy fe) . start) routes main :: IO () diff --git a/website/Shpadoinkle/Website/Partials/Footer.hs b/website/Shpadoinkle/Website/Partials/Footer.hs index ade3ea7f..3128488c 100644 --- a/website/Shpadoinkle/Website/Partials/Footer.hs +++ b/website/Shpadoinkle/Website/Partials/Footer.hs @@ -5,16 +5,18 @@ module Shpadoinkle.Website.Partials.Footer (view) where -import Prelude hiding (div) -import Shpadoinkle (MonadJSM) -import Shpadoinkle.Html (Html, a, alt, class', div, div_, - footer_, href, img', li_, nav_, - newTab, onClickM_, p, src, text, - ul) -import Shpadoinkle.Html.TH.AssetLink (assetLink) -import Shpadoinkle.Website.Style as Style -import Shpadoinkle.Website.Types.Nav (Nav (NHome), goTo) -import Shpadoinkle.Widgets.Types (humanize) +import Prelude hiding (div) +import Shpadoinkle (MonadJSM) +import Shpadoinkle.Html (Html, a, alt, class', + div, div_, footer_, + href, img', li_, nav_, + newTab, onClickM_, p, + src, text, ul) +import Shpadoinkle.Html.TH.AssetLink (assetLink) +import Shpadoinkle.Website.Style as Style +import Shpadoinkle.Website.Types.CurrentYear (CurrentYear) +import Shpadoinkle.Website.Types.Nav (Nav (NHome), goTo) +import Shpadoinkle.Widgets.Types (humanize) footerLink :: MonadJSM m => Nav -> Html m a @@ -24,8 +26,8 @@ footerLink n = li_ ] -view :: MonadJSM m => Html m a -view = +view :: MonadJSM m => CurrentYear -> Html m a +view cy = footer_ [ div [ class' footer__wrapper ] [ a [ onClickM_ $ goTo NHome ] @@ -61,6 +63,6 @@ view = ] ] , div [ class' footer_copyright ] - [ "© 2020-2021 Platonic Systems" ] + [ text $ "© 2020-" <> humanize cy <> " Platonic Systems" ] ] ] diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index 97542186..98132122 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -8,16 +8,17 @@ module Shpadoinkle.Website.Partials.Template ) where -import Data.Text (Text) -import Prelude hiding (div) -import Shpadoinkle (MonadJSM) +import Data.Text (Text) +import Prelude hiding (div) +import Shpadoinkle (MonadJSM) import Shpadoinkle.Html -import Shpadoinkle.Html.TH.AssetLink (assetLink) -import Shpadoinkle.Router (toHydration) -import Shpadoinkle.Run (Env (..), entrypoint) -import qualified Shpadoinkle.Website.Partials.Footer as Footer -import qualified Shpadoinkle.Website.Partials.Nav as Nav -import Shpadoinkle.Website.Types.Frontend (Frontend) +import Shpadoinkle.Html.TH.AssetLink (assetLink) +import Shpadoinkle.Router (toHydration) +import Shpadoinkle.Run (Env (..), entrypoint) +import qualified Shpadoinkle.Website.Partials.Footer as Footer +import qualified Shpadoinkle.Website.Partials.Nav as Nav +import Shpadoinkle.Website.Types.CurrentYear (CurrentYear) +import Shpadoinkle.Website.Types.Frontend (Frontend) codemirrorCDN :: Text -> Text @@ -56,8 +57,8 @@ template ev fe content' = html [ lang "en" ] ] -wrapper :: MonadJSM m => Frontend -> [Html m a] -> [Html m a] -wrapper fe content' = Nav.view fe <> +wrapper :: MonadJSM m => CurrentYear -> Frontend -> [Html m a] -> [Html m a] +wrapper yc fe content' = Nav.view fe <> [ h "main" [] content' - , Footer.view + , Footer.view yc ] diff --git a/website/Shpadoinkle/Website/Run.hs b/website/Shpadoinkle/Website/Run.hs index 1b378d86..912686fa 100644 --- a/website/Shpadoinkle/Website/Run.hs +++ b/website/Shpadoinkle/Website/Run.hs @@ -58,6 +58,7 @@ import Shpadoinkle.Run (runJSorWarp) #endif import Shpadoinkle.DeveloperTools +import Shpadoinkle.Website.Types.CurrentYear (getCurrentYear) import Shpadoinkle.Website.Types.Effects.Hooglable (Hooglable (..)) import Shpadoinkle.Website.Types.Effects.Swan (Swan (..)) import Shpadoinkle.Website.Types.Frontend (Frontend) @@ -117,8 +118,9 @@ app = do mutex <- Examples <$> newTVarIO Nothing <*> newTVarIO Nothing <*> newTVarIO Nothing model :: TVar Frontend <- newTVarIO =<< runReaderT (runApp (start RFourOhFour)) mutex withDeveloperTools model - fullPageSPA' @(SPA JSM) (flip runReaderT mutex . runApp) runSnabbdom model (withHydration startJS) view stage - (fmap constUpdate . startJS) routes + yc <- getCurrentYear + fullPageSPA' @(SPA JSM) (flip runReaderT mutex . runApp) runSnabbdom model (withHydration (startJS model)) (view yc) stage + (fmap constUpdate . startJS model) routes main :: IO () @@ -130,6 +132,7 @@ main = runJSorWarp 8080 app dev :: IO () dev = liveWithBackend 8080 app . pure $ Servant.serve (Proxy @ (SPA IO)) $ serveUI @(SPA IO) "." (\r -> do x <- start r - return $ template @App Dev x (view x)) routes + cy <- getCurrentYear + return $ template @App Dev x (view cy x)) routes #endif diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index 920fa5d5..ad00efb8 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -28,12 +28,14 @@ import Shpadoinkle.Website.Component.LiveExample import qualified Shpadoinkle.Website.Page.Documentation as Documentation import qualified Shpadoinkle.Website.Page.Home as Home import Shpadoinkle.Website.Partials.Template +import Shpadoinkle.Website.Types.CurrentYear import Shpadoinkle.Website.Types.Effects.Example import Shpadoinkle.Website.Types.Example (Example) import Shpadoinkle.Website.Types.Frontend import Shpadoinkle.Website.Types.Home import Shpadoinkle.Website.Types.Routes -import UnliftIO.Async (Concurrently (..)) +import UnliftIO.Async (Concurrently (..), + async) default (Text) @@ -47,8 +49,8 @@ fourOhFour :: Html m a fourOhFour = h2_ [ "404" ] -view :: (MonadJSM m, ExampleEffects m) => Frontend -> Html m Frontend -view fe = div_ . wrapper fe $ case fe of +view :: (MonadJSM m, ExampleEffects m) => CurrentYear -> Frontend -> Html m Frontend +view cy fe = div_ . wrapper cy fe $ case fe of MHome x -> onSum #_MHome <$> Home.view x MConcepts -> [ Documentation.concepts ] MGettingStarted gsr -> [ Documentation.gettingStarted gsr ] @@ -90,9 +92,10 @@ start = \case RSandbox -> pure MSandbox -startJS :: (MonadUnliftIO m, MonadJSM m, ExampleEffects m) => Route -> m Frontend -startJS r = do +startJS :: (MonadUnliftIO m, MonadJSM m, ExampleEffects m) => TVar Frontend -> Route -> m Frontend +startJS model r = do fe <- start r case fe of - MHome home' -> MHome <$> compileExamples home' - x -> pure x + MHome home' -> () <$ async (atomically . writeTVar model . MHome =<< compileExamples home') + _ -> pure () + return fe diff --git a/website/package.yaml b/website/package.yaml index 0f2af505..9b3acc55 100644 --- a/website/package.yaml +++ b/website/package.yaml @@ -76,6 +76,7 @@ executables: - Shpadoinkle.Website.Partials.Footer - Shpadoinkle.Website.Page.Home - Shpadoinkle.Website.Page.Documentation + - Shpadoinkle.Website.Types.CurrentYear - Shpadoinkle.Website.Types.Nav - Shpadoinkle.Website.Types.Home - Shpadoinkle.Website.Types.Hoogle @@ -125,6 +126,7 @@ executables: - Shpadoinkle.Website.Types.Effects.Hooglable - Shpadoinkle.Website.Types.Effects.Swan - Shpadoinkle.Website.Types.Frontend + - Shpadoinkle.Website.Types.CurrentYear - Shpadoinkle.Website.Types.Home - Shpadoinkle.Website.Types.Routes - Shpadoinkle.Website.View -- GitLab From 54ec7b9aa9da7657052b641147dbd0f9f9032a7e Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 15 Apr 2021 15:54:32 -0600 Subject: [PATCH 040/116] ensure copyright is up to date --- .../Shpadoinkle/Website/Types/CurrentYear.hs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 website/Shpadoinkle/Website/Types/CurrentYear.hs diff --git a/website/Shpadoinkle/Website/Types/CurrentYear.hs b/website/Shpadoinkle/Website/Types/CurrentYear.hs new file mode 100644 index 00000000..0e3c148f --- /dev/null +++ b/website/Shpadoinkle/Website/Types/CurrentYear.hs @@ -0,0 +1,21 @@ +module Shpadoinkle.Website.Types.CurrentYear (CurrentYear, getCurrentYear) where + + +import Control.Monad.IO.Class (MonadIO, liftIO) +import Data.Text (Text, pack) +import Data.Time.Calendar (toGregorian) +import Data.Time.Clock (getCurrentTime, utctDay) +import Shpadoinkle.Widgets.Types (Humanize (..)) + + +newtype CurrentYear = CurrentYear Text + + +getCurrentYear :: MonadIO m => m CurrentYear +getCurrentYear = liftIO $ do + (year, _, _) <- toGregorian . utctDay <$> getCurrentTime + return $ CurrentYear $ pack $ show year + + +instance Humanize CurrentYear where + humanize (CurrentYear x) = x -- GitLab From 8bc294975b935e8739470d07e5399cde4fa16032 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 15 Apr 2021 18:46:06 -0600 Subject: [PATCH 041/116] nav mobile toggle --- examples/servant-crud/View.hs | 4 +- website/Shpadoinkle/Website/Disembodied.hs | 2 +- .../Shpadoinkle/Website/Partials/Footer.hs | 2 +- website/Shpadoinkle/Website/Partials/Nav.hs | 18 +++--- .../Shpadoinkle/Website/Partials/Template.hs | 9 ++- website/Shpadoinkle/Website/Run.hs | 13 +++-- website/Shpadoinkle/Website/Types/Routes.hs | 55 ++++++++++--------- website/Shpadoinkle/Website/View.hs | 29 ++++++---- website/assets/style.css | 5 ++ 9 files changed, 82 insertions(+), 55 deletions(-) diff --git a/examples/servant-crud/View.hs b/examples/servant-crud/View.hs index dfd0f515..27d2c227 100644 --- a/examples/servant-crud/View.hs +++ b/examples/servant-crud/View.hs @@ -183,8 +183,8 @@ editForm mid ef = H.div_ isValid = getValid errs -start :: (Monad m, CRUDSpaceCraft m) => Route -> m Frontend -start = \case +start :: (Monad m, CRUDSpaceCraft m) => Route -> m (Toggle, Frontend) +start = (,) mempty . \case RList s -> MList . Roster (SortCol SKUT ASC) s <$> listSpaceCraft REcho t -> return $ MEcho t RNew -> return $ MDetail Nothing emptyEditForm diff --git a/website/Shpadoinkle/Website/Disembodied.hs b/website/Shpadoinkle/Website/Disembodied.hs index 41badd64..221091af 100644 --- a/website/Shpadoinkle/Website/Disembodied.hs +++ b/website/Shpadoinkle/Website/Disembodied.hs @@ -42,7 +42,7 @@ newtype Noop a = Noop (JSM a) getStarts :: (MonadJSM m, ExampleEffects m) => IO (SiteSpec (SPA m)) getStarts = do cy <- getCurrentYear - traverseUnions (fmap (\fe -> template Prod fe $ view cy fe) . start) routes + traverseUnions (fmap (\fe -> template Prod (snd fe) $ view cy fe) . start) routes main :: IO () diff --git a/website/Shpadoinkle/Website/Partials/Footer.hs b/website/Shpadoinkle/Website/Partials/Footer.hs index 3128488c..27f278d5 100644 --- a/website/Shpadoinkle/Website/Partials/Footer.hs +++ b/website/Shpadoinkle/Website/Partials/Footer.hs @@ -63,6 +63,6 @@ view cy = ] ] , div [ class' footer_copyright ] - [ text $ "© 2020-" <> humanize cy <> " Platonic Systems" ] + [ text $ "© 2020-" <> humanize cy <> " Platonic Systems Limited" ] ] ] diff --git a/website/Shpadoinkle/Website/Partials/Nav.hs b/website/Shpadoinkle/Website/Partials/Nav.hs index 178a182b..2c27f719 100644 --- a/website/Shpadoinkle/Website/Partials/Nav.hs +++ b/website/Shpadoinkle/Website/Partials/Nav.hs @@ -14,6 +14,7 @@ import Shpadoinkle.Website.Style as Style import Shpadoinkle.Website.Types.Frontend import Shpadoinkle.Website.Types.Nav import Shpadoinkle.Widgets.Types +import Shpadoinkle.Widgets.Types.Physical (Toggle (..)) toActive :: Frontend -> Maybe Nav @@ -35,17 +36,20 @@ navLinkMobile :: Nav -> Html m a navLinkMobile n = a [ class' nav_mobile__link, href "#" ] [ text $ humanize n ] -view :: MonadJSM m => Frontend -> [Html m a] -view r = +view :: MonadJSM m => Toggle -> Frontend -> [Html m Toggle] +view t r = [ H.nav [ class' Style.nav ] [ a [ href "/" ] [ img' [ class' nav__logo, src $(assetLink "/assets/landing_logo.svg") ] ] - , img' [ class' nav__menu_icon, src $(assetLink "/assets/menu_icon.svg") ] - , img' [ class' nav__menu_icon, src $(assetLink "/assets/menu_close_icon.svg") ] + , img' [ class' nav__menu_icon, onClick toggle, src $ case t of + Closed _ -> $(assetLink "/assets/menu_icon.svg") + Open -> $(assetLink "/assets/menu_close_icon.svg") + ] , ul [ class' nav__links ] $ navLinkDesktop r <$> [minBound..maxBound] ] - , H.nav [ class' nav_mobile ] - [ div [ class' nav_mobile__wrapper ] $ navLinkMobile <$> [minBound..maxBound] - ] + , H.nav [ class' nav_mobile ] $ case t of + Closed _ -> [] + Open -> [ div [ class' nav_mobile__wrapper ] $ navLinkMobile <$> [minBound..maxBound] + ] ] diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index 98132122..201d557b 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -8,17 +8,20 @@ module Shpadoinkle.Website.Partials.Template ) where +import Control.Lens (_1, _2) import Data.Text (Text) import Prelude hiding (div) import Shpadoinkle (MonadJSM) import Shpadoinkle.Html import Shpadoinkle.Html.TH.AssetLink (assetLink) +import Shpadoinkle.Lens (generalize) import Shpadoinkle.Router (toHydration) import Shpadoinkle.Run (Env (..), entrypoint) import qualified Shpadoinkle.Website.Partials.Footer as Footer import qualified Shpadoinkle.Website.Partials.Nav as Nav import Shpadoinkle.Website.Types.CurrentYear (CurrentYear) import Shpadoinkle.Website.Types.Frontend (Frontend) +import Shpadoinkle.Widgets.Types.Physical (Toggle (..)) codemirrorCDN :: Text -> Text @@ -57,8 +60,8 @@ template ev fe content' = html [ lang "en" ] ] -wrapper :: MonadJSM m => CurrentYear -> Frontend -> [Html m a] -> [Html m a] -wrapper yc fe content' = Nav.view fe <> +wrapper :: MonadJSM m => CurrentYear -> Toggle -> Frontend -> [Html m a] -> [Html m (Toggle, a)] +wrapper yc t fe content' = (generalize _1 <$> Nav.view t fe) <> (generalize _2 <$> [ h "main" [] content' , Footer.view yc - ] + ]) diff --git a/website/Shpadoinkle/Website/Run.hs b/website/Shpadoinkle/Website/Run.hs index 912686fa..5c85e261 100644 --- a/website/Shpadoinkle/Website/Run.hs +++ b/website/Shpadoinkle/Website/Run.hs @@ -61,7 +61,6 @@ import Shpadoinkle.DeveloperTools import Shpadoinkle.Website.Types.CurrentYear (getCurrentYear) import Shpadoinkle.Website.Types.Effects.Hooglable (Hooglable (..)) import Shpadoinkle.Website.Types.Effects.Swan (Swan (..)) -import Shpadoinkle.Website.Types.Frontend (Frontend) import Shpadoinkle.Website.Types.Home (Examples (..)) import Shpadoinkle.Website.Types.Hoogle.API as Hoogle import Shpadoinkle.Website.Types.Hoogle.Target (Target) @@ -116,10 +115,16 @@ findTargetsM (Search s) = client (Proxy @ HoogleAPI) (Just "json") (Just s) (Jus app :: JSM () app = do mutex <- Examples <$> newTVarIO Nothing <*> newTVarIO Nothing <*> newTVarIO Nothing - model :: TVar Frontend <- newTVarIO =<< runReaderT (runApp (start RFourOhFour)) mutex + model <- newTVarIO =<< runReaderT (runApp (start RFourOhFour)) mutex withDeveloperTools model yc <- getCurrentYear - fullPageSPA' @(SPA JSM) (flip runReaderT mutex . runApp) runSnabbdom model (withHydration (startJS model)) (view yc) stage + fullPageSPA' @(SPA JSM) + (flip runReaderT mutex . runApp) + runSnabbdom + model + (withHydration (startJS model)) + (view yc) + stage (fmap constUpdate . startJS model) routes @@ -133,6 +138,6 @@ dev :: IO () dev = liveWithBackend 8080 app . pure $ Servant.serve (Proxy @ (SPA IO)) $ serveUI @(SPA IO) "." (\r -> do x <- start r cy <- getCurrentYear - return $ template @App Dev x (view cy x)) routes + return $ template @App Dev (snd x) (view cy x)) routes #endif diff --git a/website/Shpadoinkle/Website/Types/Routes.hs b/website/Shpadoinkle/Website/Types/Routes.hs index f5e6607a..fd0f3411 100644 --- a/website/Shpadoinkle/Website/Types/Routes.hs +++ b/website/Shpadoinkle/Website/Types/Routes.hs @@ -29,6 +29,7 @@ import Shpadoinkle.Website.Types.Frontend (Frontend) import qualified Shpadoinkle.Website.Types.Routes.GettingStarted as GettingStarted import qualified Shpadoinkle.Website.Types.Routes.Packages as Packages import qualified Shpadoinkle.Website.Types.Routes.Tutorial as Tutorial +import Shpadoinkle.Widgets.Types.Physical (Toggle) data Route @@ -43,14 +44,14 @@ data Route type SPA m - = "home" :> View m Frontend - :<|> "concepts" :> View m Frontend - :<|> "getting-started" :> GettingStarted.SPA m Frontend - :<|> "packages" :> Packages.SPA m Frontend - :<|> "tutorial" :> Tutorial.SPA m Frontend - :<|> "sandbox" :> View m Frontend - :<|> "404" :> View m Frontend - :<|> View m Frontend + = "home" :> View m (Toggle, Frontend) + :<|> "concepts" :> View m (Toggle, Frontend) + :<|> "getting-started" :> GettingStarted.SPA m (Toggle, Frontend) + :<|> "packages" :> Packages.SPA m (Toggle, Frontend) + :<|> "tutorial" :> Tutorial.SPA m (Toggle, Frontend) + :<|> "sandbox" :> View m (Toggle, Frontend) + :<|> "404" :> View m (Toggle, Frontend) + :<|> View m (Toggle, Frontend) routes :: SPA m :>> Route @@ -68,46 +69,46 @@ routes instance Routed (SPA m) Route where redirect = \case RHome -> - Redirect (Proxy @("home" :> View m Frontend)) id + Redirect (Proxy @("home" :> View m (Toggle, Frontend))) id RConcepts -> - Redirect (Proxy @("concepts" :> View m Frontend)) id + Redirect (Proxy @("concepts" :> View m (Toggle, Frontend))) id RGettingStarted GettingStarted.RGSIndex -> - Redirect (Proxy @("getting-started" :> View m Frontend)) id + Redirect (Proxy @("getting-started" :> View m (Toggle, Frontend))) id RGettingStarted GettingStarted.RGSAddingToYourProject -> - Redirect (Proxy @("getting-started" :> "adding-to-your-project" :> View m Frontend)) id + Redirect (Proxy @("getting-started" :> "adding-to-your-project" :> View m (Toggle, Frontend))) id RGettingStarted GettingStarted.RGSExtendAnExample -> - Redirect (Proxy @("getting-started" :> "extend-an-example" :> View m Frontend)) id + Redirect (Proxy @("getting-started" :> "extend-an-example" :> View m (Toggle, Frontend))) id RPackages Packages.RPIndex -> - Redirect (Proxy @("packages" :> View m Frontend)) id + Redirect (Proxy @("packages" :> View m (Toggle, Frontend))) id RPackages Packages.RPCore -> - Redirect (Proxy @("packages" :> "core" :> View m Frontend)) id + Redirect (Proxy @("packages" :> "core" :> View m (Toggle, Frontend))) id RPackages Packages.RPConsole -> - Redirect (Proxy @("packages" :> "console" :> View m Frontend)) id + Redirect (Proxy @("packages" :> "console" :> View m (Toggle, Frontend))) id RPackages Packages.RPBackends -> - Redirect (Proxy @("packages" :> "backends" :> View m Frontend)) id + Redirect (Proxy @("packages" :> "backends" :> View m (Toggle, Frontend))) id RPackages Packages.RPHtml -> - Redirect (Proxy @("packages" :> "html" :> View m Frontend)) id + Redirect (Proxy @("packages" :> "html" :> View m (Toggle, Frontend))) id RPackages Packages.RPLens -> - Redirect (Proxy @("packages" :> "lens" :> View m Frontend)) id + Redirect (Proxy @("packages" :> "lens" :> View m (Toggle, Frontend))) id RPackages Packages.RPRouter -> - Redirect (Proxy @("packages" :> "router" :> View m Frontend)) id + Redirect (Proxy @("packages" :> "router" :> View m (Toggle, Frontend))) id RPackages Packages.RPWidgets -> - Redirect (Proxy @("packages" :> "widgets" :> View m Frontend)) id + Redirect (Proxy @("packages" :> "widgets" :> View m (Toggle, Frontend))) id RTutorial Tutorial.RTIndex -> - Redirect (Proxy @("tutorial" :> View m Frontend)) id + Redirect (Proxy @("tutorial" :> View m (Toggle, Frontend))) id RTutorial Tutorial.RTCalculator -> - Redirect (Proxy @("tutorial" :> "calculator" :> View m Frontend)) id + Redirect (Proxy @("tutorial" :> "calculator" :> View m (Toggle, Frontend))) id RTutorial Tutorial.RTImmediateExecution -> - Redirect (Proxy @("tutorial" :> "immediate-execution" :> View m Frontend)) id + Redirect (Proxy @("tutorial" :> "immediate-execution" :> View m (Toggle, Frontend))) id RTutorial Tutorial.RTComposing -> - Redirect (Proxy @("tutorial" :> "composing" :> View m Frontend)) id + Redirect (Proxy @("tutorial" :> "composing" :> View m (Toggle, Frontend))) id RSandbox -> - Redirect (Proxy @("sandbox" :> View m Frontend)) id + Redirect (Proxy @("sandbox" :> View m (Toggle, Frontend))) id RFourOhFour -> - Redirect (Proxy @("404" :> View m Frontend)) id + Redirect (Proxy @("404" :> View m (Toggle, Frontend))) id diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index ad00efb8..9f20a9df 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -7,13 +7,17 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TupleSections #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-} + module Shpadoinkle.Website.View where -import Control.Lens ((%~), (&), (^.)) +import Control.Lens (_2, (%~), (&), (.~), + (^.)) +import Control.Monad (void) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Reader import Data.Generics.Labels () @@ -34,6 +38,7 @@ import Shpadoinkle.Website.Types.Example (Example) import Shpadoinkle.Website.Types.Frontend import Shpadoinkle.Website.Types.Home import Shpadoinkle.Website.Types.Routes +import Shpadoinkle.Widgets.Types.Physical (Toggle (..)) import UnliftIO.Async (Concurrently (..), async) @@ -49,8 +54,8 @@ fourOhFour :: Html m a fourOhFour = h2_ [ "404" ] -view :: (MonadJSM m, ExampleEffects m) => CurrentYear -> Frontend -> Html m Frontend -view cy fe = div_ . wrapper cy fe $ case fe of +view :: (MonadJSM m, ExampleEffects m) => CurrentYear -> (Toggle, Frontend) -> Html m (Toggle, Frontend) +view cy (t, fe) = div_ . wrapper cy t fe $ case fe of MHome x -> onSum #_MHome <$> Home.view x MConcepts -> [ Documentation.concepts ] MGettingStarted gsr -> [ Documentation.gettingStarted gsr ] @@ -70,7 +75,7 @@ compileExamples home' = runConcurrently $ do & #examples . #todo %~ t where compileWith :: ExampleLens -> Concurrently m (Example -> Example) - compileWith l = Concurrently $ do + compileWith l = Concurrently $ runContinuation (compileExample l) $ home' ^. #examples . l @@ -81,8 +86,8 @@ genExampleTokens = liftIO $ do return e -start :: MonadIO m => Route -> m Frontend -start = \case +start :: MonadIO m => Route -> m (Toggle, Frontend) +start = fmap (mempty,) . \case RHome -> MHome . emptyHome <$> genExampleTokens RConcepts -> pure MConcepts RGettingStarted x -> pure $ MGettingStarted x @@ -92,10 +97,14 @@ start = \case RSandbox -> pure MSandbox -startJS :: (MonadUnliftIO m, MonadJSM m, ExampleEffects m) => TVar Frontend -> Route -> m Frontend +startJS + :: (MonadUnliftIO m, MonadJSM m, ExampleEffects m) + => TVar (Toggle, Frontend) -> Route -> m (Toggle, Frontend) startJS model r = do - fe <- start r + t@(_, fe) <- start r case fe of - MHome home' -> () <$ async (atomically . writeTVar model . MHome =<< compileExamples home') + MHome home' -> void . async $ do + corn <- compileExamples home' + atomically . modifyTVar model $ _2 . #_MHome .~ corn _ -> pure () - return fe + return t diff --git a/website/assets/style.css b/website/assets/style.css index 112d922e..2c8341cc 100644 --- a/website/assets/style.css +++ b/website/assets/style.css @@ -1,5 +1,10 @@ @font-face{font-family:Adelle;src:url(/assets/Adelle-Regular.woff2) format('woff2');font-weight:400;font-style:normal}@font-face{font-family:AdelleBold;src:url(/assets/Adelle-Bold.woff2) format('woff2');font-weight:400;font-style:normal}/*! modern-normalize v1.0.0 | MIT License | https://github.com/sindresorhus/modern-normalize */*,::after,::before{box-sizing:border-box}:root{-moz-tab-size:4;-o-tab-size:4;tab-size:4}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}body{font-family:system-ui,-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji'}hr{height:0;color:inherit}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Consolas,'Liberation Mono',Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],button{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}button{background-color:transparent;background-image:none}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}fieldset{margin:0;padding:0}ol,ul{list-style:none;margin:0;padding:0}html{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";line-height:1.5}body{font-family:inherit;line-height:inherit}*,::after,::before{box-sizing:border-box;border-width:0;border-style:solid;border-color:currentColor}hr{border-top-width:1px}img{border-style:solid}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#a1a1aa}input:-ms-input-placeholder,textarea:-ms-input-placeholder{opacity:1;color:#a1a1aa}input::placeholder,textarea::placeholder{opacity:1;color:#a1a1aa}button{cursor:pointer}table{border-collapse:collapse}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,optgroup,select,textarea{padding:0;line-height:inherit;color:inherit}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.rounded{border-radius:.25rem}.flex{display:flex}.table{display:table}.table-row{display:table-row}.grid{display:grid}.hidden{display:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.flex-1{flex:1 1 0%}.font-bold{font-family:AdelleBold,Verdana}.font-bold{font-weight:700}.h-32{height:8rem}.text-xs{font-size:.75rem;line-height:1rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.leading-8{line-height:2rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-0{margin-left:0}.mr-4{margin-right:1rem}.mb-4{margin-bottom:1rem}.mb-24{margin-bottom:6rem}.max-w-sm{max-width:24rem}.min-h-screen{min-height:100vh}.opacity-0{opacity:0}.opacity-100{opacity:1}.p-2{padding:.5rem}.p-6{padding:1.5rem}.p-10{padding:2.5rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.px-10{padding-left:2.5rem;padding-right:2.5rem}.py-16{padding-top:4rem;padding-bottom:4rem}.pt-0{padding-top:0}.pt-12{padding-top:3rem}.fixed{position:fixed}.sticky{position:sticky}.top-0{top:0}.right-0{right:0}.bottom-0{bottom:0}*{--tw-shadow:0 0 #0000}*{--tw-ring-inset:var(--tw-empty, );/*!*//*!*/--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59, 130, 246, 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000}.text-white{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.w-full{width:100%}.gap-8{gap:2rem}.gap-10{gap:2.5rem}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.transform{--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;transform:translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-90{--tw-scale-x:.9;--tw-scale-y:.9}.scale-100{--tw-scale-x:1;--tw-scale-y:1}.transition{transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:150ms}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}@-webkit-keyframes spin{to{transform:rotate(360deg)}}@keyframes spin{to{transform:rotate(360deg)}}@-webkit-keyframes ping{100%,75%{transform:scale(2);opacity:0}}@keyframes ping{100%,75%{transform:scale(2);opacity:0}}@-webkit-keyframes pulse{50%{opacity:.5}}@keyframes pulse{50%{opacity:.5}}@-webkit-keyframes bounce{0%,100%{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@keyframes bounce{0%,100%{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}#preamble>.sectionbody>.paragraph>p{padding:1rem 0}.listingblock{padding:2rem 0;overflow-x:scroll}.listingblock>.content>.highlight>code{border:2px solid #e2e0d5;background-color:transparent;padding:15px 10px}div.admonitionblock{position:relative;margin:3rem 0}div.admonitionblock>table>tbody>tr>td.icon{border-radius:18px;color:#fff;display:flex;height:30px;justify-content:flex-start;left:5px;padding:0 1.5rem;position:absolute;top:-15px;width:135px}div.admonitionblock>table>tbody>tr>td.content{padding:20px 10px 15px 1rem}div.admonitionblock.caution{border:2px dashed #ac2a34}div.admonitionblock.caution>table>tbody>tr>td.icon{background:#ac2a34}div.admonitionblock.caution>table>tbody>tr>td.content{color:#ac2a34}div.admonitionblock.note{border:2px dashed #0f406b}div.admonitionblock.note>table>tbody>tr>td.icon{background:#0f406b}div.admonitionblock.note>table>tbody>tr>td.content{color:#0f406b}div.admonitionblock.warning{border:2px dashed #e37743}div.admonitionblock.warning>table>tbody>tr>td.icon{background:#e37743}div.admonitionblock.warning>table>tbody>tr>td.content{color:#e37743}h1,h2,h3,h4,h5{padding:1rem 0}h1{font-size:1.875rem}h2{font-size:1.375rem}h3{font-size:1.5rem}@media (min-width:768px){h1{font-size:3.75rem}h2{font-size:2.625rem}h3{font-size:1.325rem}}.split-button:active{transform:translate(2px,2px)}.started-header{background-image:url(/assets/get_started_header_bg.d41c50d2.svg);height:96px;background-position:center;background-repeat:no-repeat;background-size:cover;margin-bottom:3rem;position:relative;z-index:50}@media (min-width:768px){.started-header{margin-bottom:-3.5rem}}@media (min-width:768px){.started-header{background-image:url(/assets/get_started_header_bg.05d59513.svg);height:156px}}body{--tw-bg-opacity:1;background-color:rgba(237,225,113,var(--tw-bg-opacity));--tw-border-opacity:1;border-color:rgba(217,206,95,var(--tw-border-opacity));border-style:solid;font-family:Adelle,ui-serif}.nav{width:100%}@media (min-width:640px){.nav{max-width:640px}}@media (min-width:768px){.nav{max-width:768px}}@media (min-width:1024px){.nav{max-width:1024px}}@media (min-width:1280px){.nav{max-width:1280px}}@media (min-width:1536px){.nav{max-width:1536px}}.nav{display:flex;justify-content:space-between;margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem;padding-top:2rem;position:relative;width:100%}.nav__logo{width:75%}@media (min-width:768px){.nav__logo{width:18rem}}.nav__menu-icon{display:block;padding-right:.75rem;width:2.5rem}@media (min-width:768px){.nav__menu-icon{display:none}}.nav__links{display:none;position:absolute;right:0;text-transform:uppercase;gap:1.25rem}@media (min-width:768px){.nav__links{display:flex}}.nav__link{display:flex;flex-direction:column;justify-content:center;position:relative;text-align:center;width:7rem}.nav__link-underline{position:absolute;top:1.25rem}.nav__link__underline-docs{padding-top:.5rem;position:absolute;top:1.25rem}.nav-mobile{padding-top:3rem}@media (min-width:640px){.nav-mobile{display:none}}.nav-mobile__wrapper>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.nav-mobile__wrapper{padding-top:.5rem;padding-bottom:1rem}.nav-mobile__link{border-color:transparent;display:block;font-size:1.875rem;line-height:2.25rem;padding-top:.5rem;padding-bottom:.5rem;padding-right:1rem;padding-left:1rem;--tw-text-opacity:1;color:rgba(18,18,18,var(--tw-text-opacity))}.nav-mobile__links>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.nav-mobile__links{padding-top:.5rem;padding-bottom:1rem}.nav-mobile__heading{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity));display:flex;flex-direction:column;justify-content:flex-end;height:5rem;font-size:1.5rem;line-height:2rem;padding-bottom:.75rem;padding-left:1rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:100%}.nav-mobile__heading__title{display:flex;justify-content:space-between;padding-right:1rem}.nav-mobile__heading__close{display:block;padding-right:.75rem}@media (min-width:768px){.nav-mobile__heading__close{display:none}}.nav-mobile__links__level1{padding-left:3rem}.nav-mobile__links__level2{padding-left:1rem}.hero-bg{display:none;margin-top:-21rem;width:100%}@media (min-width:768px){.hero-bg{display:block}}.hero-bg-mobile{display:block;margin-top:-16rem;width:100%}@media (min-width:768px){.hero-bg-mobile{display:none}}.hero{width:100%}@media (min-width:640px){.hero{max-width:640px}}@media (min-width:768px){.hero{max-width:768px}}@media (min-width:1024px){.hero{max-width:1024px}}@media (min-width:1280px){.hero{max-width:1280px}}@media (min-width:1536px){.hero{max-width:1536px}}.hero{margin-left:auto;margin-right:auto;margin-top:3rem;padding-left:1rem;padding-right:1rem;padding-bottom:6rem}@media (min-width:768px){.hero{margin-top:6rem}}.hero-title-wrapper{justify-content:space-between;position:relative}.hero-title{font-size:1.875rem;line-height:2.25rem}@media (min-width:768px){.hero-title{font-size:2.25rem;line-height:2.5rem;line-height:1.625}}@media (min-width:1024px){.hero-title{font-size:3rem;line-height:1}}.hero-button{display:flex;margin-bottom:3rem;padding-top:1.25rem}@media (min-width:768px){.hero-button{padding-top:2.5rem}}.hero-button__content{--tw-border-opacity:1;border-color:rgba(210,199,91,var(--tw-border-opacity));width:13rem}.hero-button__split{--tw-bg-opacity:1;background-color:rgba(18,18,18,var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.feature-button{display:none;justify-content:center;margin-bottom:3rem}@media (min-width:768px){.feature-button{display:flex;padding-top:1.5rem}}.feature-button__content{--tw-border-opacity:1;border-color:rgba(196,91,40,var(--tw-border-opacity));width:24rem}.feature-button__split{--tw-bg-opacity:1;background-color:rgba(241,226,85,var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.split-button{background-color:transparent;border-radius:.75rem;border-style:solid;border-width:2px;display:flex;height:3rem;position:relative}.split-button__button{display:flex;position:absolute;left:-.5rem;top:-.75rem}.split-button__split1{border-radius:.75rem;display:inline-flex;align-items:center;font-weight:500;height:3rem;margin-right:.5rem;padding-top:.75rem;padding-bottom:.75rem;padding-left:1.5rem;padding-right:1.5rem}.split-button__split2{border-radius:.75rem;display:inline-flex;align-items:center;font-weight:500;height:3rem;font-size:1.125rem;line-height:1.75rem;padding-top:.75rem;padding-bottom:.75rem;padding-left:.75rem;padding-right:.75rem;--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.transition{width:100%}.feature__section{width:full;margin-top:-.5rem;background-color:#e37743}.feature__wrapper{width:100%}@media (min-width:640px){.feature__wrapper{max-width:640px}}@media (min-width:768px){.feature__wrapper{max-width:768px}}@media (min-width:1024px){.feature__wrapper{max-width:1024px}}@media (min-width:1280px){.feature__wrapper{max-width:1280px}}@media (min-width:1536px){.feature__wrapper{max-width:1536px}}.feature__wrapper{margin-left:auto;margin-right:auto;padding-left:0;padding-right:0;padding-top:3rem}@media (min-width:768px){.feature__wrapper{padding-left:1rem;padding-right:1rem}}.feature__title{font-size:1.875rem;line-height:2.25rem;margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem;width:100%}@media (min-width:768px){.feature__title{font-size:2.25rem;line-height:2.5rem;padding-left:0;padding-right:0;text-align:center;width:60%}}.feature__cards{display:flex;flex-wrap:wrap;justify-content:center;padding-top:3rem}.feature__card{display:flex;justify-content:center;padding-bottom:1.5rem;width:100%}@media (min-width:768px){.feature__card{margin-bottom:3.5rem;width:50%}}@media (min-width:1024px){.feature__card{width:33.333333%}}.feature__card__content{--tw-bg-opacity:1;background-color:rgba(208,79,34,var(--tw-bg-opacity));border-radius:0;padding-top:1rem;padding-bottom:1rem;padding-left:2rem;padding-right:2rem;width:100%}@media (min-width:768px){.feature__card__content{border-radius:1.5rem;height:20rem;width:20rem}}.feature__card__heading{display:inline-flex;align-items:center;padding-bottom:1.25rem}.feature__card__title{font-size:1.875rem;line-height:2.25rem;margin-left:1rem;margin-bottom:-.5rem}.feature__card__description{font-size:1.125rem;line-height:1.75rem}.components__section{--tw-bg-opacity:1;background-color:rgba(58,52,83,var(--tw-bg-opacity));flex-direction:column;align-items:center;margin-top:-.5rem;padding-top:3rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:100%}.component{width:100%}@media (min-width:640px){.component{max-width:640px}}@media (min-width:768px){.component{max-width:768px}}@media (min-width:1024px){.component{max-width:1024px}}@media (min-width:1280px){.component{max-width:1280px}}@media (min-width:1536px){.component{max-width:1536px}}.component{display:flex;flex-wrap:wrap;align-items:center;margin-left:auto;margin-right:auto;margin-bottom:3rem;padding-left:1rem;padding-right:1rem}.component__info{padding-right:0;padding-bottom:1rem;width:100%}@media (min-width:1024px){.component__info{padding-bottom:0;padding-right:2rem;width:33.333333%}}.component__info__title{padding-bottom:.75rem}.component__example{width:100%}@media (min-width:1024px){.component__example{width:66.666667%}}.component__example__code{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity));border-radius:.75rem;height:12rem}footer{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity));margin-top:-.5rem}.footer__wrapper{width:100%}@media (min-width:640px){.footer__wrapper{max-width:640px}}@media (min-width:768px){.footer__wrapper{max-width:768px}}@media (min-width:1024px){.footer__wrapper{max-width:1024px}}@media (min-width:1280px){.footer__wrapper{max-width:1280px}}@media (min-width:1536px){.footer__wrapper{max-width:1536px}}.footer__wrapper{margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem}.footer__nav{display:flex;flex-direction:column;justify-content:space-between;line-height:2rem;padding-top:3rem;width:100%;gap:2rem}@media (min-width:768px){.footer__nav{flex-direction:row;width:40%;gap:0}}.footer__nav__category{--tw-text-opacity:1;color:rgba(91,56,233,var(--tw-text-opacity))}.footer__nav__links{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));text-transform:uppercase}.footer_copyright{font-size:.875rem;line-height:1.25rem;padding-top:3rem;padding-bottom:3rem;--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.documentation{--tw-bg-opacity:1;background-color:rgba(250,247,230,var(--tw-bg-opacity));--tw-border-opacity:1;border-color:rgba(217,206,95,var(--tw-border-opacity));border-style:solid;line-height:2.25rem}.mobile-mode{background-color:#ede171!important}.top-border{border-top-width:4px}.sidebar{display:flex}.side-nav>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.side-nav{--tw-bg-opacity:1;background-color:rgba(242,239,217,var(--tw-bg-opacity));display:none;flex-direction:column;padding-left:3.5rem;padding-top:5rem;position:relative;width:21rem;z-index:0}@media (min-width:768px){.side-nav{display:flex}}.side-nav__expand>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.side-nav__expand__button{border-radius:.75rem;cursor:pointer;display:flex;align-items:center;height:3.5rem;font-size:1.125rem;line-height:1.75rem;padding-left:1rem;padding-right:1rem}.side-nav__expand__button:hover{--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.side-nav__expand__button{width:100%}.side-nav__link:hover{--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.side-nav__expand__icon{margin-left:auto}.side-nav__expand__list{padding-left:2rem}.side-nav__expand__list2{padding-left:1.5rem}.docs{width:100%}@media (min-width:640px){.docs{max-width:640px}}@media (min-width:768px){.docs{max-width:768px}}@media (min-width:1024px){.docs{max-width:1024px}}@media (min-width:1280px){.docs{max-width:1280px}}@media (min-width:1536px){.docs{max-width:1536px}}.docs{margin-left:auto;margin-right:auto;margin-left:0;margin-bottom:6rem;padding-left:1rem;padding-right:1rem;padding-top:0}@media (min-width:768px){.docs{margin-left:2rem;padding-top:8rem}}.separator{--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-style:solid;border-top-width:2px;margin-top:2.5rem;margin-bottom:2.5rem}.code-block{--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-style:solid;border-width:2px;margin-top:2.5rem;margin-bottom:2.5rem;width:100%}.code-block__content{margin:1.5rem}.bullet-list{margin-top:.5rem;margin-bottom:.5rem;margin-left:1rem}.bullet-list__callout>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.bullet-list__callout{display:flex}code.doc{overflow-x:auto;background-color:transparent;--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-width:2px;display:block;margin-top:1rem;margin-bottom:1rem;padding:1rem}.admon{border-style:dashed;border-width:2px;margin-top:2.5rem;margin-bottom:2.5rem;width:100%}.admon-warning{--tw-border-opacity:1;border-color:rgba(220,38,38,var(--tw-border-opacity))}.admon__button{border-radius:9999px;display:flex;align-items:center;justify-content:center;height:2rem;margin-left:1rem;margin-top:-1rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:8rem;gap:.5rem}.admon__button-warning{--tw-bg-opacity:1;background-color:rgba(220,38,38,var(--tw-bg-opacity))}.admon__button-note{--tw-bg-opacity:1;background-color:rgba(30,58,138,var(--tw-bg-opacity))}.admon-note{--tw-border-opacity:1;border-color:rgba(30,58,138,var(--tw-border-opacity))}.secondary-nav{margin-bottom:1.5rem}@media (min-width:768px){.secondary-nav{display:none}}.secondary-nav__content{--tw-border-opacity:1;border-color:rgba(210,199,91,var(--tw-border-opacity));width:10rem}.secondary-nav__split{--tw-bg-opacity:1;background-color:rgba(18,18,18,var(--tw-bg-opacity));border-radius:.75rem;display:inline-flex;align-items:center;justify-content:center;font-weight:500;height:3rem;padding-left:.5rem;padding-right:.5rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:9rem}.secondary-nav__split>p{margin-right:6px}@media (min-width:768px){.md\:flex-row{flex-direction:row}.md\:ml-8{margin-left:2rem}.md\:pt-32{padding-top:8rem}.md\:w-2\/5{width:40%}.md\:gap-0{gap:0}} + +.nav-mobile{ + display: block !important; +} + .CodeMirror { height: auto; } -- GitLab From 9933d23b6f19cc0d2a922306d0ce3e674b73c927 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 15 Apr 2021 21:27:38 -0600 Subject: [PATCH 042/116] GPL notices --- isreal/Main.hs | 19 ++ isreal/README.md | 5 + isreal/Shpadoinkle/Isreal/Types.hs | 18 ++ isreal/base.nix | 18 ++ isreal/deploy.nix | 18 ++ isreal/deploy/networking.nix | 4 +- isreal/module.nix | 18 ++ isreal/service.nix | 18 ++ isreal/swan-shell.nix | 18 ++ snowman/README.md | 8 +- snowman/default.nix | 18 ++ snowman/generate.sh | 18 ++ website/README.md | 5 + .../Shpadoinkle/Website/Page/Documentation.hs | 172 ++++++++++++++++-- website/Shpadoinkle/Website/Page/Home.hs | 4 +- website/Shpadoinkle/Website/Partials/Nav.hs | 2 +- .../Shpadoinkle/Website/Partials/Template.hs | 4 +- website/Shpadoinkle/Website/View.hs | 28 +-- website/assets/style.css | 2 +- website/docs/nav.adoc | 16 -- 20 files changed, 359 insertions(+), 54 deletions(-) delete mode 100644 website/docs/nav.adoc diff --git a/isreal/Main.hs b/isreal/Main.hs index c1b59b48..102f28eb 100644 --- a/isreal/Main.hs +++ b/isreal/Main.hs @@ -5,6 +5,25 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeApplications #-} + +{- + This file is part of Shpadoinkle Isreal. + + Shpadoinkle Isreal is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Shpadoinkle Isreal is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Shpadoinkle Isreal. If not, see . +-} + + module Main where diff --git a/isreal/README.md b/isreal/README.md index eb319fc4..220a11d4 100644 --- a/isreal/README.md +++ b/isreal/README.md @@ -64,3 +64,8 @@ Clean up your token: ```bash curl -X DELETE http://localhost:8080/clean/hello-token ``` + +## GPL License + +Shpadoinkle Isreal (all files within this directory), are provided under the terms of the GNU +General Public License v3.0 diff --git a/isreal/Shpadoinkle/Isreal/Types.hs b/isreal/Shpadoinkle/Isreal/Types.hs index eb561c14..617d7341 100644 --- a/isreal/Shpadoinkle/Isreal/Types.hs +++ b/isreal/Shpadoinkle/Isreal/Types.hs @@ -11,6 +11,24 @@ {-# LANGUAGE TypeOperators #-} +{- + This file is part of Shpadoinkle Isreal. + + Shpadoinkle Isreal is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Shpadoinkle Isreal is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Shpadoinkle Isreal. If not, see . +-} + + module Shpadoinkle.Isreal.Types ( Options (..) , CompileError (..) diff --git a/isreal/base.nix b/isreal/base.nix index ded4f90f..3a447b24 100644 --- a/isreal/base.nix +++ b/isreal/base.nix @@ -1,3 +1,21 @@ + + +# This file is part of Shpadoinkle Isreal. +# +# Shpadoinkle Isreal is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Shpadoinkle Isreal is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Shpadoinkle Isreal. If not, see . + + { pkgs, ...}: { boot.cleanTmpDir = true; diff --git a/isreal/deploy.nix b/isreal/deploy.nix index 908558a8..8e0cbb8d 100644 --- a/isreal/deploy.nix +++ b/isreal/deploy.nix @@ -1,3 +1,21 @@ + + +# This file is part of Shpadoinkle Isreal. +# +# Shpadoinkle Isreal is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Shpadoinkle Isreal is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Shpadoinkle Isreal. If not, see . + + { network.description = "colorado"; isreal-swan = { diff --git a/isreal/deploy/networking.nix b/isreal/deploy/networking.nix index d78754c1..72f08211 100644 --- a/isreal/deploy/networking.nix +++ b/isreal/deploy/networking.nix @@ -12,7 +12,7 @@ eth0 = { ipv4.addresses = [ { address="104.131.28.246"; prefixLength=18; } -{ address="10.17.0.5"; prefixLength=16; } + { address="10.17.0.5"; prefixLength=16; } ]; ipv6.addresses = [ { address="fe80::e8a3:8cff:fe62:6d3b"; prefixLength=64; } @@ -20,7 +20,7 @@ ipv4.routes = [ { address = "104.131.0.1"; prefixLength = 32; } ]; ipv6.routes = [ { address = ""; prefixLength = 32; } ]; }; - + }; }; services.udev.extraRules = '' diff --git a/isreal/module.nix b/isreal/module.nix index ab477c7b..3c695cdb 100644 --- a/isreal/module.nix +++ b/isreal/module.nix @@ -1,3 +1,21 @@ + + +# This file is part of Shpadoinkle Isreal. +# +# Shpadoinkle Isreal is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Shpadoinkle Isreal is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Shpadoinkle Isreal. If not, see . + + { lib, config, ... }: with lib; let diff --git a/isreal/service.nix b/isreal/service.nix index cba44782..7d4ef340 100644 --- a/isreal/service.nix +++ b/isreal/service.nix @@ -1,3 +1,21 @@ + + +# This file is part of Shpadoinkle Isreal. +# +# Shpadoinkle Isreal is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Shpadoinkle Isreal is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Shpadoinkle Isreal. If not, see . + + let ports = { isreal = 8080; hoogle = 8090; }; in { services = { diff --git a/isreal/swan-shell.nix b/isreal/swan-shell.nix index 94954a08..3272e850 100644 --- a/isreal/swan-shell.nix +++ b/isreal/swan-shell.nix @@ -1,3 +1,21 @@ + + +# This file is part of Shpadoinkle Isreal. +# +# Shpadoinkle Isreal is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Shpadoinkle Isreal is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Shpadoinkle Isreal. If not, see . + + { chan ? (import ../nix/chan.nix) , enableLibraryProfiling ? false , enableExecutableProfiling ? false diff --git a/snowman/README.md b/snowman/README.md index e85e019d..9ff026fe 100644 --- a/snowman/README.md +++ b/snowman/README.md @@ -73,6 +73,10 @@ Get a hoogle server in one line nix-shell --arg withHoogle true --command "hoogle serve" ``` -### License +## GPL and CC0 Licenses -The code to generate a snowman is GPL-3 licensed. However the code generated on your local machine is CC0. +Shpadoinkle Snowman (all files within this directory), are provided under the terms of the GNU +General Public License v3.0. + +However, the code generated by Shpadoinkle Snowman is licensed under the terms of the +Creative Commons Zero License. Which implies "No Rights Reserved". diff --git a/snowman/default.nix b/snowman/default.nix index e5bcd226..2c34a922 100644 --- a/snowman/default.nix +++ b/snowman/default.nix @@ -1,3 +1,21 @@ + + +# This file is part of Shpadoinkle Snowman. +# +# Shpadoinkle Snowman is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Shpadoinkle Snowman is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Shpadoinkle Snowman. If not, see . + + { chan ? (import ../nix/chan.nix) }: let pkgs = import ../nix/pkgs.nix { inherit chan; }; in pkgs.writeShellScriptBin "swan" '' diff --git a/snowman/generate.sh b/snowman/generate.sh index a1a41e9e..38f28bab 100644 --- a/snowman/generate.sh +++ b/snowman/generate.sh @@ -1,5 +1,23 @@ #!/usr/bin/env bash + + +# This file is part of Shpadoinkle Snowman. +# +# Shpadoinkle Snowman is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Shpadoinkle Snowman is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Shpadoinkle Snowman. If not, see . + + set -eu if ! command -v nix-build &> /dev/null diff --git a/website/README.md b/website/README.md index 2a788f02..5e1f25c9 100644 --- a/website/README.md +++ b/website/README.md @@ -1 +1,6 @@ # Shpadoinkle Marketing + +## GPL License + +Shpadoinkle Isreal (all files within this directory), are provided under the terms of the GNU +General Public License v3.0 diff --git a/website/Shpadoinkle/Website/Page/Documentation.hs b/website/Shpadoinkle/Website/Page/Documentation.hs index d6b5845c..3849e238 100644 --- a/website/Shpadoinkle/Website/Page/Documentation.hs +++ b/website/Shpadoinkle/Website/Page/Documentation.hs @@ -8,6 +8,7 @@ module Shpadoinkle.Website.Page.Documentation where import Data.Text import Shpadoinkle.Html import Shpadoinkle.Template.TH +import Shpadoinkle.Website.Style hiding (nav) import qualified Shpadoinkle.Website.Types.Routes.GettingStarted as GettingStarted import qualified Shpadoinkle.Website.Types.Routes.Packages as Packages import qualified Shpadoinkle.Website.Types.Routes.Tutorial as Tutorial @@ -17,22 +18,22 @@ concepts , gsIndex, gsAddingToYourProject, gsExtendAnExample , rpIndex, rpCore, rpConsole, rpBackends, rpHtml, rpLens, rpRouter, rpWidgets , rtIndex, rtCalculator, rtImmediateExecution, rtComposing :: Html m a -concepts = div_ [] -- $(embedAsciidoc "./docs/concepts.adoc") -gsIndex = div_ $(embedAsciidoc "./docs/getting-started/index.adoc") -gsAddingToYourProject = div_ [] -- $(embedAsciidoc "./docs/getting-started/adding-to-your-project.adoc") -gsExtendAnExample = div_ [] -- $(embedAsciidoc "./docs/getting-started/extend-an-example.adoc") -rpIndex = div_ [] -- $(embedAsciidoc "./docs/packages/index.adoc") -rpCore = div_ [] -- $(embedAsciidoc "./docs/packages/core.adoc") -rpConsole = div_ [] -- $(embedAsciidoc "./docs/packages/console.adoc") -rpBackends = div_ [] -- $(embedAsciidoc "./docs/packages/backends.adoc") -rpHtml = div_ [] -- $(embedAsciidoc "./docs/packages/html.adoc") -rpLens = div_ [] -- $(embedAsciidoc "./docs/packages/lens.adoc") -rpRouter = div_ [] -- $(embedAsciidoc "./docs/packages/router.adoc") -rpWidgets = div_ [] -- $(embedAsciidoc "./docs/packages/widgets.adoc") -rtIndex = div_ [] -- $(embedAsciidoc "./docs/tutorial/index.adoc") -rtCalculator = div_ [] -- $(embedAsciidoc "./docs/tutorial/calculator.adoc") -rtImmediateExecution = div_ [] -- $(embedAsciidoc "./docs/tutorial/immediate-execution.adoc") -rtComposing = div_ [] -- $(embedAsciidoc "./docs/tutorial/composing.adoc") +concepts = docsWrap $(embedAsciidoc "./docs/concepts.adoc") +gsIndex = docsWrap $(embedAsciidoc "./docs/getting-started/index.adoc") +gsAddingToYourProject = docsWrap $(embedAsciidoc "./docs/getting-started/adding-to-your-project.adoc") +gsExtendAnExample = docsWrap $(embedAsciidoc "./docs/getting-started/extend-an-example.adoc") +rpIndex = docsWrap $(embedAsciidoc "./docs/packages/index.adoc") +rpCore = docsWrap $(embedAsciidoc "./docs/packages/core.adoc") +rpConsole = docsWrap $(embedAsciidoc "./docs/packages/console.adoc") +rpBackends = docsWrap $(embedAsciidoc "./docs/packages/backends.adoc") +rpHtml = docsWrap $(embedAsciidoc "./docs/packages/html.adoc") +rpLens = docsWrap $(embedAsciidoc "./docs/packages/lens.adoc") +rpRouter = docsWrap $(embedAsciidoc "./docs/packages/router.adoc") +rpWidgets = docsWrap $(embedAsciidoc "./docs/packages/widgets.adoc") +rtIndex = docsWrap $(embedAsciidoc "./docs/tutorial/index.adoc") +rtCalculator = docsWrap $(embedAsciidoc "./docs/tutorial/calculator.adoc") +rtImmediateExecution = docsWrap $(embedAsciidoc "./docs/tutorial/immediate-execution.adoc") +rtComposing = docsWrap $(embedAsciidoc "./docs/tutorial/composing.adoc") gettingStarted :: GettingStarted.Route -> Html m a @@ -60,3 +61,142 @@ tutorial = \case Tutorial.RTCalculator -> rtCalculator Tutorial.RTImmediateExecution -> rtImmediateExecution Tutorial.RTComposing -> rtComposing + + + {- + + -} + +docsWrap :: [Html m a] -> Html m a +docsWrap content' = section [class' flex ] + [ nav [ class' side_nav ] [] + , h "main" [ class' docs ] content' + ] diff --git a/website/Shpadoinkle/Website/Page/Home.hs b/website/Shpadoinkle/Website/Page/Home.hs index 3b7a86c3..57af926b 100644 --- a/website/Shpadoinkle/Website/Page/Home.hs +++ b/website/Shpadoinkle/Website/Page/Home.hs @@ -36,8 +36,8 @@ import Shpadoinkle.Website.Types.Routes (Route (RGettin import Shpadoinkle.Website.Types.Routes.GettingStarted (Route (RGSIndex)) -view :: (MonadJSM m, ExampleEffects m) => Home -> [Html m Home] -view x = [ hero', featureSection, onRecordEndo #examples componentsSection x ] +view :: (MonadJSM m, ExampleEffects m) => Home -> Html m Home +view x = h "main" [] [ hero', featureSection, onRecordEndo #examples componentsSection x ] heroTitle :: Text -> Html m a diff --git a/website/Shpadoinkle/Website/Partials/Nav.hs b/website/Shpadoinkle/Website/Partials/Nav.hs index 2c27f719..b4448c5a 100644 --- a/website/Shpadoinkle/Website/Partials/Nav.hs +++ b/website/Shpadoinkle/Website/Partials/Nav.hs @@ -37,7 +37,7 @@ navLinkMobile n = a [ class' nav_mobile__link, href "#" ] [ text $ humanize n ] view :: MonadJSM m => Toggle -> Frontend -> [Html m Toggle] -view t r = +view t r = pure $ div [ class' started_header ] [ H.nav [ class' Style.nav ] [ a [ href "/" ] [ img' [ class' nav__logo, src $(assetLink "/assets/landing_logo.svg") ] diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index 201d557b..c54a34fe 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -60,8 +60,8 @@ template ev fe content' = html [ lang "en" ] ] -wrapper :: MonadJSM m => CurrentYear -> Toggle -> Frontend -> [Html m a] -> [Html m (Toggle, a)] +wrapper :: MonadJSM m => CurrentYear -> Toggle -> Frontend -> Html m a -> [Html m (Toggle, a)] wrapper yc t fe content' = (generalize _1 <$> Nav.view t fe) <> (generalize _2 <$> - [ h "main" [] content' + [ content' , Footer.view yc ]) diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index 9f20a9df..f0b0bb3f 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -32,6 +32,7 @@ import Shpadoinkle.Website.Component.LiveExample import qualified Shpadoinkle.Website.Page.Documentation as Documentation import qualified Shpadoinkle.Website.Page.Home as Home import Shpadoinkle.Website.Partials.Template +import Shpadoinkle.Website.Style (documentation) import Shpadoinkle.Website.Types.CurrentYear import Shpadoinkle.Website.Types.Effects.Example import Shpadoinkle.Website.Types.Example (Example) @@ -54,15 +55,21 @@ fourOhFour :: Html m a fourOhFour = h2_ [ "404" ] +topClass :: Frontend -> ClassList +topClass = \case + MHome _ -> mempty + _ -> documentation + + view :: (MonadJSM m, ExampleEffects m) => CurrentYear -> (Toggle, Frontend) -> Html m (Toggle, Frontend) -view cy (t, fe) = div_ . wrapper cy t fe $ case fe of - MHome x -> onSum #_MHome <$> Home.view x - MConcepts -> [ Documentation.concepts ] - MGettingStarted gsr -> [ Documentation.gettingStarted gsr ] - MPackages pr -> [ Documentation.packages pr ] - MTutorial tutr -> [ Documentation.tutorial tutr ] - MSandbox -> [] - MFourOhFour -> [ fourOhFour ] +view cy (t, fe) = div [ class' $ topClass fe ] . wrapper cy t fe $ case fe of + MHome x -> onSum #_MHome $ Home.view x + MConcepts -> Documentation.concepts + MGettingStarted gsr -> Documentation.gettingStarted gsr + MPackages pr -> Documentation.packages pr + MTutorial tutr -> Documentation.tutorial tutr + MSandbox -> "" + MFourOhFour -> fourOhFour compileExamples :: forall m. (MonadUnliftIO m, MonadJSM m, ExampleEffects m) => Home -> m Home @@ -80,10 +87,7 @@ compileExamples home' = runConcurrently $ do genExampleTokens :: MonadIO m => m (Examples SnowToken) -genExampleTokens = liftIO $ do - e <- Examples <$> genSnowToken <*> genSnowToken <*> genSnowToken - print e - return e +genExampleTokens = liftIO $ Examples <$> genSnowToken <*> genSnowToken <*> genSnowToken start :: MonadIO m => Route -> m (Toggle, Frontend) diff --git a/website/assets/style.css b/website/assets/style.css index 2c8341cc..93387d84 100644 --- a/website/assets/style.css +++ b/website/assets/style.css @@ -1,4 +1,4 @@ -@font-face{font-family:Adelle;src:url(/assets/Adelle-Regular.woff2) format('woff2');font-weight:400;font-style:normal}@font-face{font-family:AdelleBold;src:url(/assets/Adelle-Bold.woff2) format('woff2');font-weight:400;font-style:normal}/*! modern-normalize v1.0.0 | MIT License | https://github.com/sindresorhus/modern-normalize */*,::after,::before{box-sizing:border-box}:root{-moz-tab-size:4;-o-tab-size:4;tab-size:4}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}body{font-family:system-ui,-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji'}hr{height:0;color:inherit}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Consolas,'Liberation Mono',Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],button{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}button{background-color:transparent;background-image:none}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}fieldset{margin:0;padding:0}ol,ul{list-style:none;margin:0;padding:0}html{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";line-height:1.5}body{font-family:inherit;line-height:inherit}*,::after,::before{box-sizing:border-box;border-width:0;border-style:solid;border-color:currentColor}hr{border-top-width:1px}img{border-style:solid}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#a1a1aa}input:-ms-input-placeholder,textarea:-ms-input-placeholder{opacity:1;color:#a1a1aa}input::placeholder,textarea::placeholder{opacity:1;color:#a1a1aa}button{cursor:pointer}table{border-collapse:collapse}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,optgroup,select,textarea{padding:0;line-height:inherit;color:inherit}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.rounded{border-radius:.25rem}.flex{display:flex}.table{display:table}.table-row{display:table-row}.grid{display:grid}.hidden{display:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.flex-1{flex:1 1 0%}.font-bold{font-family:AdelleBold,Verdana}.font-bold{font-weight:700}.h-32{height:8rem}.text-xs{font-size:.75rem;line-height:1rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.leading-8{line-height:2rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-0{margin-left:0}.mr-4{margin-right:1rem}.mb-4{margin-bottom:1rem}.mb-24{margin-bottom:6rem}.max-w-sm{max-width:24rem}.min-h-screen{min-height:100vh}.opacity-0{opacity:0}.opacity-100{opacity:1}.p-2{padding:.5rem}.p-6{padding:1.5rem}.p-10{padding:2.5rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.px-10{padding-left:2.5rem;padding-right:2.5rem}.py-16{padding-top:4rem;padding-bottom:4rem}.pt-0{padding-top:0}.pt-12{padding-top:3rem}.fixed{position:fixed}.sticky{position:sticky}.top-0{top:0}.right-0{right:0}.bottom-0{bottom:0}*{--tw-shadow:0 0 #0000}*{--tw-ring-inset:var(--tw-empty, );/*!*//*!*/--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59, 130, 246, 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000}.text-white{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.w-full{width:100%}.gap-8{gap:2rem}.gap-10{gap:2.5rem}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.transform{--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;transform:translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-90{--tw-scale-x:.9;--tw-scale-y:.9}.scale-100{--tw-scale-x:1;--tw-scale-y:1}.transition{transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:150ms}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}@-webkit-keyframes spin{to{transform:rotate(360deg)}}@keyframes spin{to{transform:rotate(360deg)}}@-webkit-keyframes ping{100%,75%{transform:scale(2);opacity:0}}@keyframes ping{100%,75%{transform:scale(2);opacity:0}}@-webkit-keyframes pulse{50%{opacity:.5}}@keyframes pulse{50%{opacity:.5}}@-webkit-keyframes bounce{0%,100%{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@keyframes bounce{0%,100%{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}#preamble>.sectionbody>.paragraph>p{padding:1rem 0}.listingblock{padding:2rem 0;overflow-x:scroll}.listingblock>.content>.highlight>code{border:2px solid #e2e0d5;background-color:transparent;padding:15px 10px}div.admonitionblock{position:relative;margin:3rem 0}div.admonitionblock>table>tbody>tr>td.icon{border-radius:18px;color:#fff;display:flex;height:30px;justify-content:flex-start;left:5px;padding:0 1.5rem;position:absolute;top:-15px;width:135px}div.admonitionblock>table>tbody>tr>td.content{padding:20px 10px 15px 1rem}div.admonitionblock.caution{border:2px dashed #ac2a34}div.admonitionblock.caution>table>tbody>tr>td.icon{background:#ac2a34}div.admonitionblock.caution>table>tbody>tr>td.content{color:#ac2a34}div.admonitionblock.note{border:2px dashed #0f406b}div.admonitionblock.note>table>tbody>tr>td.icon{background:#0f406b}div.admonitionblock.note>table>tbody>tr>td.content{color:#0f406b}div.admonitionblock.warning{border:2px dashed #e37743}div.admonitionblock.warning>table>tbody>tr>td.icon{background:#e37743}div.admonitionblock.warning>table>tbody>tr>td.content{color:#e37743}h1,h2,h3,h4,h5{padding:1rem 0}h1{font-size:1.875rem}h2{font-size:1.375rem}h3{font-size:1.5rem}@media (min-width:768px){h1{font-size:3.75rem}h2{font-size:2.625rem}h3{font-size:1.325rem}}.split-button:active{transform:translate(2px,2px)}.started-header{background-image:url(/assets/get_started_header_bg.d41c50d2.svg);height:96px;background-position:center;background-repeat:no-repeat;background-size:cover;margin-bottom:3rem;position:relative;z-index:50}@media (min-width:768px){.started-header{margin-bottom:-3.5rem}}@media (min-width:768px){.started-header{background-image:url(/assets/get_started_header_bg.05d59513.svg);height:156px}}body{--tw-bg-opacity:1;background-color:rgba(237,225,113,var(--tw-bg-opacity));--tw-border-opacity:1;border-color:rgba(217,206,95,var(--tw-border-opacity));border-style:solid;font-family:Adelle,ui-serif}.nav{width:100%}@media (min-width:640px){.nav{max-width:640px}}@media (min-width:768px){.nav{max-width:768px}}@media (min-width:1024px){.nav{max-width:1024px}}@media (min-width:1280px){.nav{max-width:1280px}}@media (min-width:1536px){.nav{max-width:1536px}}.nav{display:flex;justify-content:space-between;margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem;padding-top:2rem;position:relative;width:100%}.nav__logo{width:75%}@media (min-width:768px){.nav__logo{width:18rem}}.nav__menu-icon{display:block;padding-right:.75rem;width:2.5rem}@media (min-width:768px){.nav__menu-icon{display:none}}.nav__links{display:none;position:absolute;right:0;text-transform:uppercase;gap:1.25rem}@media (min-width:768px){.nav__links{display:flex}}.nav__link{display:flex;flex-direction:column;justify-content:center;position:relative;text-align:center;width:7rem}.nav__link-underline{position:absolute;top:1.25rem}.nav__link__underline-docs{padding-top:.5rem;position:absolute;top:1.25rem}.nav-mobile{padding-top:3rem}@media (min-width:640px){.nav-mobile{display:none}}.nav-mobile__wrapper>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.nav-mobile__wrapper{padding-top:.5rem;padding-bottom:1rem}.nav-mobile__link{border-color:transparent;display:block;font-size:1.875rem;line-height:2.25rem;padding-top:.5rem;padding-bottom:.5rem;padding-right:1rem;padding-left:1rem;--tw-text-opacity:1;color:rgba(18,18,18,var(--tw-text-opacity))}.nav-mobile__links>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.nav-mobile__links{padding-top:.5rem;padding-bottom:1rem}.nav-mobile__heading{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity));display:flex;flex-direction:column;justify-content:flex-end;height:5rem;font-size:1.5rem;line-height:2rem;padding-bottom:.75rem;padding-left:1rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:100%}.nav-mobile__heading__title{display:flex;justify-content:space-between;padding-right:1rem}.nav-mobile__heading__close{display:block;padding-right:.75rem}@media (min-width:768px){.nav-mobile__heading__close{display:none}}.nav-mobile__links__level1{padding-left:3rem}.nav-mobile__links__level2{padding-left:1rem}.hero-bg{display:none;margin-top:-21rem;width:100%}@media (min-width:768px){.hero-bg{display:block}}.hero-bg-mobile{display:block;margin-top:-16rem;width:100%}@media (min-width:768px){.hero-bg-mobile{display:none}}.hero{width:100%}@media (min-width:640px){.hero{max-width:640px}}@media (min-width:768px){.hero{max-width:768px}}@media (min-width:1024px){.hero{max-width:1024px}}@media (min-width:1280px){.hero{max-width:1280px}}@media (min-width:1536px){.hero{max-width:1536px}}.hero{margin-left:auto;margin-right:auto;margin-top:3rem;padding-left:1rem;padding-right:1rem;padding-bottom:6rem}@media (min-width:768px){.hero{margin-top:6rem}}.hero-title-wrapper{justify-content:space-between;position:relative}.hero-title{font-size:1.875rem;line-height:2.25rem}@media (min-width:768px){.hero-title{font-size:2.25rem;line-height:2.5rem;line-height:1.625}}@media (min-width:1024px){.hero-title{font-size:3rem;line-height:1}}.hero-button{display:flex;margin-bottom:3rem;padding-top:1.25rem}@media (min-width:768px){.hero-button{padding-top:2.5rem}}.hero-button__content{--tw-border-opacity:1;border-color:rgba(210,199,91,var(--tw-border-opacity));width:13rem}.hero-button__split{--tw-bg-opacity:1;background-color:rgba(18,18,18,var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.feature-button{display:none;justify-content:center;margin-bottom:3rem}@media (min-width:768px){.feature-button{display:flex;padding-top:1.5rem}}.feature-button__content{--tw-border-opacity:1;border-color:rgba(196,91,40,var(--tw-border-opacity));width:24rem}.feature-button__split{--tw-bg-opacity:1;background-color:rgba(241,226,85,var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.split-button{background-color:transparent;border-radius:.75rem;border-style:solid;border-width:2px;display:flex;height:3rem;position:relative}.split-button__button{display:flex;position:absolute;left:-.5rem;top:-.75rem}.split-button__split1{border-radius:.75rem;display:inline-flex;align-items:center;font-weight:500;height:3rem;margin-right:.5rem;padding-top:.75rem;padding-bottom:.75rem;padding-left:1.5rem;padding-right:1.5rem}.split-button__split2{border-radius:.75rem;display:inline-flex;align-items:center;font-weight:500;height:3rem;font-size:1.125rem;line-height:1.75rem;padding-top:.75rem;padding-bottom:.75rem;padding-left:.75rem;padding-right:.75rem;--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.transition{width:100%}.feature__section{width:full;margin-top:-.5rem;background-color:#e37743}.feature__wrapper{width:100%}@media (min-width:640px){.feature__wrapper{max-width:640px}}@media (min-width:768px){.feature__wrapper{max-width:768px}}@media (min-width:1024px){.feature__wrapper{max-width:1024px}}@media (min-width:1280px){.feature__wrapper{max-width:1280px}}@media (min-width:1536px){.feature__wrapper{max-width:1536px}}.feature__wrapper{margin-left:auto;margin-right:auto;padding-left:0;padding-right:0;padding-top:3rem}@media (min-width:768px){.feature__wrapper{padding-left:1rem;padding-right:1rem}}.feature__title{font-size:1.875rem;line-height:2.25rem;margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem;width:100%}@media (min-width:768px){.feature__title{font-size:2.25rem;line-height:2.5rem;padding-left:0;padding-right:0;text-align:center;width:60%}}.feature__cards{display:flex;flex-wrap:wrap;justify-content:center;padding-top:3rem}.feature__card{display:flex;justify-content:center;padding-bottom:1.5rem;width:100%}@media (min-width:768px){.feature__card{margin-bottom:3.5rem;width:50%}}@media (min-width:1024px){.feature__card{width:33.333333%}}.feature__card__content{--tw-bg-opacity:1;background-color:rgba(208,79,34,var(--tw-bg-opacity));border-radius:0;padding-top:1rem;padding-bottom:1rem;padding-left:2rem;padding-right:2rem;width:100%}@media (min-width:768px){.feature__card__content{border-radius:1.5rem;height:20rem;width:20rem}}.feature__card__heading{display:inline-flex;align-items:center;padding-bottom:1.25rem}.feature__card__title{font-size:1.875rem;line-height:2.25rem;margin-left:1rem;margin-bottom:-.5rem}.feature__card__description{font-size:1.125rem;line-height:1.75rem}.components__section{--tw-bg-opacity:1;background-color:rgba(58,52,83,var(--tw-bg-opacity));flex-direction:column;align-items:center;margin-top:-.5rem;padding-top:3rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:100%}.component{width:100%}@media (min-width:640px){.component{max-width:640px}}@media (min-width:768px){.component{max-width:768px}}@media (min-width:1024px){.component{max-width:1024px}}@media (min-width:1280px){.component{max-width:1280px}}@media (min-width:1536px){.component{max-width:1536px}}.component{display:flex;flex-wrap:wrap;align-items:center;margin-left:auto;margin-right:auto;margin-bottom:3rem;padding-left:1rem;padding-right:1rem}.component__info{padding-right:0;padding-bottom:1rem;width:100%}@media (min-width:1024px){.component__info{padding-bottom:0;padding-right:2rem;width:33.333333%}}.component__info__title{padding-bottom:.75rem}.component__example{width:100%}@media (min-width:1024px){.component__example{width:66.666667%}}.component__example__code{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity));border-radius:.75rem;height:12rem}footer{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity));margin-top:-.5rem}.footer__wrapper{width:100%}@media (min-width:640px){.footer__wrapper{max-width:640px}}@media (min-width:768px){.footer__wrapper{max-width:768px}}@media (min-width:1024px){.footer__wrapper{max-width:1024px}}@media (min-width:1280px){.footer__wrapper{max-width:1280px}}@media (min-width:1536px){.footer__wrapper{max-width:1536px}}.footer__wrapper{margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem}.footer__nav{display:flex;flex-direction:column;justify-content:space-between;line-height:2rem;padding-top:3rem;width:100%;gap:2rem}@media (min-width:768px){.footer__nav{flex-direction:row;width:40%;gap:0}}.footer__nav__category{--tw-text-opacity:1;color:rgba(91,56,233,var(--tw-text-opacity))}.footer__nav__links{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));text-transform:uppercase}.footer_copyright{font-size:.875rem;line-height:1.25rem;padding-top:3rem;padding-bottom:3rem;--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.documentation{--tw-bg-opacity:1;background-color:rgba(250,247,230,var(--tw-bg-opacity));--tw-border-opacity:1;border-color:rgba(217,206,95,var(--tw-border-opacity));border-style:solid;line-height:2.25rem}.mobile-mode{background-color:#ede171!important}.top-border{border-top-width:4px}.sidebar{display:flex}.side-nav>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.side-nav{--tw-bg-opacity:1;background-color:rgba(242,239,217,var(--tw-bg-opacity));display:none;flex-direction:column;padding-left:3.5rem;padding-top:5rem;position:relative;width:21rem;z-index:0}@media (min-width:768px){.side-nav{display:flex}}.side-nav__expand>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.side-nav__expand__button{border-radius:.75rem;cursor:pointer;display:flex;align-items:center;height:3.5rem;font-size:1.125rem;line-height:1.75rem;padding-left:1rem;padding-right:1rem}.side-nav__expand__button:hover{--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.side-nav__expand__button{width:100%}.side-nav__link:hover{--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.side-nav__expand__icon{margin-left:auto}.side-nav__expand__list{padding-left:2rem}.side-nav__expand__list2{padding-left:1.5rem}.docs{width:100%}@media (min-width:640px){.docs{max-width:640px}}@media (min-width:768px){.docs{max-width:768px}}@media (min-width:1024px){.docs{max-width:1024px}}@media (min-width:1280px){.docs{max-width:1280px}}@media (min-width:1536px){.docs{max-width:1536px}}.docs{margin-left:auto;margin-right:auto;margin-left:0;margin-bottom:6rem;padding-left:1rem;padding-right:1rem;padding-top:0}@media (min-width:768px){.docs{margin-left:2rem;padding-top:8rem}}.separator{--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-style:solid;border-top-width:2px;margin-top:2.5rem;margin-bottom:2.5rem}.code-block{--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-style:solid;border-width:2px;margin-top:2.5rem;margin-bottom:2.5rem;width:100%}.code-block__content{margin:1.5rem}.bullet-list{margin-top:.5rem;margin-bottom:.5rem;margin-left:1rem}.bullet-list__callout>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.bullet-list__callout{display:flex}code.doc{overflow-x:auto;background-color:transparent;--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-width:2px;display:block;margin-top:1rem;margin-bottom:1rem;padding:1rem}.admon{border-style:dashed;border-width:2px;margin-top:2.5rem;margin-bottom:2.5rem;width:100%}.admon-warning{--tw-border-opacity:1;border-color:rgba(220,38,38,var(--tw-border-opacity))}.admon__button{border-radius:9999px;display:flex;align-items:center;justify-content:center;height:2rem;margin-left:1rem;margin-top:-1rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:8rem;gap:.5rem}.admon__button-warning{--tw-bg-opacity:1;background-color:rgba(220,38,38,var(--tw-bg-opacity))}.admon__button-note{--tw-bg-opacity:1;background-color:rgba(30,58,138,var(--tw-bg-opacity))}.admon-note{--tw-border-opacity:1;border-color:rgba(30,58,138,var(--tw-border-opacity))}.secondary-nav{margin-bottom:1.5rem}@media (min-width:768px){.secondary-nav{display:none}}.secondary-nav__content{--tw-border-opacity:1;border-color:rgba(210,199,91,var(--tw-border-opacity));width:10rem}.secondary-nav__split{--tw-bg-opacity:1;background-color:rgba(18,18,18,var(--tw-bg-opacity));border-radius:.75rem;display:inline-flex;align-items:center;justify-content:center;font-weight:500;height:3rem;padding-left:.5rem;padding-right:.5rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:9rem}.secondary-nav__split>p{margin-right:6px}@media (min-width:768px){.md\:flex-row{flex-direction:row}.md\:ml-8{margin-left:2rem}.md\:pt-32{padding-top:8rem}.md\:w-2\/5{width:40%}.md\:gap-0{gap:0}} +@font-face{font-family:Adelle;src:url(/assets/Adelle-Regular.woff2) format('woff2');font-weight:400;font-style:normal}@font-face{font-family:AdelleBold;src:url(/assets/Adelle-Bold.woff2) format('woff2');font-weight:400;font-style:normal}/*! modern-normalize v1.0.0 | MIT License | https://github.com/sindresorhus/modern-normalize */*,::after,::before{box-sizing:border-box}:root{-moz-tab-size:4;-o-tab-size:4;tab-size:4}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}body{font-family:system-ui,-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji'}hr{height:0;color:inherit}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Consolas,'Liberation Mono',Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],button{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}button{background-color:transparent;background-image:none}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}fieldset{margin:0;padding:0}ol,ul{list-style:none;margin:0;padding:0}html{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";line-height:1.5}body{font-family:inherit;line-height:inherit}*,::after,::before{box-sizing:border-box;border-width:0;border-style:solid;border-color:currentColor}hr{border-top-width:1px}img{border-style:solid}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#a1a1aa}input:-ms-input-placeholder,textarea:-ms-input-placeholder{opacity:1;color:#a1a1aa}input::placeholder,textarea::placeholder{opacity:1;color:#a1a1aa}button{cursor:pointer}table{border-collapse:collapse}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,optgroup,select,textarea{padding:0;line-height:inherit;color:inherit}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.rounded{border-radius:.25rem}.flex{display:flex}.table{display:table}.table-row{display:table-row}.grid{display:grid}.hidden{display:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.flex-1{flex:1 1 0%}.font-bold{font-family:AdelleBold,Verdana}.font-bold{font-weight:700}.h-32{height:8rem}.text-xs{font-size:.75rem;line-height:1rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.leading-8{line-height:2rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-0{margin-left:0}.mr-4{margin-right:1rem}.mb-4{margin-bottom:1rem}.mb-24{margin-bottom:6rem}.max-w-sm{max-width:24rem}.min-h-screen{min-height:100vh}.opacity-0{opacity:0}.opacity-100{opacity:1}.p-2{padding:.5rem}.p-6{padding:1.5rem}.p-10{padding:2.5rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.px-10{padding-left:2.5rem;padding-right:2.5rem}.py-16{padding-top:4rem;padding-bottom:4rem}.pt-0{padding-top:0}.pt-12{padding-top:3rem}.fixed{position:fixed}.sticky{position:sticky}.top-0{top:0}.right-0{right:0}.bottom-0{bottom:0}*{--tw-shadow:0 0 #0000}*{--tw-ring-inset:var(--tw-empty, );/*!*//*!*/--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59, 130, 246, 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000}.text-white{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.w-full{width:100%}.gap-8{gap:2rem}.gap-10{gap:2.5rem}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.transform{--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;transform:translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-90{--tw-scale-x:.9;--tw-scale-y:.9}.scale-100{--tw-scale-x:1;--tw-scale-y:1}.transition{transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:150ms}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}@-webkit-keyframes spin{to{transform:rotate(360deg)}}@keyframes spin{to{transform:rotate(360deg)}}@-webkit-keyframes ping{100%,75%{transform:scale(2);opacity:0}}@keyframes ping{100%,75%{transform:scale(2);opacity:0}}@-webkit-keyframes pulse{50%{opacity:.5}}@keyframes pulse{50%{opacity:.5}}@-webkit-keyframes bounce{0%,100%{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@keyframes bounce{0%,100%{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}#preamble>.sectionbody>.paragraph>p{padding:1rem 0}.listingblock{padding:2rem 0;overflow-x:scroll}.listingblock>.content>.highlight>code{border:2px solid #e2e0d5;background-color:transparent;padding:15px 10px}div.admonitionblock{position:relative;margin:3rem 0}div.admonitionblock>table>tbody>tr>td.icon{border-radius:18px;color:#fff;display:flex;height:30px;justify-content:flex-start;left:5px;padding:0 1.5rem;position:absolute;top:-15px;width:135px}div.admonitionblock>table>tbody>tr>td.content{padding:20px 10px 15px 1rem}div.admonitionblock.caution{border:2px dashed #ac2a34}div.admonitionblock.caution>table>tbody>tr>td.icon{background:#ac2a34}div.admonitionblock.caution>table>tbody>tr>td.content{color:#ac2a34}div.admonitionblock.note{border:2px dashed #0f406b}div.admonitionblock.note>table>tbody>tr>td.icon{background:#0f406b}div.admonitionblock.note>table>tbody>tr>td.content{color:#0f406b}div.admonitionblock.warning{border:2px dashed #e37743}div.admonitionblock.warning>table>tbody>tr>td.icon{background:#e37743}div.admonitionblock.warning>table>tbody>tr>td.content{color:#e37743}h1,h2,h3,h4,h5{padding:1rem 0}h1{font-size:1.875rem}h2{font-size:1.375rem}h3{font-size:1.5rem}@media (min-width:768px){h1{font-size:3.75rem}h2{font-size:2.625rem}h3{font-size:1.325rem}}.split-button:active{transform:translate(2px,2px)}.started-header{background-image:url(/assets/get_started_header_bg.svg);height:96px;background-position:center;background-repeat:no-repeat;background-size:cover;margin-bottom:3rem;position:relative;z-index:50}@media (min-width:768px){.started-header{margin-bottom:-3.5rem}}@media (min-width:768px){.started-header{background-image:url(/assets/get_started_header_bg.svg);height:156px}}body{--tw-bg-opacity:1;background-color:rgba(237,225,113,var(--tw-bg-opacity));--tw-border-opacity:1;border-color:rgba(217,206,95,var(--tw-border-opacity));border-style:solid;font-family:Adelle,ui-serif}.nav{width:100%}@media (min-width:640px){.nav{max-width:640px}}@media (min-width:768px){.nav{max-width:768px}}@media (min-width:1024px){.nav{max-width:1024px}}@media (min-width:1280px){.nav{max-width:1280px}}@media (min-width:1536px){.nav{max-width:1536px}}.nav{display:flex;justify-content:space-between;margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem;padding-top:2rem;position:relative;width:100%}.nav__logo{width:75%}@media (min-width:768px){.nav__logo{width:18rem}}.nav__menu-icon{display:block;padding-right:.75rem;width:2.5rem}@media (min-width:768px){.nav__menu-icon{display:none}}.nav__links{display:none;position:absolute;right:0;text-transform:uppercase;gap:1.25rem}@media (min-width:768px){.nav__links{display:flex}}.nav__link{display:flex;flex-direction:column;justify-content:center;position:relative;text-align:center;width:7rem}.nav__link-underline{position:absolute;top:1.25rem}.nav__link__underline-docs{padding-top:.5rem;position:absolute;top:1.25rem}.nav-mobile{padding-top:3rem}@media (min-width:640px){.nav-mobile{display:none}}.nav-mobile__wrapper>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.nav-mobile__wrapper{padding-top:.5rem;padding-bottom:1rem}.nav-mobile__link{border-color:transparent;display:block;font-size:1.875rem;line-height:2.25rem;padding-top:.5rem;padding-bottom:.5rem;padding-right:1rem;padding-left:1rem;--tw-text-opacity:1;color:rgba(18,18,18,var(--tw-text-opacity))}.nav-mobile__links>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.nav-mobile__links{padding-top:.5rem;padding-bottom:1rem}.nav-mobile__heading{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity));display:flex;flex-direction:column;justify-content:flex-end;height:5rem;font-size:1.5rem;line-height:2rem;padding-bottom:.75rem;padding-left:1rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:100%}.nav-mobile__heading__title{display:flex;justify-content:space-between;padding-right:1rem}.nav-mobile__heading__close{display:block;padding-right:.75rem}@media (min-width:768px){.nav-mobile__heading__close{display:none}}.nav-mobile__links__level1{padding-left:3rem}.nav-mobile__links__level2{padding-left:1rem}.hero-bg{display:none;margin-top:-21rem;width:100%}@media (min-width:768px){.hero-bg{display:block}}.hero-bg-mobile{display:block;margin-top:-16rem;width:100%}@media (min-width:768px){.hero-bg-mobile{display:none}}.hero{width:100%}@media (min-width:640px){.hero{max-width:640px}}@media (min-width:768px){.hero{max-width:768px}}@media (min-width:1024px){.hero{max-width:1024px}}@media (min-width:1280px){.hero{max-width:1280px}}@media (min-width:1536px){.hero{max-width:1536px}}.hero{margin-left:auto;margin-right:auto;margin-top:3rem;padding-left:1rem;padding-right:1rem;padding-bottom:6rem}@media (min-width:768px){.hero{margin-top:6rem}}.hero-title-wrapper{justify-content:space-between;position:relative}.hero-title{font-size:1.875rem;line-height:2.25rem}@media (min-width:768px){.hero-title{font-size:2.25rem;line-height:2.5rem;line-height:1.625}}@media (min-width:1024px){.hero-title{font-size:3rem;line-height:1}}.hero-button{display:flex;margin-bottom:3rem;padding-top:1.25rem}@media (min-width:768px){.hero-button{padding-top:2.5rem}}.hero-button__content{--tw-border-opacity:1;border-color:rgba(210,199,91,var(--tw-border-opacity));width:13rem}.hero-button__split{--tw-bg-opacity:1;background-color:rgba(18,18,18,var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.feature-button{display:none;justify-content:center;margin-bottom:3rem}@media (min-width:768px){.feature-button{display:flex;padding-top:1.5rem}}.feature-button__content{--tw-border-opacity:1;border-color:rgba(196,91,40,var(--tw-border-opacity));width:24rem}.feature-button__split{--tw-bg-opacity:1;background-color:rgba(241,226,85,var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.split-button{background-color:transparent;border-radius:.75rem;border-style:solid;border-width:2px;display:flex;height:3rem;position:relative}.split-button__button{display:flex;position:absolute;left:-.5rem;top:-.75rem}.split-button__split1{border-radius:.75rem;display:inline-flex;align-items:center;font-weight:500;height:3rem;margin-right:.5rem;padding-top:.75rem;padding-bottom:.75rem;padding-left:1.5rem;padding-right:1.5rem}.split-button__split2{border-radius:.75rem;display:inline-flex;align-items:center;font-weight:500;height:3rem;font-size:1.125rem;line-height:1.75rem;padding-top:.75rem;padding-bottom:.75rem;padding-left:.75rem;padding-right:.75rem;--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.transition{width:100%}.feature__section{width:full;margin-top:-.5rem;background-color:#e37743}.feature__wrapper{width:100%}@media (min-width:640px){.feature__wrapper{max-width:640px}}@media (min-width:768px){.feature__wrapper{max-width:768px}}@media (min-width:1024px){.feature__wrapper{max-width:1024px}}@media (min-width:1280px){.feature__wrapper{max-width:1280px}}@media (min-width:1536px){.feature__wrapper{max-width:1536px}}.feature__wrapper{margin-left:auto;margin-right:auto;padding-left:0;padding-right:0;padding-top:3rem}@media (min-width:768px){.feature__wrapper{padding-left:1rem;padding-right:1rem}}.feature__title{font-size:1.875rem;line-height:2.25rem;margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem;width:100%}@media (min-width:768px){.feature__title{font-size:2.25rem;line-height:2.5rem;padding-left:0;padding-right:0;text-align:center;width:60%}}.feature__cards{display:flex;flex-wrap:wrap;justify-content:center;padding-top:3rem}.feature__card{display:flex;justify-content:center;padding-bottom:1.5rem;width:100%}@media (min-width:768px){.feature__card{margin-bottom:3.5rem;width:50%}}@media (min-width:1024px){.feature__card{width:33.333333%}}.feature__card__content{--tw-bg-opacity:1;background-color:rgba(208,79,34,var(--tw-bg-opacity));border-radius:0;padding-top:1rem;padding-bottom:1rem;padding-left:2rem;padding-right:2rem;width:100%}@media (min-width:768px){.feature__card__content{border-radius:1.5rem;height:20rem;width:20rem}}.feature__card__heading{display:inline-flex;align-items:center;padding-bottom:1.25rem}.feature__card__title{font-size:1.875rem;line-height:2.25rem;margin-left:1rem;margin-bottom:-.5rem}.feature__card__description{font-size:1.125rem;line-height:1.75rem}.components__section{--tw-bg-opacity:1;background-color:rgba(58,52,83,var(--tw-bg-opacity));flex-direction:column;align-items:center;margin-top:-.5rem;padding-top:3rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:100%}.component{width:100%}@media (min-width:640px){.component{max-width:640px}}@media (min-width:768px){.component{max-width:768px}}@media (min-width:1024px){.component{max-width:1024px}}@media (min-width:1280px){.component{max-width:1280px}}@media (min-width:1536px){.component{max-width:1536px}}.component{display:flex;flex-wrap:wrap;align-items:center;margin-left:auto;margin-right:auto;margin-bottom:3rem;padding-left:1rem;padding-right:1rem}.component__info{padding-right:0;padding-bottom:1rem;width:100%}@media (min-width:1024px){.component__info{padding-bottom:0;padding-right:2rem;width:33.333333%}}.component__info__title{padding-bottom:.75rem}.component__example{width:100%}@media (min-width:1024px){.component__example{width:66.666667%}}.component__example__code{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity));border-radius:.75rem;height:12rem}footer{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity));margin-top:-.5rem}.footer__wrapper{width:100%}@media (min-width:640px){.footer__wrapper{max-width:640px}}@media (min-width:768px){.footer__wrapper{max-width:768px}}@media (min-width:1024px){.footer__wrapper{max-width:1024px}}@media (min-width:1280px){.footer__wrapper{max-width:1280px}}@media (min-width:1536px){.footer__wrapper{max-width:1536px}}.footer__wrapper{margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem}.footer__nav{display:flex;flex-direction:column;justify-content:space-between;line-height:2rem;padding-top:3rem;width:100%;gap:2rem}@media (min-width:768px){.footer__nav{flex-direction:row;width:40%;gap:0}}.footer__nav__category{--tw-text-opacity:1;color:rgba(91,56,233,var(--tw-text-opacity))}.footer__nav__links{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));text-transform:uppercase}.footer_copyright{font-size:.875rem;line-height:1.25rem;padding-top:3rem;padding-bottom:3rem;--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.documentation{--tw-bg-opacity:1;background-color:rgba(250,247,230,var(--tw-bg-opacity));--tw-border-opacity:1;border-color:rgba(217,206,95,var(--tw-border-opacity));border-style:solid;line-height:2.25rem}.mobile-mode{background-color:#ede171!important}.top-border{border-top-width:4px}.sidebar{display:flex}.side-nav>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.side-nav{--tw-bg-opacity:1;background-color:rgba(242,239,217,var(--tw-bg-opacity));display:none;flex-direction:column;padding-left:3.5rem;padding-top:5rem;position:relative;width:21rem;z-index:0}@media (min-width:768px){.side-nav{display:flex}}.side-nav__expand>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.side-nav__expand__button{border-radius:.75rem;cursor:pointer;display:flex;align-items:center;height:3.5rem;font-size:1.125rem;line-height:1.75rem;padding-left:1rem;padding-right:1rem}.side-nav__expand__button:hover{--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.side-nav__expand__button{width:100%}.side-nav__link:hover{--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.side-nav__expand__icon{margin-left:auto}.side-nav__expand__list{padding-left:2rem}.side-nav__expand__list2{padding-left:1.5rem}.docs{width:100%}@media (min-width:640px){.docs{max-width:640px}}@media (min-width:768px){.docs{max-width:768px}}@media (min-width:1024px){.docs{max-width:1024px}}@media (min-width:1280px){.docs{max-width:1280px}}@media (min-width:1536px){.docs{max-width:1536px}}.docs{margin-left:auto;margin-right:auto;margin-left:0;margin-bottom:6rem;padding-left:1rem;padding-right:1rem;padding-top:0}@media (min-width:768px){.docs{margin-left:2rem;padding-top:8rem}}.separator{--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-style:solid;border-top-width:2px;margin-top:2.5rem;margin-bottom:2.5rem}.code-block{--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-style:solid;border-width:2px;margin-top:2.5rem;margin-bottom:2.5rem;width:100%}.code-block__content{margin:1.5rem}.bullet-list{margin-top:.5rem;margin-bottom:.5rem;margin-left:1rem}.bullet-list__callout>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.bullet-list__callout{display:flex}code.doc{overflow-x:auto;background-color:transparent;--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-width:2px;display:block;margin-top:1rem;margin-bottom:1rem;padding:1rem}.admon{border-style:dashed;border-width:2px;margin-top:2.5rem;margin-bottom:2.5rem;width:100%}.admon-warning{--tw-border-opacity:1;border-color:rgba(220,38,38,var(--tw-border-opacity))}.admon__button{border-radius:9999px;display:flex;align-items:center;justify-content:center;height:2rem;margin-left:1rem;margin-top:-1rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:8rem;gap:.5rem}.admon__button-warning{--tw-bg-opacity:1;background-color:rgba(220,38,38,var(--tw-bg-opacity))}.admon__button-note{--tw-bg-opacity:1;background-color:rgba(30,58,138,var(--tw-bg-opacity))}.admon-note{--tw-border-opacity:1;border-color:rgba(30,58,138,var(--tw-border-opacity))}.secondary-nav{margin-bottom:1.5rem}@media (min-width:768px){.secondary-nav{display:none}}.secondary-nav__content{--tw-border-opacity:1;border-color:rgba(210,199,91,var(--tw-border-opacity));width:10rem}.secondary-nav__split{--tw-bg-opacity:1;background-color:rgba(18,18,18,var(--tw-bg-opacity));border-radius:.75rem;display:inline-flex;align-items:center;justify-content:center;font-weight:500;height:3rem;padding-left:.5rem;padding-right:.5rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:9rem}.secondary-nav__split>p{margin-right:6px}@media (min-width:768px){.md\:flex-row{flex-direction:row}.md\:ml-8{margin-left:2rem}.md\:pt-32{padding-top:8rem}.md\:w-2\/5{width:40%}.md\:gap-0{gap:0}} .nav-mobile{ diff --git a/website/docs/nav.adoc b/website/docs/nav.adoc deleted file mode 100644 index 665f1f37..00000000 --- a/website/docs/nav.adoc +++ /dev/null @@ -1,16 +0,0 @@ -* xref:concepts.adoc[Concepts] -* xref:getting-started/index.adoc[Getting Started] -** xref:getting-started/extend-an-example.adoc[Extend an Example] -** xref:getting-started/adding-to-your-project.adoc[Adding to a Project] -* xref:tutorial/index.adoc[Tutorial] -** xref:tutorial/calculator.adoc[Simple Calculator] -** xref:tutorial/immediate-execution.adoc[Immediate Execution] -** xref:tutorial/composing.adoc[Composing HTML Elements] -* xref:packages/index.adoc[Reference] -** xref:packages/core.adoc[Core] -** xref:packages/backends.adoc[Backends] -** xref:packages/console.adoc[Console] -** xref:packages/lens.adoc[Lens] -** xref:packages/html.adoc[Html] -** xref:packages/router.adoc[Router] -** xref:packages/widgets.adoc[Widgets] -- GitLab From d17d10bf13483cc8c9c6101b49c6c28178e989d8 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 15 Apr 2021 22:13:15 -0600 Subject: [PATCH 043/116] docs progress --- .../Shpadoinkle/Website/Page/Documentation.hs | 195 +++++------------- .../Website/Types/Routes/GettingStarted.hs | 30 ++- .../Website/Types/Routes/Packages.hs | 35 +++- .../Website/Types/Routes/Tutorial.hs | 31 ++- 4 files changed, 118 insertions(+), 173 deletions(-) diff --git a/website/Shpadoinkle/Website/Page/Documentation.hs b/website/Shpadoinkle/Website/Page/Documentation.hs index 3849e238..f042dc73 100644 --- a/website/Shpadoinkle/Website/Page/Documentation.hs +++ b/website/Shpadoinkle/Website/Page/Documentation.hs @@ -1,23 +1,34 @@ -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} module Shpadoinkle.Website.Page.Documentation where -import Data.Text -import Shpadoinkle.Html -import Shpadoinkle.Template.TH +import Data.Text (Text, pack) +import Prelude hiding (div) +import Shpadoinkle (MonadJSM) +import Shpadoinkle.Html as H +import Shpadoinkle.Router (navigate) +import Shpadoinkle.Template.TH (embedAsciidoc) import Shpadoinkle.Website.Style hiding (nav) +import Shpadoinkle.Website.Types.Routes (Route (..), + SPA) import qualified Shpadoinkle.Website.Types.Routes.GettingStarted as GettingStarted import qualified Shpadoinkle.Website.Types.Routes.Packages as Packages import qualified Shpadoinkle.Website.Types.Routes.Tutorial as Tutorial +import Shpadoinkle.Widgets.Types (Humanize (..)) concepts , gsIndex, gsAddingToYourProject, gsExtendAnExample , rpIndex, rpCore, rpConsole, rpBackends, rpHtml, rpLens, rpRouter, rpWidgets - , rtIndex, rtCalculator, rtImmediateExecution, rtComposing :: Html m a + , rtIndex, rtCalculator, rtImmediateExecution, rtComposing + :: MonadJSM m => Html m a concepts = docsWrap $(embedAsciidoc "./docs/concepts.adoc") gsIndex = docsWrap $(embedAsciidoc "./docs/getting-started/index.adoc") gsAddingToYourProject = docsWrap $(embedAsciidoc "./docs/getting-started/adding-to-your-project.adoc") @@ -36,14 +47,14 @@ rtImmediateExecution = docsWrap $(embedAsciidoc "./docs/tutorial/immediate-exec rtComposing = docsWrap $(embedAsciidoc "./docs/tutorial/composing.adoc") -gettingStarted :: GettingStarted.Route -> Html m a +gettingStarted :: MonadJSM m => GettingStarted.Route -> Html m a gettingStarted = \case GettingStarted.RGSIndex -> gsIndex GettingStarted.RGSAddingToYourProject -> gsAddingToYourProject GettingStarted.RGSExtendAnExample -> gsExtendAnExample -packages :: Packages.Route -> Html m a +packages :: MonadJSM m => Packages.Route -> Html m a packages = \case Packages.RPIndex -> rpIndex Packages.RPCore -> rpCore @@ -55,7 +66,7 @@ packages = \case Packages.RPWidgets -> rpWidgets -tutorial :: Tutorial.Route -> Html m a +tutorial :: MonadJSM m => Tutorial.Route -> Html m a tutorial = \case Tutorial.RTIndex -> rtIndex Tutorial.RTCalculator -> rtCalculator @@ -63,140 +74,38 @@ tutorial = \case Tutorial.RTComposing -> rtComposing - {- - - -} - -docsWrap :: [Html m a] -> Html m a +expandable + :: forall r m a + . (MonadJSM m, Bounded r, Enum r, Humanize r) + => (r -> Route) -> Text -> Html m a +expandable toR t = div [ class' side_nav__expand ] + [ div [ class' side_nav__expand__button + , onClickM_ $ navigate @(SPA m) (toR $ minBound @r) + ] + [ text t + , H.span [ class' side_nav__expand__icon ] + [ + ] + ] + + , div [ class' side_nav__expand ] + [ ul [ class' side_nav__expand__list ] $ sideNavLink <$> [ succ minBound .. maxBound @r ] + ] + ] + + +sideNavLink :: Humanize r => r -> Html m a +sideNavLink r = li_ + [ div [ class' side_nav__link ] [ text $ humanize r ] + ] + + +docsWrap :: MonadJSM m => [Html m a] -> Html m a docsWrap content' = section [class' flex ] - [ nav [ class' side_nav ] [] + [ nav [ class' side_nav ] + [ expandable RGettingStarted "Getting Started" + , expandable RTutorial "Tutorial" + , expandable RPackages "Reference" + ] , h "main" [ class' docs ] content' ] diff --git a/website/Shpadoinkle/Website/Types/Routes/GettingStarted.hs b/website/Shpadoinkle/Website/Types/Routes/GettingStarted.hs index d994e5f4..4c0c566a 100644 --- a/website/Shpadoinkle/Website/Types/Routes/GettingStarted.hs +++ b/website/Shpadoinkle/Website/Types/Routes/GettingStarted.hs @@ -1,17 +1,20 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeOperators #-} module Shpadoinkle.Website.Types.Routes.GettingStarted where -import Data.Aeson (FromJSON, ToJSON) -import GHC.Generics (Generic) -import Servant.API (type (:<|>) ((:<|>)), type (:>)) -import Shpadoinkle (NFData) -import Shpadoinkle.Router (View, type (:>>)) +import Data.Aeson (FromJSON, ToJSON) +import GHC.Generics (Generic) +import Servant.API (type (:<|>) ((:<|>)), type (:>)) +import Shpadoinkle (NFData) +import Shpadoinkle.Router (View, type (:>>)) +import Shpadoinkle.Widgets.Types (Humanize (..)) @@ -19,7 +22,7 @@ data Route = RGSIndex | RGSAddingToYourProject | RGSExtendAnExample - deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) + deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, FromJSON, ToJSON, NFData) type SPA m a @@ -33,3 +36,10 @@ routes = RGSIndex :<|> RGSAddingToYourProject :<|> RGSExtendAnExample + + +instance Humanize Route where + humanize = \case + RGSIndex -> "" + RGSAddingToYourProject -> "Adding to your project" + RGSExtendAnExample -> "Extend an example" diff --git a/website/Shpadoinkle/Website/Types/Routes/Packages.hs b/website/Shpadoinkle/Website/Types/Routes/Packages.hs index 4908403c..05044a7d 100644 --- a/website/Shpadoinkle/Website/Types/Routes/Packages.hs +++ b/website/Shpadoinkle/Website/Types/Routes/Packages.hs @@ -1,17 +1,20 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeOperators #-} module Shpadoinkle.Website.Types.Routes.Packages where -import Data.Aeson (FromJSON, ToJSON) -import GHC.Generics (Generic) -import Servant.API (type (:<|>) ((:<|>)), type (:>)) -import Shpadoinkle (NFData) -import Shpadoinkle.Router (View, type (:>>)) +import Data.Aeson (FromJSON, ToJSON) +import GHC.Generics (Generic) +import Servant.API (type (:<|>) ((:<|>)), type (:>)) +import Shpadoinkle (NFData) +import Shpadoinkle.Router (View, type (:>>)) +import Shpadoinkle.Widgets.Types (Humanize (..)) data Route @@ -23,7 +26,7 @@ data Route | RPLens | RPRouter | RPWidgets - deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) + deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, FromJSON, ToJSON, NFData) type SPA m a @@ -47,3 +50,15 @@ routes :<|> RPLens :<|> RPRouter :<|> RPWidgets + + +instance Humanize Route where + humanize = \case + RPIndex -> "" + RPCore -> "Core" + RPConsole -> "Console" + RPBackends -> "Backends" + RPHtml -> "Html" + RPLens -> "Lens" + RPRouter -> "Router" + RPWidgets -> "Widgets" diff --git a/website/Shpadoinkle/Website/Types/Routes/Tutorial.hs b/website/Shpadoinkle/Website/Types/Routes/Tutorial.hs index 5d7183a2..0696eec7 100644 --- a/website/Shpadoinkle/Website/Types/Routes/Tutorial.hs +++ b/website/Shpadoinkle/Website/Types/Routes/Tutorial.hs @@ -1,17 +1,20 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeOperators #-} module Shpadoinkle.Website.Types.Routes.Tutorial where -import Data.Aeson (FromJSON, ToJSON) -import GHC.Generics (Generic) -import Servant.API (type (:<|>) ((:<|>)), type (:>)) -import Shpadoinkle (NFData) -import Shpadoinkle.Router (View, type (:>>)) +import Data.Aeson (FromJSON, ToJSON) +import GHC.Generics (Generic) +import Servant.API (type (:<|>) ((:<|>)), type (:>)) +import Shpadoinkle (NFData) +import Shpadoinkle.Router (View, type (:>>)) +import Shpadoinkle.Widgets.Types (Humanize (..)) data Route @@ -19,7 +22,7 @@ data Route | RTCalculator | RTImmediateExecution | RTComposing - deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) + deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, FromJSON, ToJSON, NFData) type SPA m a @@ -35,3 +38,11 @@ routes :<|> RTCalculator :<|> RTImmediateExecution :<|> RTComposing + + +instance Humanize Route where + humanize = \case + RTIndex -> "" + RTCalculator -> "Calculator" + RTImmediateExecution -> "Immediate Execution" + RTComposing -> "Composing" -- GitLab From b4f2daa9561ef69837fa911a25c907700efe1e16 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 15 Apr 2021 22:31:02 -0600 Subject: [PATCH 044/116] doc links work --- website/Shpadoinkle/Website/Page/Documentation.hs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/website/Shpadoinkle/Website/Page/Documentation.hs b/website/Shpadoinkle/Website/Page/Documentation.hs index f042dc73..4fb28e74 100644 --- a/website/Shpadoinkle/Website/Page/Documentation.hs +++ b/website/Shpadoinkle/Website/Page/Documentation.hs @@ -1,6 +1,7 @@ {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} @@ -89,14 +90,14 @@ expandable toR t = div [ class' side_nav__expand ] ] , div [ class' side_nav__expand ] - [ ul [ class' side_nav__expand__list ] $ sideNavLink <$> [ succ minBound .. maxBound @r ] + [ ul [ class' side_nav__expand__list ] $ sideNavLink toR <$> [ succ minBound .. maxBound @r ] ] ] -sideNavLink :: Humanize r => r -> Html m a -sideNavLink r = li_ - [ div [ class' side_nav__link ] [ text $ humanize r ] +sideNavLink :: forall r m a. MonadJSM m => Humanize r => (r -> Route) -> r -> Html m a +sideNavLink toR r = li_ + [ div [ class' side_nav__link, onClickM_ $ navigate @(SPA m) (toR r) ] [ text $ humanize r ] ] -- GitLab From b1b2ac964398ff66244b352fcf76fb4c2c3f6be8 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 15 Apr 2021 22:37:25 -0600 Subject: [PATCH 045/116] just style --- .../Shpadoinkle/Website/Page/Documentation.hs | 62 +++++++------------ 1 file changed, 23 insertions(+), 39 deletions(-) diff --git a/website/Shpadoinkle/Website/Page/Documentation.hs b/website/Shpadoinkle/Website/Page/Documentation.hs index 4fb28e74..6119585d 100644 --- a/website/Shpadoinkle/Website/Page/Documentation.hs +++ b/website/Shpadoinkle/Website/Page/Documentation.hs @@ -25,54 +25,38 @@ import qualified Shpadoinkle.Website.Types.Routes.Tutorial as Tutorial import Shpadoinkle.Widgets.Types (Humanize (..)) -concepts - , gsIndex, gsAddingToYourProject, gsExtendAnExample - , rpIndex, rpCore, rpConsole, rpBackends, rpHtml, rpLens, rpRouter, rpWidgets - , rtIndex, rtCalculator, rtImmediateExecution, rtComposing - :: MonadJSM m => Html m a -concepts = docsWrap $(embedAsciidoc "./docs/concepts.adoc") -gsIndex = docsWrap $(embedAsciidoc "./docs/getting-started/index.adoc") -gsAddingToYourProject = docsWrap $(embedAsciidoc "./docs/getting-started/adding-to-your-project.adoc") -gsExtendAnExample = docsWrap $(embedAsciidoc "./docs/getting-started/extend-an-example.adoc") -rpIndex = docsWrap $(embedAsciidoc "./docs/packages/index.adoc") -rpCore = docsWrap $(embedAsciidoc "./docs/packages/core.adoc") -rpConsole = docsWrap $(embedAsciidoc "./docs/packages/console.adoc") -rpBackends = docsWrap $(embedAsciidoc "./docs/packages/backends.adoc") -rpHtml = docsWrap $(embedAsciidoc "./docs/packages/html.adoc") -rpLens = docsWrap $(embedAsciidoc "./docs/packages/lens.adoc") -rpRouter = docsWrap $(embedAsciidoc "./docs/packages/router.adoc") -rpWidgets = docsWrap $(embedAsciidoc "./docs/packages/widgets.adoc") -rtIndex = docsWrap $(embedAsciidoc "./docs/tutorial/index.adoc") -rtCalculator = docsWrap $(embedAsciidoc "./docs/tutorial/calculator.adoc") -rtImmediateExecution = docsWrap $(embedAsciidoc "./docs/tutorial/immediate-execution.adoc") -rtComposing = docsWrap $(embedAsciidoc "./docs/tutorial/composing.adoc") +concepts :: MonadJSM m => Html m a +concepts = docsWrap $(embedAsciidoc "./docs/concepts.adoc") gettingStarted :: MonadJSM m => GettingStarted.Route -> Html m a -gettingStarted = \case - GettingStarted.RGSIndex -> gsIndex - GettingStarted.RGSAddingToYourProject -> gsAddingToYourProject - GettingStarted.RGSExtendAnExample -> gsExtendAnExample +gettingStarted = docsWrap . \case + GettingStarted.RGSIndex -> + $(embedAsciidoc "./docs/getting-started/index.adoc") + GettingStarted.RGSAddingToYourProject -> + $(embedAsciidoc "./docs/getting-started/adding-to-your-project.adoc") + GettingStarted.RGSExtendAnExample -> + $(embedAsciidoc "./docs/getting-started/extend-an-example.adoc") packages :: MonadJSM m => Packages.Route -> Html m a -packages = \case - Packages.RPIndex -> rpIndex - Packages.RPCore -> rpCore - Packages.RPConsole -> rpConsole - Packages.RPBackends -> rpBackends - Packages.RPHtml -> rpHtml - Packages.RPLens -> rpLens - Packages.RPRouter -> rpRouter - Packages.RPWidgets -> rpWidgets +packages = docsWrap . \case + Packages.RPIndex -> $(embedAsciidoc "./docs/packages/index.adoc") + Packages.RPCore -> $(embedAsciidoc "./docs/packages/core.adoc") + Packages.RPConsole -> $(embedAsciidoc "./docs/packages/console.adoc") + Packages.RPBackends -> $(embedAsciidoc "./docs/packages/backends.adoc") + Packages.RPHtml -> $(embedAsciidoc "./docs/packages/html.adoc") + Packages.RPLens -> $(embedAsciidoc "./docs/packages/lens.adoc") + Packages.RPRouter -> $(embedAsciidoc "./docs/packages/router.adoc") + Packages.RPWidgets -> $(embedAsciidoc "./docs/packages/widgets.adoc") tutorial :: MonadJSM m => Tutorial.Route -> Html m a -tutorial = \case - Tutorial.RTIndex -> rtIndex - Tutorial.RTCalculator -> rtCalculator - Tutorial.RTImmediateExecution -> rtImmediateExecution - Tutorial.RTComposing -> rtComposing +tutorial = docsWrap . \case + Tutorial.RTIndex -> $(embedAsciidoc "./docs/tutorial/index.adoc") + Tutorial.RTCalculator -> $(embedAsciidoc "./docs/tutorial/calculator.adoc") + Tutorial.RTImmediateExecution -> $(embedAsciidoc "./docs/tutorial/immediate-execution.adoc") + Tutorial.RTComposing -> $(embedAsciidoc "./docs/tutorial/composing.adoc") expandable -- GitLab From 55183ac5b154c463dcf0935c4a48b13a4e4d415b Mon Sep 17 00:00:00 2001 From: Jose Soto Date: Mon, 19 Apr 2021 01:24:05 -0700 Subject: [PATCH 046/116] CSS Fixes #102 #104 #105 --- .gitignore | 1 + website/assets/style.css | 1864 +++++++++++++++++++++++++++++++++++++- 2 files changed, 1856 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index d903a6dc..500af88a 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ roster.db .vscode *.tar.gz */docs/**/*.html +tags diff --git a/website/assets/style.css b/website/assets/style.css index 93387d84..02805edd 100644 --- a/website/assets/style.css +++ b/website/assets/style.css @@ -1,14 +1,1860 @@ -@font-face{font-family:Adelle;src:url(/assets/Adelle-Regular.woff2) format('woff2');font-weight:400;font-style:normal}@font-face{font-family:AdelleBold;src:url(/assets/Adelle-Bold.woff2) format('woff2');font-weight:400;font-style:normal}/*! modern-normalize v1.0.0 | MIT License | https://github.com/sindresorhus/modern-normalize */*,::after,::before{box-sizing:border-box}:root{-moz-tab-size:4;-o-tab-size:4;tab-size:4}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}body{font-family:system-ui,-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji'}hr{height:0;color:inherit}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Consolas,'Liberation Mono',Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],button{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}button{background-color:transparent;background-image:none}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}fieldset{margin:0;padding:0}ol,ul{list-style:none;margin:0;padding:0}html{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";line-height:1.5}body{font-family:inherit;line-height:inherit}*,::after,::before{box-sizing:border-box;border-width:0;border-style:solid;border-color:currentColor}hr{border-top-width:1px}img{border-style:solid}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#a1a1aa}input:-ms-input-placeholder,textarea:-ms-input-placeholder{opacity:1;color:#a1a1aa}input::placeholder,textarea::placeholder{opacity:1;color:#a1a1aa}button{cursor:pointer}table{border-collapse:collapse}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,optgroup,select,textarea{padding:0;line-height:inherit;color:inherit}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.rounded{border-radius:.25rem}.flex{display:flex}.table{display:table}.table-row{display:table-row}.grid{display:grid}.hidden{display:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.flex-1{flex:1 1 0%}.font-bold{font-family:AdelleBold,Verdana}.font-bold{font-weight:700}.h-32{height:8rem}.text-xs{font-size:.75rem;line-height:1rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.leading-8{line-height:2rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-0{margin-left:0}.mr-4{margin-right:1rem}.mb-4{margin-bottom:1rem}.mb-24{margin-bottom:6rem}.max-w-sm{max-width:24rem}.min-h-screen{min-height:100vh}.opacity-0{opacity:0}.opacity-100{opacity:1}.p-2{padding:.5rem}.p-6{padding:1.5rem}.p-10{padding:2.5rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.px-10{padding-left:2.5rem;padding-right:2.5rem}.py-16{padding-top:4rem;padding-bottom:4rem}.pt-0{padding-top:0}.pt-12{padding-top:3rem}.fixed{position:fixed}.sticky{position:sticky}.top-0{top:0}.right-0{right:0}.bottom-0{bottom:0}*{--tw-shadow:0 0 #0000}*{--tw-ring-inset:var(--tw-empty, );/*!*//*!*/--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59, 130, 246, 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000}.text-white{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.w-full{width:100%}.gap-8{gap:2rem}.gap-10{gap:2.5rem}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.transform{--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;transform:translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-90{--tw-scale-x:.9;--tw-scale-y:.9}.scale-100{--tw-scale-x:1;--tw-scale-y:1}.transition{transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:150ms}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}@-webkit-keyframes spin{to{transform:rotate(360deg)}}@keyframes spin{to{transform:rotate(360deg)}}@-webkit-keyframes ping{100%,75%{transform:scale(2);opacity:0}}@keyframes ping{100%,75%{transform:scale(2);opacity:0}}@-webkit-keyframes pulse{50%{opacity:.5}}@keyframes pulse{50%{opacity:.5}}@-webkit-keyframes bounce{0%,100%{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@keyframes bounce{0%,100%{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}#preamble>.sectionbody>.paragraph>p{padding:1rem 0}.listingblock{padding:2rem 0;overflow-x:scroll}.listingblock>.content>.highlight>code{border:2px solid #e2e0d5;background-color:transparent;padding:15px 10px}div.admonitionblock{position:relative;margin:3rem 0}div.admonitionblock>table>tbody>tr>td.icon{border-radius:18px;color:#fff;display:flex;height:30px;justify-content:flex-start;left:5px;padding:0 1.5rem;position:absolute;top:-15px;width:135px}div.admonitionblock>table>tbody>tr>td.content{padding:20px 10px 15px 1rem}div.admonitionblock.caution{border:2px dashed #ac2a34}div.admonitionblock.caution>table>tbody>tr>td.icon{background:#ac2a34}div.admonitionblock.caution>table>tbody>tr>td.content{color:#ac2a34}div.admonitionblock.note{border:2px dashed #0f406b}div.admonitionblock.note>table>tbody>tr>td.icon{background:#0f406b}div.admonitionblock.note>table>tbody>tr>td.content{color:#0f406b}div.admonitionblock.warning{border:2px dashed #e37743}div.admonitionblock.warning>table>tbody>tr>td.icon{background:#e37743}div.admonitionblock.warning>table>tbody>tr>td.content{color:#e37743}h1,h2,h3,h4,h5{padding:1rem 0}h1{font-size:1.875rem}h2{font-size:1.375rem}h3{font-size:1.5rem}@media (min-width:768px){h1{font-size:3.75rem}h2{font-size:2.625rem}h3{font-size:1.325rem}}.split-button:active{transform:translate(2px,2px)}.started-header{background-image:url(/assets/get_started_header_bg.svg);height:96px;background-position:center;background-repeat:no-repeat;background-size:cover;margin-bottom:3rem;position:relative;z-index:50}@media (min-width:768px){.started-header{margin-bottom:-3.5rem}}@media (min-width:768px){.started-header{background-image:url(/assets/get_started_header_bg.svg);height:156px}}body{--tw-bg-opacity:1;background-color:rgba(237,225,113,var(--tw-bg-opacity));--tw-border-opacity:1;border-color:rgba(217,206,95,var(--tw-border-opacity));border-style:solid;font-family:Adelle,ui-serif}.nav{width:100%}@media (min-width:640px){.nav{max-width:640px}}@media (min-width:768px){.nav{max-width:768px}}@media (min-width:1024px){.nav{max-width:1024px}}@media (min-width:1280px){.nav{max-width:1280px}}@media (min-width:1536px){.nav{max-width:1536px}}.nav{display:flex;justify-content:space-between;margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem;padding-top:2rem;position:relative;width:100%}.nav__logo{width:75%}@media (min-width:768px){.nav__logo{width:18rem}}.nav__menu-icon{display:block;padding-right:.75rem;width:2.5rem}@media (min-width:768px){.nav__menu-icon{display:none}}.nav__links{display:none;position:absolute;right:0;text-transform:uppercase;gap:1.25rem}@media (min-width:768px){.nav__links{display:flex}}.nav__link{display:flex;flex-direction:column;justify-content:center;position:relative;text-align:center;width:7rem}.nav__link-underline{position:absolute;top:1.25rem}.nav__link__underline-docs{padding-top:.5rem;position:absolute;top:1.25rem}.nav-mobile{padding-top:3rem}@media (min-width:640px){.nav-mobile{display:none}}.nav-mobile__wrapper>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.nav-mobile__wrapper{padding-top:.5rem;padding-bottom:1rem}.nav-mobile__link{border-color:transparent;display:block;font-size:1.875rem;line-height:2.25rem;padding-top:.5rem;padding-bottom:.5rem;padding-right:1rem;padding-left:1rem;--tw-text-opacity:1;color:rgba(18,18,18,var(--tw-text-opacity))}.nav-mobile__links>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.nav-mobile__links{padding-top:.5rem;padding-bottom:1rem}.nav-mobile__heading{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity));display:flex;flex-direction:column;justify-content:flex-end;height:5rem;font-size:1.5rem;line-height:2rem;padding-bottom:.75rem;padding-left:1rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:100%}.nav-mobile__heading__title{display:flex;justify-content:space-between;padding-right:1rem}.nav-mobile__heading__close{display:block;padding-right:.75rem}@media (min-width:768px){.nav-mobile__heading__close{display:none}}.nav-mobile__links__level1{padding-left:3rem}.nav-mobile__links__level2{padding-left:1rem}.hero-bg{display:none;margin-top:-21rem;width:100%}@media (min-width:768px){.hero-bg{display:block}}.hero-bg-mobile{display:block;margin-top:-16rem;width:100%}@media (min-width:768px){.hero-bg-mobile{display:none}}.hero{width:100%}@media (min-width:640px){.hero{max-width:640px}}@media (min-width:768px){.hero{max-width:768px}}@media (min-width:1024px){.hero{max-width:1024px}}@media (min-width:1280px){.hero{max-width:1280px}}@media (min-width:1536px){.hero{max-width:1536px}}.hero{margin-left:auto;margin-right:auto;margin-top:3rem;padding-left:1rem;padding-right:1rem;padding-bottom:6rem}@media (min-width:768px){.hero{margin-top:6rem}}.hero-title-wrapper{justify-content:space-between;position:relative}.hero-title{font-size:1.875rem;line-height:2.25rem}@media (min-width:768px){.hero-title{font-size:2.25rem;line-height:2.5rem;line-height:1.625}}@media (min-width:1024px){.hero-title{font-size:3rem;line-height:1}}.hero-button{display:flex;margin-bottom:3rem;padding-top:1.25rem}@media (min-width:768px){.hero-button{padding-top:2.5rem}}.hero-button__content{--tw-border-opacity:1;border-color:rgba(210,199,91,var(--tw-border-opacity));width:13rem}.hero-button__split{--tw-bg-opacity:1;background-color:rgba(18,18,18,var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.feature-button{display:none;justify-content:center;margin-bottom:3rem}@media (min-width:768px){.feature-button{display:flex;padding-top:1.5rem}}.feature-button__content{--tw-border-opacity:1;border-color:rgba(196,91,40,var(--tw-border-opacity));width:24rem}.feature-button__split{--tw-bg-opacity:1;background-color:rgba(241,226,85,var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.split-button{background-color:transparent;border-radius:.75rem;border-style:solid;border-width:2px;display:flex;height:3rem;position:relative}.split-button__button{display:flex;position:absolute;left:-.5rem;top:-.75rem}.split-button__split1{border-radius:.75rem;display:inline-flex;align-items:center;font-weight:500;height:3rem;margin-right:.5rem;padding-top:.75rem;padding-bottom:.75rem;padding-left:1.5rem;padding-right:1.5rem}.split-button__split2{border-radius:.75rem;display:inline-flex;align-items:center;font-weight:500;height:3rem;font-size:1.125rem;line-height:1.75rem;padding-top:.75rem;padding-bottom:.75rem;padding-left:.75rem;padding-right:.75rem;--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.transition{width:100%}.feature__section{width:full;margin-top:-.5rem;background-color:#e37743}.feature__wrapper{width:100%}@media (min-width:640px){.feature__wrapper{max-width:640px}}@media (min-width:768px){.feature__wrapper{max-width:768px}}@media (min-width:1024px){.feature__wrapper{max-width:1024px}}@media (min-width:1280px){.feature__wrapper{max-width:1280px}}@media (min-width:1536px){.feature__wrapper{max-width:1536px}}.feature__wrapper{margin-left:auto;margin-right:auto;padding-left:0;padding-right:0;padding-top:3rem}@media (min-width:768px){.feature__wrapper{padding-left:1rem;padding-right:1rem}}.feature__title{font-size:1.875rem;line-height:2.25rem;margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem;width:100%}@media (min-width:768px){.feature__title{font-size:2.25rem;line-height:2.5rem;padding-left:0;padding-right:0;text-align:center;width:60%}}.feature__cards{display:flex;flex-wrap:wrap;justify-content:center;padding-top:3rem}.feature__card{display:flex;justify-content:center;padding-bottom:1.5rem;width:100%}@media (min-width:768px){.feature__card{margin-bottom:3.5rem;width:50%}}@media (min-width:1024px){.feature__card{width:33.333333%}}.feature__card__content{--tw-bg-opacity:1;background-color:rgba(208,79,34,var(--tw-bg-opacity));border-radius:0;padding-top:1rem;padding-bottom:1rem;padding-left:2rem;padding-right:2rem;width:100%}@media (min-width:768px){.feature__card__content{border-radius:1.5rem;height:20rem;width:20rem}}.feature__card__heading{display:inline-flex;align-items:center;padding-bottom:1.25rem}.feature__card__title{font-size:1.875rem;line-height:2.25rem;margin-left:1rem;margin-bottom:-.5rem}.feature__card__description{font-size:1.125rem;line-height:1.75rem}.components__section{--tw-bg-opacity:1;background-color:rgba(58,52,83,var(--tw-bg-opacity));flex-direction:column;align-items:center;margin-top:-.5rem;padding-top:3rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:100%}.component{width:100%}@media (min-width:640px){.component{max-width:640px}}@media (min-width:768px){.component{max-width:768px}}@media (min-width:1024px){.component{max-width:1024px}}@media (min-width:1280px){.component{max-width:1280px}}@media (min-width:1536px){.component{max-width:1536px}}.component{display:flex;flex-wrap:wrap;align-items:center;margin-left:auto;margin-right:auto;margin-bottom:3rem;padding-left:1rem;padding-right:1rem}.component__info{padding-right:0;padding-bottom:1rem;width:100%}@media (min-width:1024px){.component__info{padding-bottom:0;padding-right:2rem;width:33.333333%}}.component__info__title{padding-bottom:.75rem}.component__example{width:100%}@media (min-width:1024px){.component__example{width:66.666667%}}.component__example__code{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity));border-radius:.75rem;height:12rem}footer{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity));margin-top:-.5rem}.footer__wrapper{width:100%}@media (min-width:640px){.footer__wrapper{max-width:640px}}@media (min-width:768px){.footer__wrapper{max-width:768px}}@media (min-width:1024px){.footer__wrapper{max-width:1024px}}@media (min-width:1280px){.footer__wrapper{max-width:1280px}}@media (min-width:1536px){.footer__wrapper{max-width:1536px}}.footer__wrapper{margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem}.footer__nav{display:flex;flex-direction:column;justify-content:space-between;line-height:2rem;padding-top:3rem;width:100%;gap:2rem}@media (min-width:768px){.footer__nav{flex-direction:row;width:40%;gap:0}}.footer__nav__category{--tw-text-opacity:1;color:rgba(91,56,233,var(--tw-text-opacity))}.footer__nav__links{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));text-transform:uppercase}.footer_copyright{font-size:.875rem;line-height:1.25rem;padding-top:3rem;padding-bottom:3rem;--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.documentation{--tw-bg-opacity:1;background-color:rgba(250,247,230,var(--tw-bg-opacity));--tw-border-opacity:1;border-color:rgba(217,206,95,var(--tw-border-opacity));border-style:solid;line-height:2.25rem}.mobile-mode{background-color:#ede171!important}.top-border{border-top-width:4px}.sidebar{display:flex}.side-nav>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.side-nav{--tw-bg-opacity:1;background-color:rgba(242,239,217,var(--tw-bg-opacity));display:none;flex-direction:column;padding-left:3.5rem;padding-top:5rem;position:relative;width:21rem;z-index:0}@media (min-width:768px){.side-nav{display:flex}}.side-nav__expand>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.side-nav__expand__button{border-radius:.75rem;cursor:pointer;display:flex;align-items:center;height:3.5rem;font-size:1.125rem;line-height:1.75rem;padding-left:1rem;padding-right:1rem}.side-nav__expand__button:hover{--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.side-nav__expand__button{width:100%}.side-nav__link:hover{--tw-text-opacity:1;color:rgba(76,76,76,var(--tw-text-opacity))}.side-nav__expand__icon{margin-left:auto}.side-nav__expand__list{padding-left:2rem}.side-nav__expand__list2{padding-left:1.5rem}.docs{width:100%}@media (min-width:640px){.docs{max-width:640px}}@media (min-width:768px){.docs{max-width:768px}}@media (min-width:1024px){.docs{max-width:1024px}}@media (min-width:1280px){.docs{max-width:1280px}}@media (min-width:1536px){.docs{max-width:1536px}}.docs{margin-left:auto;margin-right:auto;margin-left:0;margin-bottom:6rem;padding-left:1rem;padding-right:1rem;padding-top:0}@media (min-width:768px){.docs{margin-left:2rem;padding-top:8rem}}.separator{--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-style:solid;border-top-width:2px;margin-top:2.5rem;margin-bottom:2.5rem}.code-block{--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-style:solid;border-width:2px;margin-top:2.5rem;margin-bottom:2.5rem;width:100%}.code-block__content{margin:1.5rem}.bullet-list{margin-top:.5rem;margin-bottom:.5rem;margin-left:1rem}.bullet-list__callout>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.bullet-list__callout{display:flex}code.doc{overflow-x:auto;background-color:transparent;--tw-border-opacity:1;border-color:rgba(226,224,213,var(--tw-border-opacity));border-width:2px;display:block;margin-top:1rem;margin-bottom:1rem;padding:1rem}.admon{border-style:dashed;border-width:2px;margin-top:2.5rem;margin-bottom:2.5rem;width:100%}.admon-warning{--tw-border-opacity:1;border-color:rgba(220,38,38,var(--tw-border-opacity))}.admon__button{border-radius:9999px;display:flex;align-items:center;justify-content:center;height:2rem;margin-left:1rem;margin-top:-1rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:8rem;gap:.5rem}.admon__button-warning{--tw-bg-opacity:1;background-color:rgba(220,38,38,var(--tw-bg-opacity))}.admon__button-note{--tw-bg-opacity:1;background-color:rgba(30,58,138,var(--tw-bg-opacity))}.admon-note{--tw-border-opacity:1;border-color:rgba(30,58,138,var(--tw-border-opacity))}.secondary-nav{margin-bottom:1.5rem}@media (min-width:768px){.secondary-nav{display:none}}.secondary-nav__content{--tw-border-opacity:1;border-color:rgba(210,199,91,var(--tw-border-opacity));width:10rem}.secondary-nav__split{--tw-bg-opacity:1;background-color:rgba(18,18,18,var(--tw-bg-opacity));border-radius:.75rem;display:inline-flex;align-items:center;justify-content:center;font-weight:500;height:3rem;padding-left:.5rem;padding-right:.5rem;--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));width:9rem}.secondary-nav__split>p{margin-right:6px}@media (min-width:768px){.md\:flex-row{flex-direction:row}.md\:ml-8{margin-left:2rem}.md\:pt-32{padding-top:8rem}.md\:w-2\/5{width:40%}.md\:gap-0{gap:0}} +@font-face { + font-family: Adelle; + src: url(/assets/Adelle-Regular.woff2) format("woff2"); + font-weight: 400; + font-style: normal; +} +@font-face { + font-family: AdelleBold; + src: url(/assets/Adelle-Bold.woff2) format("woff2"); + font-weight: 400; + font-style: normal; +} /*! modern-normalize v1.0.0 | MIT License | https://github.com/sindresorhus/modern-normalize */ +*, +::after, +::before { + box-sizing: border-box; +} +:root { + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; +} +html { + line-height: 1.15; + -webkit-text-size-adjust: 100%; +} +body { + margin: 0; +} +body { + font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, + sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; +} +hr { + height: 0; + color: inherit; +} +abbr[title] { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} +b, +strong { + font-weight: bolder; +} +code, +kbd, +pre, +samp { + font-family: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", Menlo, + monospace; + font-size: 1em; +} +small { + font-size: 80%; +} +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} +sub { + bottom: -0.25em; +} +sup { + top: -0.5em; +} +table { + text-indent: 0; + border-color: inherit; +} +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + font-size: 100%; + line-height: 1.15; + margin: 0; +} +button, +select { + text-transform: none; +} +[type="button"], +button { + -webkit-appearance: button; +} +legend { + padding: 0; +} +progress { + vertical-align: baseline; +} +summary { + display: list-item; +} +blockquote, +dd, +dl, +figure, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +p, +pre { + margin: 0; +} +button { + background-color: transparent; + background-image: none; +} +button:focus { + outline: 1px dotted; + outline: 5px auto -webkit-focus-ring-color; +} +fieldset { + margin: 0; + padding: 0; +} +ol, +ul { + list-style: none; + margin: 0; + padding: 0; +} +html { + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, + "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, + "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + line-height: 1.5; +} +body { + font-family: inherit; + line-height: inherit; +} +*, +::after, +::before { + box-sizing: border-box; + border-width: 0; + border-style: solid; + border-color: currentColor; +} +hr { + border-top-width: 1px; +} +img { + border-style: solid; +} +textarea { + resize: vertical; +} +input::-moz-placeholder, +textarea::-moz-placeholder { + opacity: 1; + color: #a1a1aa; +} +input:-ms-input-placeholder, +textarea:-ms-input-placeholder { + opacity: 1; + color: #a1a1aa; +} +input::placeholder, +textarea::placeholder { + opacity: 1; + color: #a1a1aa; +} +button { + cursor: pointer; +} +table { + border-collapse: collapse; +} +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} +a { + color: inherit; + text-decoration: inherit; +} +button, +input, +optgroup, +select, +textarea { + padding: 0; + line-height: inherit; + color: inherit; +} +code, +kbd, +pre, +samp { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, + "Liberation Mono", "Courier New", monospace; +} +audio, +canvas, +embed, +iframe, +img, +object, +svg, +video { + display: block; + vertical-align: middle; +} +img, +video { + max-width: 100%; + height: auto; +} +.container { + width: 100%; +} +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } +} +.rounded { + border-radius: 0.25rem; +} +.flex { + display: flex; +} +.table { + display: table; +} +.table-row { + display: table-row; +} +.grid { + display: grid; +} +.hidden { + display: none; +} +.flex-col { + flex-direction: column; +} +.flex-wrap { + flex-wrap: wrap; +} +.items-center { + align-items: center; +} +.justify-center { + justify-content: center; +} +.justify-between { + justify-content: space-between; +} +.flex-1 { + flex: 1 1 0%; +} +.font-bold { + font-family: AdelleBold, Verdana; +} +.font-bold { + font-weight: 700; +} +.h-32 { + height: 8rem; +} +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} +.leading-8 { + line-height: 2rem; +} +.mx-auto { + margin-left: auto; + margin-right: auto; +} +.ml-0 { + margin-left: 0; +} +.mr-4 { + margin-right: 1rem; +} +.mb-4 { + margin-bottom: 1rem; +} +.mb-24 { + margin-bottom: 6rem; +} +.max-w-sm { + max-width: 24rem; +} +.min-h-screen { + min-height: 100vh; +} +.opacity-0 { + opacity: 0; +} +.opacity-100 { + opacity: 1; +} +.p-2 { + padding: 0.5rem; +} +.p-6 { + padding: 1.5rem; +} +.p-10 { + padding: 2.5rem; +} +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} +.px-8 { + padding-left: 2rem; + padding-right: 2rem; +} +.px-10 { + padding-left: 2.5rem; + padding-right: 2.5rem; +} +.py-16 { + padding-top: 4rem; + padding-bottom: 4rem; +} +.pt-0 { + padding-top: 0; +} +.pt-12 { + padding-top: 3rem; +} +.fixed { + position: fixed; +} +.sticky { + position: sticky; +} +.top-0 { + top: 0; +} +.right-0 { + right: 0; +} +.bottom-0 { + bottom: 0; +} +* { + * --tw-shadow: 0 0 #0000; + * } + * * { + * --tw-ring-inset: var(--tw-empty); /*!*/ /*!*/ + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgba(59, 130, 246, 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + } + .text-white { + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); + } + .w-full { + width: 100%; + } + .gap-8 { + gap: 2rem; + } + .gap-10 { + gap: 2.5rem; + } + .grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + .transform { + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + transform: translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) + rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) + scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + } + .scale-90 { + --tw-scale-x: 0.9; + --tw-scale-y: 0.9; + } + .scale-100 { + --tw-scale-x: 1; + --tw-scale-y: 1; + } + .transition { + transition-property: background-color, border-color, color, fill, stroke, + opacity, box-shadow, transform; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + } + .ease-in { + transition-timing-function: cubic-bezier(0.4, 0, 1, 1); + } + .ease-out { + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + } + @-webkit-keyframes spin { + to { + transform: rotate(360deg); + } + } + @keyframes spin { + to { + transform: rotate(360deg); + } + } + @-webkit-keyframes ping { + 100%, + 75% { + transform: scale(2); + opacity: 0; + } + } + @keyframes ping { + 100%, + 75% { + transform: scale(2); + opacity: 0; + } + } + @-webkit-keyframes pulse { + 50% { + opacity: 0.5; + } + } + @keyframes pulse { + 50% { + opacity: 0.5; + } + } + @-webkit-keyframes bounce { + 0%, + 100% { + transform: translateY(-25%); + -webkit-animation-timing-function: cubic-bezier(0.8, 0, 1, 1); + animation-timing-function: cubic-bezier(0.8, 0, 1, 1); + } + 50% { + transform: none; + -webkit-animation-timing-function: cubic-bezier(0, 0, 0.2, 1); + animation-timing-function: cubic-bezier(0, 0, 0.2, 1); + } + } + @keyframes bounce { + 0%, + 100% { + transform: translateY(-25%); + -webkit-animation-timing-function: cubic-bezier(0.8, 0, 1, 1); + animation-timing-function: cubic-bezier(0.8, 0, 1, 1); + } + 50% { + transform: none; + -webkit-animation-timing-function: cubic-bezier(0, 0, 0.2, 1); + animation-timing-function: cubic-bezier(0, 0, 0.2, 1); + } + } + #preamble > .sectionbody > .paragraph > p { + padding: 1rem 0; + } + .listingblock { + padding: 2rem 0; + overflow-x: scroll; + } + .listingblock > .content > .highlight > code { + border: 2px solid #e2e0d5; + background-color: transparent; + padding: 15px 10px; + } + div.admonitionblock { + position: relative; + margin: 3rem 0; + } + div.admonitionblock > table > tbody > tr > td.icon { + border-radius: 18px; + color: #fff; + display: flex; + height: 30px; + justify-content: flex-start; + left: 5px; + padding: 0 1.5rem; + position: absolute; + top: -15px; + width: 135px; + } + div.admonitionblock > table > tbody > tr > td.content { + padding: 20px 10px 15px 1rem; + } + div.admonitionblock.caution { + border: 2px dashed #ac2a34; + } + div.admonitionblock.caution > table > tbody > tr > td.icon { + background: #ac2a34; + } + div.admonitionblock.caution > table > tbody > tr > td.content { + color: #ac2a34; + } + div.admonitionblock.note { + border: 2px dashed #0f406b; + } + div.admonitionblock.note > table > tbody > tr > td.icon { + background: #0f406b; + } + div.admonitionblock.note > table > tbody > tr > td.content { + color: #0f406b; + } + div.admonitionblock.warning { + border: 2px dashed #e37743; + } + div.admonitionblock.warning > table > tbody > tr > td.icon { + background: #e37743; + } + div.admonitionblock.warning > table > tbody > tr > td.content { + color: #e37743; + } + h1, + h2, + h3, + h4, + h5 { + padding: 1rem 0; + } + h1 { + font-size: 1.875rem; + } + h2 { + font-size: 1.375rem; + } + h3 { + font-size: 1.5rem; + } + @media (min-width: 768px) { + h1 { + font-size: 3.75rem; + } + h2 { + font-size: 2.625rem; + } + h3 { + font-size: 1.325rem; + } + } + .split-button:active { + transform: translate(2px, 2px); + } + .started-header { + background-image: url(/assets/get_started_header_bg.svg); + height: 96px; + background-position: center; + background-repeat: no-repeat; + background-size: cover; + margin-bottom: 3rem; + position: relative; + z-index: 50; + } + @media (min-width: 768px) { + .started-header { + margin-bottom: -3.5rem; + } + } + @media (min-width: 768px) { + .started-header { + background-image: url(/assets/get_started_header_bg.svg); + height: 156px; + } + } + body { + --tw-bg-opacity: 1; + background-color: rgba(237, 225, 113, var(--tw-bg-opacity)); + --tw-border-opacity: 1; + border-color: rgba(217, 206, 95, var(--tw-border-opacity)); + border-style: solid; + font-family: Adelle, ui-serif; + } + .nav { + width: 100%; + } + @media (min-width: 640px) { + .nav { + max-width: 640px; + } + } + @media (min-width: 768px) { + .nav { + max-width: 768px; + } + } + @media (min-width: 1024px) { + .nav { + max-width: 1024px; + } + } + @media (min-width: 1280px) { + .nav { + max-width: 1280px; + } + } + @media (min-width: 1536px) { + .nav { + max-width: 1536px; + } + } + .nav { + display: flex; + justify-content: space-between; + margin-left: auto; + margin-right: auto; + padding-left: 1rem; + padding-right: 1rem; + padding-top: 2rem; + position: relative; + width: 100%; + } + .nav__logo { + width: 75%; + } + @media (min-width: 768px) { + .nav__logo { + width: 70%; + } + } + .nav__menu-icon { + display: block; + padding-right: 0.75rem; + width: 2.5rem; + } + @media (min-width: 768px) { + .nav__menu-icon { + display: none; + } + } + .nav__links { + display: none; + position: absolute; + right: 0; + text-transform: uppercase; + gap: 1.25rem; + } + @media (min-width: 768px) { + .nav__links { + display: flex; + } + } + .nav__link { + display: flex; + flex-direction: column; + justify-content: center; + position: relative; + text-align: center; + width: 7rem; + } + .nav__link-underline { + position: absolute; + top: 1.25rem; + } + .nav__link__underline-docs { + padding-top: 0.5rem; + position: absolute; + top: 1.25rem; + } + .nav-mobile { + padding-top: 3rem; + } + @media (min-width: 640px) { + .nav-mobile { + display: none; + } + } + .nav-mobile__wrapper > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); + } + .nav-mobile__wrapper { + padding-top: 0.5rem; + padding-bottom: 1rem; + } + .nav-mobile__link { + border-color: transparent; + display: block; + font-size: 1.875rem; + line-height: 2.25rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-right: 1rem; + padding-left: 1rem; + --tw-text-opacity: 1; + color: rgba(18, 18, 18, var(--tw-text-opacity)); + } + .nav-mobile__links > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); + } + .nav-mobile__links { + padding-top: 0.5rem; + padding-bottom: 1rem; + } + .nav-mobile__heading { + --tw-bg-opacity: 1; + background-color: rgba(0, 0, 0, var(--tw-bg-opacity)); + display: flex; + flex-direction: column; + justify-content: flex-end; + height: 5rem; + font-size: 1.5rem; + line-height: 2rem; + padding-bottom: 0.75rem; + padding-left: 1rem; + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); + width: 100%; + } + .nav-mobile__heading__title { + display: flex; + justify-content: space-between; + padding-right: 1rem; + } + .nav-mobile__heading__close { + display: block; + padding-right: 0.75rem; + } + @media (min-width: 768px) { + .nav-mobile__heading__close { + display: none; + } + } + .nav-mobile__links__level1 { + padding-left: 3rem; + } + .nav-mobile__links__level2 { + padding-left: 1rem; + } + .hero-bg { + display: none; + margin-top: -28rem; + width: 100%; + z-index: 50; + position: relative; + } + @media (min-width: 768px) { + .hero-bg { + display: block; + } + } + .hero-bg-mobile { + display: block; + margin-top: -16rem; + width: 100%; + } + @media (min-width: 768px) { + .hero-bg-mobile { + display: none; + } + } + .hero { + width: 100%; + } + @media (min-width: 640px) { + .hero { + max-width: 640px; + } + } + @media (min-width: 768px) { + .hero { + max-width: 768px; + } + } + @media (min-width: 1024px) { + .hero { + max-width: 1024px; + } + } + @media (min-width: 1280px) { + .hero { + max-width: 1280px; + } + } + @media (min-width: 1536px) { + .hero { + max-width: 1536px; + } + } + .hero { + position: relative; + z-index: 60; + margin-left: auto; + margin-right: auto; + margin-top: 3rem; + padding-left: 1rem; + padding-right: 1rem; + padding-bottom: 6rem; + } + @media (min-width: 768px) { + .hero { + margin-top: 6rem; + } + } + .hero-title-wrapper { + justify-content: space-between; + position: relative; + } + .hero-title { + font-size: 1.875rem; + line-height: 2.25rem; + } + @media (min-width: 768px) { + .hero-title { + font-size: 2.25rem; + line-height: 2.5rem; + line-height: 1.625; + } + } + @media (min-width: 1024px) { + .hero-title { + font-size: 3rem; + line-height: 1; + } + } + @media (min-width: 1440px) { + .hero-title { + font-size: 5rem; + } + } + @media (min-width: 1800px) { + .hero-title { + font-size: 6rem; + } + } + .hero-button { + display: flex; + margin-bottom: 3rem; + padding-top: 1.25rem; + } + @media (min-width: 768px) { + .hero-button { + padding-top: 2.5rem; + } + } + .hero-button__content { + --tw-border-opacity: 1; + border-color: rgba(210, 199, 91, var(--tw-border-opacity)); + width: 13rem; + } + .hero-button__split { + --tw-bg-opacity: 1; + background-color: rgba(18, 18, 18, var(--tw-bg-opacity)); + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); + } + .feature-button { + display: none; + justify-content: center; + margin-bottom: 3rem; + } + @media (min-width: 768px) { + .feature-button { + display: flex; + padding-top: 1.5rem; + } + } + .feature-button__content { + --tw-border-opacity: 1; + border-color: rgba(196, 91, 40, var(--tw-border-opacity)); + width: 24rem; + } + .feature-button__split { + --tw-bg-opacity: 1; + background-color: rgba(241, 226, 85, var(--tw-bg-opacity)); + --tw-text-opacity: 1; + color: rgba(0, 0, 0, var(--tw-text-opacity)); + } + .split-button { + background-color: transparent; + border-radius: 0.75rem; + border-style: solid; + border-width: 2px; + display: flex; + height: 3rem; + position: relative; + } + .split-button__button { + display: flex; + position: absolute; + left: -0.5rem; + top: -0.75rem; + } + .split-button__split1 { + border-radius: 0.75rem; + display: inline-flex; + align-items: center; + font-weight: 500; + height: 3rem; + margin-right: 0.5rem; + padding-top: 0.75rem; + padding-bottom: 0.75rem; + padding-left: 1.5rem; + padding-right: 1.5rem; + } + .split-button__split2 { + border-radius: 0.75rem; + display: inline-flex; + align-items: center; + font-weight: 500; + height: 3rem; + font-size: 1.125rem; + line-height: 1.75rem; + padding-top: 0.75rem; + padding-bottom: 0.75rem; + padding-left: 0.75rem; + padding-right: 0.75rem; + --tw-text-opacity: 1; + color: rgba(0, 0, 0, var(--tw-text-opacity)); + } + .transition { + width: 100%; + } + .feature__section { + width: full; + margin-top: -0.5rem; + background-color: #e37743; + } + .feature__wrapper { + width: 100%; + } + @media (min-width: 640px) { + .feature__wrapper { + max-width: 640px; + } + } + @media (min-width: 768px) { + .feature__wrapper { + max-width: 768px; + } + } + @media (min-width: 1024px) { + .feature__wrapper { + max-width: 1024px; + } + } + @media (min-width: 1280px) { + .feature__wrapper { + max-width: 1280px; + } + } + @media (min-width: 1536px) { + .feature__wrapper { + max-width: 1536px; + } + } + .feature__wrapper { + margin-left: auto; + margin-right: auto; + padding-left: 0; + padding-right: 0; + padding-top: 3rem; + } + @media (min-width: 768px) { + .feature__wrapper { + padding-left: 1rem; + padding-right: 1rem; + } + } + .feature__title { + font-size: 1.875rem; + line-height: 2.25rem; + margin-left: auto; + margin-right: auto; + padding-left: 1rem; + padding-right: 1rem; + width: 100%; + } + @media (min-width: 768px) { + .feature__title { + font-size: 2.25rem; + line-height: 2.5rem; + padding-left: 0; + padding-right: 0; + text-align: center; + width: 60%; + } + } + .feature__cards { + display: flex; + flex-wrap: wrap; + justify-content: center; + padding-top: 3rem; + } + .feature__card { + display: flex; + justify-content: center; + padding-bottom: 1.5rem; + width: 100%; + } + @media (min-width: 768px) { + .feature__card { + margin-bottom: 3.5rem; + width: 50%; + } + } + @media (min-width: 1024px) { + .feature__card { + width: 33.333333%; + } + } + .feature__card__content { + --tw-bg-opacity: 1; + background-color: rgba(208, 79, 34, var(--tw-bg-opacity)); + border-radius: 0; + padding-top: 1rem; + padding-bottom: 1rem; + padding-left: 2rem; + padding-right: 2rem; + width: 100%; + } + @media (min-width: 768px) { + .feature__card__content { + border-radius: 1.5rem; + height: 20rem; + width: 20rem; + } + } + .feature__card__heading { + display: inline-flex; + align-items: center; + padding-bottom: 1.25rem; + } + .feature__card__title { + font-size: 1.875rem; + line-height: 2.25rem; + margin-left: 1rem; + margin-bottom: -0.5rem; + } + .feature__card__description { + font-size: 1.125rem; + line-height: 1.75rem; + } + .components__section { + --tw-bg-opacity: 1; + background-color: rgba(58, 52, 83, var(--tw-bg-opacity)); + flex-direction: column; + align-items: center; + margin-top: -0.5rem; + padding-top: 3rem; + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); + width: 100%; + } + .component { + width: 100%; + } + @media (min-width: 640px) { + .component { + max-width: 640px; + } + } + @media (min-width: 768px) { + .component { + max-width: 768px; + } + } + @media (min-width: 1024px) { + .component { + max-width: 1024px; + } + } + @media (min-width: 1280px) { + .component { + max-width: 1280px; + } + } + @media (min-width: 1536px) { + .component { + max-width: 1536px; + } + } + .component { + display: flex; + flex-wrap: wrap; + align-items: center; + margin-left: auto; + margin-right: auto; + margin-bottom: 3rem; + padding-left: 1rem; + padding-right: 1rem; + } + .component__info { + padding-right: 0; + padding-bottom: 1rem; + width: 100%; + } + @media (min-width: 1024px) { + .component__info { + padding-bottom: 0; + padding-right: 2rem; + width: 33.333333%; + } + } + .component__info__title { + padding-bottom: 0.75rem; + } + .component__example { + width: 100%; + } + @media (min-width: 1024px) { + .component__example { + width: 66.666667%; + } + } + .component__example__code { + --tw-bg-opacity: 1; + background-color: rgba(255, 255, 255, var(--tw-bg-opacity)); + border-radius: 0.75rem; + height: 12rem; + } + footer { + --tw-bg-opacity: 1; + background-color: rgba(0, 0, 0, var(--tw-bg-opacity)); + margin-top: -0.5rem; + } + .footer__wrapper { + width: 100%; + } + @media (min-width: 640px) { + .footer__wrapper { + max-width: 640px; + } + } + @media (min-width: 768px) { + .footer__wrapper { + max-width: 768px; + } + } + @media (min-width: 1024px) { + .footer__wrapper { + max-width: 1024px; + } + } + @media (min-width: 1280px) { + .footer__wrapper { + max-width: 1280px; + } + } + @media (min-width: 1536px) { + .footer__wrapper { + max-width: 1536px; + } + } + .footer__wrapper { + margin-left: auto; + margin-right: auto; + padding-left: 1rem; + padding-right: 1rem; + } + .footer__nav { + display: flex; + flex-direction: column; + justify-content: space-between; + line-height: 2rem; + padding-top: 3rem; + width: 100%; + gap: 2rem; + } + @media (min-width: 768px) { + .footer__nav { + flex-direction: row; + width: 40%; + gap: 0; + } + } + .footer__nav__category { + --tw-text-opacity: 1; + color: rgba(91, 56, 233, var(--tw-text-opacity)); + } + .footer__nav__links { + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); + text-transform: uppercase; + } + .footer_copyright { + font-size: 0.875rem; + line-height: 1.25rem; + padding-top: 3rem; + padding-bottom: 3rem; + --tw-text-opacity: 1; + color: rgba(76, 76, 76, var(--tw-text-opacity)); + } + .documentation { + --tw-bg-opacity: 1; + background-color: rgba(250, 247, 230, var(--tw-bg-opacity)); + --tw-border-opacity: 1; + border-color: rgba(217, 206, 95, var(--tw-border-opacity)); + border-style: solid; + line-height: 2.25rem; + } + .mobile-mode { + background-color: #ede171 !important; + } + .top-border { + border-top-width: 4px; + } + .sidebar { + display: flex; + } + .side-nav > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1rem * var(--tw-space-y-reverse)); + } + .side-nav { + --tw-bg-opacity: 1; + background-color: rgba(242, 239, 217, var(--tw-bg-opacity)); + display: none; + flex-direction: column; + padding-left: 3.5rem; + padding-top: 5rem; + position: relative; + width: 21rem; + z-index: 0; + } + @media (min-width: 768px) { + .side-nav { + display: flex; + } + } + .side-nav__expand > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.25rem * var(--tw-space-y-reverse)); + } + .side-nav__expand__button { + border-radius: 0.75rem; + cursor: pointer; + display: flex; + align-items: center; + height: 3.5rem; + font-size: 1.125rem; + line-height: 1.75rem; + padding-left: 1rem; + padding-right: 1rem; + } + .side-nav__expand__button:hover { + --tw-text-opacity: 1; + color: rgba(76, 76, 76, var(--tw-text-opacity)); + } + .side-nav__expand__button { + width: 100%; + } + .side-nav__link:hover { + --tw-text-opacity: 1; + color: rgba(76, 76, 76, var(--tw-text-opacity)); + } + .side-nav__expand__icon { + margin-left: auto; + } + .side-nav__expand__list { + padding-left: 2rem; + } + .side-nav__expand__list2 { + padding-left: 1.5rem; + } + .docs { + width: 100%; + } + @media (min-width: 640px) { + .docs { + max-width: 640px; + } + } + @media (min-width: 768px) { + .docs { + max-width: 768px; + } + } + @media (min-width: 1024px) { + .docs { + max-width: 1024px; + } + } + @media (min-width: 1280px) { + .docs { + max-width: 1280px; + } + } + @media (min-width: 1536px) { + .docs { + max-width: 1536px; + } + } + .docs { + margin-left: auto; + margin-right: auto; + margin-left: 0; + margin-bottom: 6rem; + padding-left: 1rem; + padding-right: 1rem; + padding-top: 0; + } + @media (min-width: 768px) { + .docs { + margin-left: 2rem; + padding-top: 8rem; + } + } + .separator { + --tw-border-opacity: 1; + border-color: rgba(226, 224, 213, var(--tw-border-opacity)); + border-style: solid; + border-top-width: 2px; + margin-top: 2.5rem; + margin-bottom: 2.5rem; + } + .code-block { + --tw-border-opacity: 1; + border-color: rgba(226, 224, 213, var(--tw-border-opacity)); + border-style: solid; + border-width: 2px; + margin-top: 2.5rem; + margin-bottom: 2.5rem; + width: 100%; + } + .code-block__content { + margin: 1.5rem; + } + .bullet-list { + margin-top: 0.5rem; + margin-bottom: 0.5rem; + margin-left: 1rem; + } + .bullet-list__callout > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.5rem * var(--tw-space-x-reverse)); + margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); + } + .bullet-list__callout { + display: flex; + } + code.doc { + overflow-x: auto; + background-color: transparent; + --tw-border-opacity: 1; + border-color: rgba(226, 224, 213, var(--tw-border-opacity)); + border-width: 2px; + display: block; + margin-top: 1rem; + margin-bottom: 1rem; + padding: 1rem; + } + .admon { + border-style: dashed; + border-width: 2px; + margin-top: 2.5rem; + margin-bottom: 2.5rem; + width: 100%; + } + .admon-warning { + --tw-border-opacity: 1; + border-color: rgba(220, 38, 38, var(--tw-border-opacity)); + } + .admon__button { + border-radius: 9999px; + display: flex; + align-items: center; + justify-content: center; + height: 2rem; + margin-left: 1rem; + margin-top: -1rem; + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); + width: 8rem; + gap: 0.5rem; + } + .admon__button-warning { + --tw-bg-opacity: 1; + background-color: rgba(220, 38, 38, var(--tw-bg-opacity)); + } + .admon__button-note { + --tw-bg-opacity: 1; + background-color: rgba(30, 58, 138, var(--tw-bg-opacity)); + } + .admon-note { + --tw-border-opacity: 1; + border-color: rgba(30, 58, 138, var(--tw-border-opacity)); + } + .secondary-nav { + margin-bottom: 1.5rem; + } + @media (min-width: 768px) { + .secondary-nav { + display: none; + } + } + .secondary-nav__content { + --tw-border-opacity: 1; + border-color: rgba(210, 199, 91, var(--tw-border-opacity)); + width: 10rem; + } + .secondary-nav__split { + --tw-bg-opacity: 1; + background-color: rgba(18, 18, 18, var(--tw-bg-opacity)); + border-radius: 0.75rem; + display: inline-flex; + align-items: center; + justify-content: center; + font-weight: 500; + height: 3rem; + padding-left: 0.5rem; + padding-right: 0.5rem; + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); + width: 9rem; + } + .secondary-nav__split > p { + margin-right: 6px; + } + @media (min-width: 768px) { + .md\:flex-row { + flex-direction: row; + } + .md\:ml-8 { + margin-left: 2rem; + } + .md\:pt-32 { + padding-top: 8rem; + } + .md\:w-2\/5 { + width: 40%; + } + .md\:gap-0 { + gap: 0; + } + } + .nav-mobile { + display: block !important; + } -.nav-mobile{ - display: block !important; -} + .CodeMirror { + height: auto; + } -.CodeMirror { - height: auto; -} + .component__example__code { + overflow: hidden; + } -.component__example__code { - overflow: hidden; } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } -- GitLab From 148d173282f9535d84dc222683d36b9fb03bdf86 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 19 Apr 2021 13:32:37 -0600 Subject: [PATCH 047/116] add cabals --- .gitignore | 1 - .../pardiff/Shpadoinkle-backend-pardiff.cabal | 50 +++ .../Shpadoinkle-backend-snabbdom.cabal | 49 +++ .../static/Shpadoinkle-backend-static.cabal | 36 ++ console/Shpadoinkle-console.cabal | 42 +++ core/Shpadoinkle.cabal | 57 ++++ .../Shpadoinkle-developer-tools.cabal | 79 +++++ disembodied/Shpadoinkle-disembodied.cabal | 42 +++ examples/Shpadoinkle-examples.cabal | 321 ++++++++++++++++++ html/Shpadoinkle-html.cabal | 75 ++++ isreal/Shpadoinkle-isreal.cabal | 76 +++++ lens/Shpadoinkle-lens.cabal | 33 ++ router/Shpadoinkle-router.cabal | 69 ++++ streaming/Shpadoinkle-streaming.cabal | 34 ++ template/Shpadoinkle-template.cabal | 65 ++++ tests/Shpadoinkle-tests.cabal | 42 +++ website/Shpadoinkle-website.cabal | 151 ++++++++ widgets/Shpadoinkle-widgets.cabal | 98 ++++++ 18 files changed, 1319 insertions(+), 1 deletion(-) create mode 100644 backends/pardiff/Shpadoinkle-backend-pardiff.cabal create mode 100644 backends/snabbdom/Shpadoinkle-backend-snabbdom.cabal create mode 100644 backends/static/Shpadoinkle-backend-static.cabal create mode 100644 console/Shpadoinkle-console.cabal create mode 100644 core/Shpadoinkle.cabal create mode 100644 developer-tools/Shpadoinkle-developer-tools.cabal create mode 100644 disembodied/Shpadoinkle-disembodied.cabal create mode 100644 examples/Shpadoinkle-examples.cabal create mode 100644 html/Shpadoinkle-html.cabal create mode 100644 isreal/Shpadoinkle-isreal.cabal create mode 100644 lens/Shpadoinkle-lens.cabal create mode 100644 router/Shpadoinkle-router.cabal create mode 100644 streaming/Shpadoinkle-streaming.cabal create mode 100644 template/Shpadoinkle-template.cabal create mode 100644 tests/Shpadoinkle-tests.cabal create mode 100644 website/Shpadoinkle-website.cabal create mode 100644 widgets/Shpadoinkle-widgets.cabal diff --git a/.gitignore b/.gitignore index 500af88a..3fbf96dc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ *dist* -*.cabal *.ghc* *result* *project.local diff --git a/backends/pardiff/Shpadoinkle-backend-pardiff.cabal b/backends/pardiff/Shpadoinkle-backend-pardiff.cabal new file mode 100644 index 00000000..542c7dcc --- /dev/null +++ b/backends/pardiff/Shpadoinkle-backend-pardiff.cabal @@ -0,0 +1,50 @@ +cabal-version: 1.12 + +-- This file has been generated from package.yaml by hpack version 0.33.0. +-- +-- see: https://github.com/sol/hpack +-- +-- hash: 3b4db1eab5d10653c65c5dbcbf7e37375bf8cb79698d293399067889c4df3332 + +name: Shpadoinkle-backend-pardiff +version: 0.3.0.0 +synopsis: A Virtual Dom in pure Haskell, based on Html as an Alignable Functor. +description: Virtual DOM diffing in pure Haskell. The diffing is a lawful usage of @alignWith@ from the @Data.These@ package. This implementation stores a reference to the @RawNode@ for each node in the virtual tree, merging keyed and unkeyed virtual DOM techniques. +category: Web +author: Isaac Shapira +maintainer: fresheyeball@protonmail.com +license: BSD3 +license-file: LICENSE +build-type: Simple +extra-source-files: + README.md + CHANGELOG.md + +source-repository head + type: git + location: https://gitlab.com/platonic/shpadoinkle.git + +library + exposed-modules: + Shpadoinkle.Backend.ParDiff + other-modules: + Data.Once + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + build-depends: + Shpadoinkle + , base >=4.12.0 && <4.16 + , compactable >=0.1.2 && <0.2 + , containers >=0.6.0 && <0.7 + , exceptions + , file-embed >=0.0.11 && <0.1 + , ghcjs-dom >=0.9.4 && <0.20 + , jsaddle >=0.9.7 && <0.20 + , monad-control >=1.0.2 && <1.1 + , mtl >=2.2.2 && <2.3 + , random >=1.1 && <1.3 + , text >=1.2.3 && <1.3 + , transformers-base >=0.4.5 && <0.5 + , unliftio >=0.2.12 && <0.3 + default-language: Haskell2010 diff --git a/backends/snabbdom/Shpadoinkle-backend-snabbdom.cabal b/backends/snabbdom/Shpadoinkle-backend-snabbdom.cabal new file mode 100644 index 00000000..d9883869 --- /dev/null +++ b/backends/snabbdom/Shpadoinkle-backend-snabbdom.cabal @@ -0,0 +1,49 @@ +cabal-version: 1.12 + +-- This file has been generated from package.yaml by hpack version 0.33.0. +-- +-- see: https://github.com/sol/hpack +-- +-- hash: a048ff109376f11935477724f967aa8203f1ca6a38f53df07bb90661bae9550e + +name: Shpadoinkle-backend-snabbdom +version: 0.3.0.1 +synopsis: Use the high-performance Snabbdom virtual dom library written in JavaScript. +description: Snabbdom is a battle-tested virtual DOM library for JavaScript. It's extremely fast, lean, and modular. Snabbdom's design makes Snabbdom a natural choice for a Shpadoinkle rendering backend, as it has a similar core philosophy of "just don't do much" and is friendly to purely functional binding. +category: Web +author: Isaac Shapira +maintainer: fresheyeball@protonmail.com +license: BSD3 +license-file: LICENSE +build-type: Simple +extra-source-files: + README.md + CHANGELOG.md + Shpadoinkle/Backend/Snabbdom/Setup.js + +source-repository head + type: git + location: https://gitlab.com/platonic/shpadoinkle.git + +library + exposed-modules: + Shpadoinkle.Backend.Snabbdom + other-modules: + Paths_Shpadoinkle_backend_snabbdom + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + build-depends: + Shpadoinkle + , base >=4.12.0 && <4.16 + , containers + , exceptions + , file-embed >=0.0.11 && <0.1 + , ghcjs-dom >=0.9.4 && <0.20 + , jsaddle >=0.9.7 && <0.20 + , monad-control + , mtl >=2.2.2 && <2.3 + , text >=1.2.3 && <1.3 + , transformers-base + , unliftio >=0.2.12 && <0.3 + default-language: Haskell2010 diff --git a/backends/static/Shpadoinkle-backend-static.cabal b/backends/static/Shpadoinkle-backend-static.cabal new file mode 100644 index 00000000..aeb41c3f --- /dev/null +++ b/backends/static/Shpadoinkle-backend-static.cabal @@ -0,0 +1,36 @@ +cabal-version: 1.12 + +-- This file has been generated from package.yaml by hpack version 0.33.0. +-- +-- see: https://github.com/sol/hpack +-- +-- hash: abdea4994cb952fb1705f4fa5521e4317836fd2b39c00be6b2734d5d934a1c46 + +name: Shpadoinkle-backend-static +version: 0.2.0.0 +synopsis: A backend for rendering Shpadoinkle as Text. +description: Shpadoinkle's static backend, which renders Html as Text. Event listeners are ignored. This is useful when rendering on the server or for static site generation. +category: Web +author: Isaac Shapira +maintainer: fresheyeball@protonmail.com +license: BSD3 +license-file: LICENSE +build-type: Simple +extra-source-files: + README.md + CHANGELOG.md + +library + exposed-modules: + Shpadoinkle.Backend.Static + other-modules: + Paths_Shpadoinkle_backend_static + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + build-depends: + Shpadoinkle + , base >=4.12.0 && <4.16 + , compactable >=0.1.2 && <0.2 + , text >=1.2.3 && <1.3 + default-language: Haskell2010 diff --git a/console/Shpadoinkle-console.cabal b/console/Shpadoinkle-console.cabal new file mode 100644 index 00000000..82b67c08 --- /dev/null +++ b/console/Shpadoinkle-console.cabal @@ -0,0 +1,42 @@ +cabal-version: 1.12 + +-- This file has been generated from package.yaml by hpack version 0.33.0. +-- +-- see: https://github.com/sol/hpack +-- +-- hash: af7b9889395474ff23153d26a2b46bb5fed502da1e4d6772a74dfd6b97df8d03 + +name: Shpadoinkle-console +version: 0.0.1.3 +synopsis: Support for the native browser console +description: This package adds support for native browser console features for logging and debugging. +category: Web +author: Isaac Shapira +maintainer: fresheyeball@protonmail.com +license: BSD3 +license-file: LICENSE +build-type: Simple +extra-source-files: + README.md + CHANGELOG.md + +source-repository head + type: git + location: https://gitlab.com/platonic/shpadoinkle.git + +library + exposed-modules: + Shpadoinkle.Console + other-modules: + Paths_Shpadoinkle_console + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + build-depends: + aeson >=1.4.4 && <1.6 + , base >=4.12.0 && <4.16 + , jsaddle >=0.9.7 && <0.20 + , lens >=4.17.1 && <5.0 + , text >=1.2.3 && <1.3 + , unliftio >=0.2.12 && <0.3 + default-language: Haskell2010 diff --git a/core/Shpadoinkle.cabal b/core/Shpadoinkle.cabal new file mode 100644 index 00000000..6b4da670 --- /dev/null +++ b/core/Shpadoinkle.cabal @@ -0,0 +1,57 @@ +cabal-version: 1.12 + +-- This file has been generated from package.yaml by hpack version 0.33.0. +-- +-- see: https://github.com/sol/hpack +-- +-- hash: 96c37df87a2e3539b59df43a9fa2593f70b4d3bc824b8fee70acd32d4ca07495 + +name: Shpadoinkle +version: 0.3.0.0 +synopsis: A programming model for declarative, high performance user interface. +description: Shpadoinkle is an abstract frontend programming model, with one-way data flow, and a single source of truth. This module provides a parsimonious implementation of Shpadoinkle with few implementation details. +category: Web +author: Isaac Shapira +maintainer: fresheyeball@protonmail.com +license: BSD3 +license-file: LICENSE +build-type: Simple +extra-source-files: + README.md + CHANGELOG.md + +source-repository head + type: git + location: https://gitlab.com/platonic/shpadoinkle.git + +library + exposed-modules: + Control.PseudoInverseCategory + Shpadoinkle + Shpadoinkle.Continuation + Shpadoinkle.Core + Shpadoinkle.Run + other-modules: + Paths_Shpadoinkle + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + build-depends: + base >=4.12.0 && <4.16 + , category >=0.2 && <0.3 + , containers + , deepseq + , ghcjs-dom >=0.9.4 && <0.20 + , jsaddle >=0.9.7 && <0.20 + , mtl + , text >=1.2.3 && <1.3 + , transformers + , unliftio + if impl(ghcjs) + else + build-depends: + jsaddle-warp >=0.9.7 && <0.20 + , wai + , wai-app-static + , warp + default-language: Haskell2010 diff --git a/developer-tools/Shpadoinkle-developer-tools.cabal b/developer-tools/Shpadoinkle-developer-tools.cabal new file mode 100644 index 00000000..3a86434e --- /dev/null +++ b/developer-tools/Shpadoinkle-developer-tools.cabal @@ -0,0 +1,79 @@ +cabal-version: 1.12 + +-- This file has been generated from package.yaml by hpack version 0.33.0. +-- +-- see: https://github.com/sol/hpack +-- +-- hash: 7d433fb1a42d3f955d1e3915867d60bda6a8c6233349de9e9767255cfdcc0474 + +name: Shpadoinkle-developer-tools +version: 0.0.0.1 +synopsis: Chrome extension to aide in development +description: A chrome extension to make developing Shpadoinkle applications easier +category: Web +author: Isaac Shapira +maintainer: fresheyeball@protonmail.com +license: BSD3 +license-file: LICENSE +build-type: Simple +extra-source-files: + README.md + +source-repository head + type: git + location: https://gitlab.com/platonic/shpadoinkle.git + +flag development + description: Add instrumentation for development purposes. + manual: True + default: False + +library + exposed-modules: + Shpadoinkle.DeveloperTools + other-modules: + Main + Paths_Shpadoinkle_developer_tools + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + build-depends: + Shpadoinkle + , Shpadoinkle-backend-pardiff + , Shpadoinkle-html + , base >=4.12.0 && <4.16 + , containers + , jsaddle + , lens + , pretty-show + , stm + , text + , time + , unliftio + if flag(development) + cpp-options: -DDEVELOPMENT + else + default-language: Haskell2010 + +executable devtools + main-is: Main.hs + other-modules: + Shpadoinkle.DeveloperTools + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -O2 + build-depends: + Shpadoinkle + , Shpadoinkle-backend-pardiff + , Shpadoinkle-html + , base >=4.12.0 && <4.16 + , containers + , jsaddle + , lens + , pretty-show + , stm + , text + , time + , unliftio + default-language: Haskell2010 diff --git a/disembodied/Shpadoinkle-disembodied.cabal b/disembodied/Shpadoinkle-disembodied.cabal new file mode 100644 index 00000000..cd24e80a --- /dev/null +++ b/disembodied/Shpadoinkle-disembodied.cabal @@ -0,0 +1,42 @@ +cabal-version: 1.12 + +-- This file has been generated from package.yaml by hpack version 0.33.0. +-- +-- see: https://github.com/sol/hpack +-- +-- hash: c9dcea41cbf0ee840bc68d0b857f3bf7b71f5d173c808dcd6c800cf9c9c11fc4 + +name: Shpadoinkle-disembodied +version: 0.0.0.1 +synopsis: Shpadoinkle as a static site. +description: Static site backed SPA applications. +category: Web +author: Isaac Shapira +maintainer: fresheyeball@protonmail.com +license: BSD3 +license-file: LICENSE +build-type: Simple +extra-source-files: + README.md + CHANGELOG.md + +library + exposed-modules: + Shpadoinkle.Disembodied + other-modules: + Shpadoinkle.Disembodied.Sample + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + build-depends: + Shpadoinkle + , Shpadoinkle-backend-static + , Shpadoinkle-html + , Shpadoinkle-router + , base >=4.12.0 && <4.16 + , directory + , filepath + , servant + , text >=1.2.3 && <1.3 + , unliftio + default-language: Haskell2010 diff --git a/examples/Shpadoinkle-examples.cabal b/examples/Shpadoinkle-examples.cabal new file mode 100644 index 00000000..3db416c5 --- /dev/null +++ b/examples/Shpadoinkle-examples.cabal @@ -0,0 +1,321 @@ +cabal-version: 1.12 + +-- This file has been generated from package.yaml by hpack version 0.33.0. +-- +-- see: https://github.com/sol/hpack +-- +-- hash: bb457dcc8282c5a3e9d4d75f9db962ca18fe63dce94330b1ee9e775409d2fbe9 + +name: Shpadoinkle-examples +version: 0.0.0.3 +synopsis: Example usages of Shpadoinkle +description: A collection of illustrative applications to show various Shpadoinkle utilities. +category: Web +author: Isaac Shapira +maintainer: fresheyeball@protonmail.com +license: BSD3 +license-file: LICENSE +build-type: Simple +extra-source-files: + README.md + CHANGELOG.md + +source-repository head + type: git + location: https://gitlab.com/platonic/shpadoinkle.git + +executable animation + main-is: Animation.hs + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -dedupe -O2 + build-depends: + Shpadoinkle + , Shpadoinkle-backend-snabbdom + , Shpadoinkle-html + , base >=4.12.0 && <4.16 + , ease + , ghcjs-dom + , stm + , text + , unliftio + default-language: Haskell2010 + +executable calculator + main-is: Calculator.hs + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -dedupe -O2 + build-depends: + Shpadoinkle + , Shpadoinkle-backend-pardiff + , Shpadoinkle-html + , Shpadoinkle-lens + , Shpadoinkle-widgets + , base >=4.12.0 && <4.16 + , lens + , safe + , text + default-language: Haskell2010 + +executable calculator-ie + main-is: CalculatorIE.hs + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -dedupe -O2 + build-depends: + Shpadoinkle + , Shpadoinkle-backend-pardiff + , Shpadoinkle-console + , Shpadoinkle-html + , Shpadoinkle-widgets + , aeson + , base >=4.12.0 && <4.16 + , file-embed + , lens + , safe + , split + , text + default-language: Haskell2010 + +executable counter + main-is: Counter.hs + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -dedupe -O2 + build-depends: + Shpadoinkle + , Shpadoinkle-backend-pardiff + , Shpadoinkle-html + , base >=4.12.0 && <4.16 + , stm + , text + default-language: Haskell2010 + +executable lens + main-is: Lens.hs + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -dedupe -O2 + build-depends: + Shpadoinkle + , Shpadoinkle-backend-pardiff + , Shpadoinkle-html + , Shpadoinkle-lens + , base >=4.12.0 && <4.16 + , generic-lens + , lens + , safe + , text + default-language: Haskell2010 + +executable servant-crud-client + main-is: Run/Client.hs + other-modules: + Types + Types.Prim + View + Client + hs-source-dirs: + ./servant-crud + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -dedupe -O2 + build-depends: + Shpadoinkle + , Shpadoinkle-backend-pardiff + , Shpadoinkle-backend-snabbdom + , Shpadoinkle-html + , Shpadoinkle-lens + , Shpadoinkle-router + , Shpadoinkle-widgets + , aeson + , base >=4.12.0 && <4.16 + , beam-core + , bytestring + , containers + , exceptions + , jsaddle + , lens + , mtl + , servant + , stm + , text + , unliftio + if impl(ghcjs) + build-depends: + servant-client-js + else + build-depends: + beam-sqlite + , servant-client + , sqlite-simple + default-language: Haskell2010 + +executable servant-crud-dev + main-is: Run/Dev.hs + other-modules: + Types.Prim + Types + View + Client + Server + hs-source-dirs: + ./servant-crud + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -dedupe -O2 + build-depends: + Shpadoinkle + , Shpadoinkle-backend-pardiff + , Shpadoinkle-backend-snabbdom + , Shpadoinkle-backend-static + , Shpadoinkle-html + , Shpadoinkle-lens + , Shpadoinkle-router + , Shpadoinkle-widgets + , aeson + , base >=4.12.0 && <4.16 + , beam-core + , beam-sqlite + , bytestring + , containers + , exceptions + , file-embed + , generic-monoid + , jsaddle + , lens + , mtl + , optparse-applicative + , servant + , servant-server + , sqlite-simple + , stm + , text + , unliftio + , wai + , wai-app-static + , warp + if impl(ghcjs) + buildable: False + else + buildable: True + default-language: Haskell2010 + +executable servant-crud-server + main-is: Run/Server.hs + other-modules: + Types.Prim + Types + View + Server + hs-source-dirs: + ./servant-crud + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -dedupe -O2 + build-depends: + Shpadoinkle + , Shpadoinkle-backend-static + , Shpadoinkle-html + , Shpadoinkle-lens + , Shpadoinkle-router + , Shpadoinkle-widgets + , aeson + , base >=4.12.0 && <4.16 + , beam-core + , beam-sqlite + , bytestring + , containers + , file-embed + , lens + , mtl + , optparse-applicative + , servant + , servant-server + , sqlite-simple + , stm + , text + , wai + , wai-app-static + , warp + if impl(ghcjs) + buildable: False + else + buildable: True + default-language: Haskell2010 + +executable streaming + main-is: Streaming.hs + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -dedupe -O2 + build-depends: + Shpadoinkle + , Shpadoinkle-backend-pardiff + , Shpadoinkle-html + , Shpadoinkle-streaming + , base >=4.12.0 && <4.16 + , streaming + , text + default-language: Haskell2010 + +executable throttle-and-debounce + main-is: ThrottleAndDebounce.hs + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -dedupe -O2 + build-depends: + Shpadoinkle + , Shpadoinkle-backend-pardiff + , Shpadoinkle-console + , Shpadoinkle-html + , base >=4.12.0 && <4.16 + , lens + , safe + , text + default-language: Haskell2010 + +executable todomvc + main-is: TODOMVC.hs + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -dedupe -O2 + build-depends: + Shpadoinkle + , Shpadoinkle-backend-snabbdom + , Shpadoinkle-html + , Shpadoinkle-lens + , base >=4.12.0 && <4.16 + , containers + , generic-lens + , lens + , pretty-show + , text + default-language: Haskell2010 + +executable widgets + main-is: Widgets.hs + other-modules: + Paths_Shpadoinkle_examples + hs-source-dirs: + ./widgets + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -dedupe -O2 + build-depends: + Shpadoinkle + , Shpadoinkle-backend-pardiff + , Shpadoinkle-backend-snabbdom + , Shpadoinkle-html + , Shpadoinkle-lens + , Shpadoinkle-widgets + , base >=4.12.0 && <4.16 + , lens + , stm + , text + default-language: Haskell2010 diff --git a/html/Shpadoinkle-html.cabal b/html/Shpadoinkle-html.cabal new file mode 100644 index 00000000..5328eb7a --- /dev/null +++ b/html/Shpadoinkle-html.cabal @@ -0,0 +1,75 @@ +cabal-version: 1.12 + +-- This file has been generated from package.yaml by hpack version 0.33.0. +-- +-- see: https://github.com/sol/hpack +-- +-- hash: 402c5cdfd2e1bf6a112fb4d5292577a41d13cec2cf416fe1f2f2b95427e9cc4c + +name: Shpadoinkle-html +version: 0.3.0.0 +synopsis: A typed, template generated Html DSL, and helpers. +description: Shpadoinkle Html is a typed template-generated Html DSL building on types provided by Shpadoinkle Core. This exports a large namespace of terms covering most of the Html specifications. Some Elm-API-style helpers are present, but as outlaw type classes. +category: Web +author: Isaac Shapira +maintainer: fresheyeball@protonmail.com +license: BSD3 +license-file: LICENSE +build-type: Simple +extra-source-files: + README.md + CHANGELOG.md + +source-repository head + type: git + location: https://gitlab.com/platonic/shpadoinkle.git + +library + exposed-modules: + Shpadoinkle.Html + Shpadoinkle.Html.Element + Shpadoinkle.Html.Event + Shpadoinkle.Html.Property + Shpadoinkle.Html.Memo + Shpadoinkle.Html.MicroData + Shpadoinkle.Html.Utils + Shpadoinkle.Html.LocalStorage + Shpadoinkle.Html.TH + Shpadoinkle.Html.TH.CSS + Shpadoinkle.Html.TH.AssetLink + Shpadoinkle.WebWorker + Shpadoinkle.Keyboard + other-modules: + Shpadoinkle.Html.Event.Basic + Shpadoinkle.Html.Event.Debounce + Shpadoinkle.Html.Event.Throttle + Shpadoinkle.Html.TH.CSSTest + Paths_Shpadoinkle_html + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + build-depends: + Shpadoinkle + , base >=4.12.0 && <4.16 + , bytestring + , compactable + , containers >=0.6.0 && <0.7 + , directory + , ghcjs-dom + , jsaddle >=0.9.7 && <0.20 + , lens + , process + , raw-strings-qq + , stm >=2.5.0 && <2.6 + , template-haskell >=2.14.0 && <2.17 + , text >=1.2.3 && <1.3 + , time + , transformers + , unliftio + if impl(ghcjs) + build-depends: + ghcjs-base + else + build-depends: + regex-pcre + default-language: Haskell2010 diff --git a/isreal/Shpadoinkle-isreal.cabal b/isreal/Shpadoinkle-isreal.cabal new file mode 100644 index 00000000..6b753bb7 --- /dev/null +++ b/isreal/Shpadoinkle-isreal.cabal @@ -0,0 +1,76 @@ +cabal-version: 1.12 + +-- This file has been generated from package.yaml by hpack version 0.33.0. +-- +-- see: https://github.com/sol/hpack +-- +-- hash: 118b6fdff3e8ab49b245598cbcb670c28aa9c7fc5c59b269549a1a722eb188a9 + +name: Shpadoinkle-isreal +version: 0.0.0.1 +synopsis: Isreal Swan will make a snowman for you! +description: Snowman as a service. The web server calls a bash script. That builds inside a nix-shell. Results are returned via http. +category: Web +author: Isaac Shapira +maintainer: fresheyeball@protonmail.com +license: GPL-3 +license-file: LICENSE +build-type: Simple +extra-source-files: + README.md + CHANGELOG.md + +source-repository head + type: git + location: https://gitlab.com/platonic/shpadoinkle.git + +library + exposed-modules: + Shpadoinkle.Isreal.Types + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -rtsopts -O2 -threaded + build-depends: + aeson + , base >=4.12.0 && <4.16 + , bytestring + , containers + , deepseq + , random + , servant + , text + , time + if impl(ghcjs) + else + build-depends: + servant-server + default-language: Haskell2010 + +executable isreal + main-is: Main.hs + other-modules: + Shpadoinkle.Isreal.Types + Paths_Shpadoinkle_isreal + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -rtsopts -O2 -threaded + build-depends: + aeson + , base >=4.12.0 && <4.16 + , bytestring + , containers + , deepseq + , directory + , filepath + , process + , random + , servant + , servant-server + , text + , time + , warp + if impl(ghcjs) + buildable: False + else + buildable: True + default-language: Haskell2010 diff --git a/lens/Shpadoinkle-lens.cabal b/lens/Shpadoinkle-lens.cabal new file mode 100644 index 00000000..153601f5 --- /dev/null +++ b/lens/Shpadoinkle-lens.cabal @@ -0,0 +1,33 @@ +cabal-version: 1.12 + +-- This file has been generated from package.yaml by hpack version 0.33.0. +-- +-- see: https://github.com/sol/hpack +-- +-- hash: 8044321b8a3c2ed1b16f23e5bd3160b932676c32a3828891205748092e686b67 + +name: Shpadoinkle-lens +version: 0.0.0.3 +synopsis: Lens combinators for Shpadoinkle applications. +description: Lens combinators for Shpadoinkle applications. +category: Web +author: Isaac Shapira +maintainer: fresheyeball@protonmail.com +license: BSD3 +license-file: LICENSE +build-type: Simple + +library + exposed-modules: + Shpadoinkle.Lens + other-modules: + Paths_Shpadoinkle_lens + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + build-depends: + Shpadoinkle + , base >=4.12.0 && <4.16 + , lens + , text >=1.2.3 && <1.3 + default-language: Haskell2010 diff --git a/router/Shpadoinkle-router.cabal b/router/Shpadoinkle-router.cabal new file mode 100644 index 00000000..5cb56976 --- /dev/null +++ b/router/Shpadoinkle-router.cabal @@ -0,0 +1,69 @@ +cabal-version: 1.12 + +-- This file has been generated from package.yaml by hpack version 0.33.0. +-- +-- see: https://github.com/sol/hpack +-- +-- hash: e75fb22c1809256ce5fedcc48873f0868410f40711cd7c59ece23b7c3a70c214 + +name: Shpadoinkle-router +version: 0.3.0.0 +synopsis: A single page application rounter for Shpadoinkle based on Servant. +description: Surjective single page application routing with Servant. Surjectivity in this context means routes can be backward compatible, allowing URLs to evolve. Since routes are specified as Servant combinators, serving these routes from the backend is trivial. For an example of leveraging the client-server isomorphism via Servant specification, check the servant-crud example. +category: Web +author: Isaac Shapira +maintainer: fresheyeball@protonmail.com +license: BSD3 +license-file: LICENSE +build-type: Simple +extra-source-files: + README.md + CHANGELOG.md + +source-repository head + type: git + location: https://gitlab.com/platonic/shpadoinkle.git + +library + exposed-modules: + Shpadoinkle.Router + Shpadoinkle.Router.Client + Shpadoinkle.Router.Server + other-modules: + Paths_Shpadoinkle_router + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + build-depends: + Shpadoinkle + , aeson >=1.4.4 && <1.6 + , base >=4.12.0 && <4.16 + , bytestring >=0.10.8 && <0.12 + , compactable >=0.1.2 && <0.2 + , exceptions >=0.10.3 && <0.11 + , ghcjs-dom >=0.9.4 && <0.20 + , http-api-data >=0.4.1 && <0.5 + , http-media + , jsaddle >=0.9.7 && <0.20 + , lens >=4.17.1 && <5.0 + , network-uri >=2.6.1 && <2.8 + , servant >=0.16 && <0.19 + , servant-rawm + , text >=1.2.3 && <1.3 + , unliftio >=0.2.12 && <0.3 + if impl(ghcjs) + build-depends: + servant-client-js >=0.1 && <0.2 + else + build-depends: + Shpadoinkle-backend-static + , http-types + , jsaddle-warp + , servant-client >=0.15.0 && <0.19 + , servant-client-js >=0.1 && <0.2 + , servant-rawm-server + , servant-server >=0.16 && <0.18 + , wai >=3.2.2 && <3.3 + , wai-app-static >=3.1.6 && <3.2 + , warp >=3.2.28 && <3.4 + default-language: Haskell2010 diff --git a/streaming/Shpadoinkle-streaming.cabal b/streaming/Shpadoinkle-streaming.cabal new file mode 100644 index 00000000..5ead1ced --- /dev/null +++ b/streaming/Shpadoinkle-streaming.cabal @@ -0,0 +1,34 @@ +cabal-version: 1.12 + +-- This file has been generated from package.yaml by hpack version 0.33.0. +-- +-- see: https://github.com/sol/hpack +-- +-- hash: 693d20b2676ff508bd219f3ec4316438b9fddb2c2fec92a13496b5e2da453954 + +name: Shpadoinkle-streaming +version: 0.0.0.1 +synopsis: Integration of the streaming library with Shpadoinkle continuations. +description: Integration of the streaming library with Shpadoinkle continuations. +category: Web +author: Morgan Thomas +maintainer: morgan.a.s.thomas@gmail.com +license: BSD3 +license-file: LICENSE +build-type: Simple + +library + exposed-modules: + Shpadoinkle.Streaming + other-modules: + Paths_Shpadoinkle_streaming + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + build-depends: + Shpadoinkle + , base >=4.12.0 && <4.16 + , lens + , streaming >=0.2 && <0.3 + , text + default-language: Haskell2010 diff --git a/template/Shpadoinkle-template.cabal b/template/Shpadoinkle-template.cabal new file mode 100644 index 00000000..9f2be26d --- /dev/null +++ b/template/Shpadoinkle-template.cabal @@ -0,0 +1,65 @@ +cabal-version: 1.12 + +-- This file has been generated from package.yaml by hpack version 0.33.0. +-- +-- see: https://github.com/sol/hpack +-- +-- hash: 1e2003b0d4387754ec560f0375528138ee32f9942fcf994d527ffcbbc35fcc39 + +name: Shpadoinkle-template +version: 0.0.0.1 +synopsis: Read standard file formats into Shpadoinkle with Template Haskell +description: This package provides TH functions to read files at compile time and embed them into Shpadoinkle views. +category: Web +author: Isaac Shapira +maintainer: fresheyeball@protonmail.com +license: BSD3 +license-file: LICENSE +build-type: Simple +extra-source-files: + README.md + CHANGELOG.md + +source-repository head + type: git + location: https://gitlab.com/platonic/shpadoinkle.git + +library + exposed-modules: + Shpadoinkle.Template.TH + Shpadoinkle.Template + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + build-depends: + Shpadoinkle + , Shpadoinkle-backend-static + , base >=4.12.0 && <4.16 + , directory + , html-parse + , process + , template-haskell + , text >=1.2.3 && <1.3 + default-language: Haskell2010 + +test-suite sample + type: exitcode-stdio-1.0 + main-is: Test.hs + other-modules: + Shpadoinkle.Template.TH + Shpadoinkle.Template + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + build-depends: + Shpadoinkle + , Shpadoinkle-backend-static + , Shpadoinkle-template + , base >=4.12.0 && <4.16 + , directory + , file-embed + , html-parse + , process + , template-haskell + , text >=1.2.3 && <1.3 + default-language: Haskell2010 diff --git a/tests/Shpadoinkle-tests.cabal b/tests/Shpadoinkle-tests.cabal new file mode 100644 index 00000000..fdb3d6d4 --- /dev/null +++ b/tests/Shpadoinkle-tests.cabal @@ -0,0 +1,42 @@ +cabal-version: 1.12 + +-- This file has been generated from package.yaml by hpack version 0.33.0. +-- +-- see: https://github.com/sol/hpack +-- +-- hash: 31a957298d4af55a5eaed244f587ce985dad5c9a8f515f05348956b10d76fe66 + +name: Shpadoinkle-tests +version: 0.0.0.1 +category: Web +author: Isaac Shapira +maintainer: fresheyeball@protonmail.com +license: BSD3 +license-file: LICENSE +build-type: Simple + +executable tests + main-is: Main.hs + other-modules: + CounterSpec + Spec + TODOSpec + Util + Widgets.DropdownSpec + Paths_Shpadoinkle_tests + hs-source-dirs: + ./. + ghc-options: -fwarn-redundant-constraints -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + build-depends: + QuickCheck + , base + , containers + , hspec + , hspec-core + , optparse-applicative + , process + , text + , wai-app-static + , warp + , webdriver + default-language: Haskell2010 diff --git a/website/Shpadoinkle-website.cabal b/website/Shpadoinkle-website.cabal new file mode 100644 index 00000000..194b1e07 --- /dev/null +++ b/website/Shpadoinkle-website.cabal @@ -0,0 +1,151 @@ +cabal-version: 1.12 + +-- This file has been generated from package.yaml by hpack version 0.33.0. +-- +-- see: https://github.com/sol/hpack +-- +-- hash: 2ae0448f068492147c5322ad2ee5c0ebb17bb81f05e5c22bea7872d3c2267f2e + +name: Shpadoinkle-website +version: 0.0.0.1 +synopsis: Shpadoinkle website +description: Website for Shpadoinkle, includeing splash pages, sandboxes, and documentation. +category: Web +author: Isaac Shapira +maintainer: fresheyeball@protonmail.com +license: GPL-3 +license-file: LICENSE +build-type: Simple +extra-source-files: + README.md + CHANGELOG.md + +executable disembodied + main-is: Shpadoinkle/Website/Disembodied.hs + other-modules: + Shpadoinkle.Website.Style + Shpadoinkle.Website.Page.Home + Shpadoinkle.Website.Page.Documentation + Shpadoinkle.Website.Partials.Template + Shpadoinkle.Website.Types.Effects.Example + Shpadoinkle.Website.Types.Effects.Hooglable + Shpadoinkle.Website.Types.Effects.Swan + Shpadoinkle.Website.Types.Frontend + Shpadoinkle.Website.Types.CurrentYear + Shpadoinkle.Website.Types.Home + Shpadoinkle.Website.Types.Routes + Shpadoinkle.Website.View + Shpadoinkle.Website.Component.LiveExample + Shpadoinkle.Website.Partials.Footer + Shpadoinkle.Website.Partials.Nav + Shpadoinkle.Website.Types.Example + Shpadoinkle.Website.Types.ExampleState + Shpadoinkle.Website.Types.Hoogle + Shpadoinkle.Website.Types.Hoogle.Target + Shpadoinkle.Website.Types.Nav + Shpadoinkle.Website.Types.Routes.GettingStarted + Shpadoinkle.Website.Types.Routes.Packages + Shpadoinkle.Website.Types.Routes.Tutorial + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -O2 + build-depends: + Shpadoinkle + , Shpadoinkle-console + , Shpadoinkle-disembodied + , Shpadoinkle-html + , Shpadoinkle-isreal + , Shpadoinkle-lens + , Shpadoinkle-router + , Shpadoinkle-template + , Shpadoinkle-widgets + , aeson + , base >=4.12.0 && <4.16 + , bytestring + , exceptions + , file-embed + , generic-lens + , generic-monoid + , ghcjs-dom + , jsaddle + , lens + , mtl + , random + , servant + , servant-server + , text >=1.2.3 && <1.3 + , time + , unliftio + if impl(ghcjs) + buildable: False + else + buildable: True + default-language: Haskell2010 + +executable run + main-is: Shpadoinkle/Website/Run.hs + other-modules: + Shpadoinkle.Website.Component.LiveExample + Shpadoinkle.Website.Partials.Template + Shpadoinkle.Website.Partials.Nav + Shpadoinkle.Website.Partials.Footer + Shpadoinkle.Website.Page.Home + Shpadoinkle.Website.Page.Documentation + Shpadoinkle.Website.Types.CurrentYear + Shpadoinkle.Website.Types.Nav + Shpadoinkle.Website.Types.Home + Shpadoinkle.Website.Types.Hoogle + Shpadoinkle.Website.Types.Hoogle.API + Shpadoinkle.Website.Types.Hoogle.Target + Shpadoinkle.Website.Types.Example + Shpadoinkle.Website.Types.Frontend + Shpadoinkle.Website.Types.ExampleState + Shpadoinkle.Website.Types.Effects.Example + Shpadoinkle.Website.Types.Effects.Hooglable + Shpadoinkle.Website.Types.Effects.Swan + Shpadoinkle.Website.Types.Routes.GettingStarted + Shpadoinkle.Website.Types.Routes.Tutorial + Shpadoinkle.Website.Types.Routes.Packages + Shpadoinkle.Website.Types.Routes + Shpadoinkle.Website.Style + Shpadoinkle.Website.View + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -O2 + build-depends: + Shpadoinkle + , Shpadoinkle-backend-snabbdom + , Shpadoinkle-console + , Shpadoinkle-developer-tools + , Shpadoinkle-html + , Shpadoinkle-isreal + , Shpadoinkle-lens + , Shpadoinkle-router + , Shpadoinkle-template + , Shpadoinkle-widgets + , aeson + , base >=4.12.0 && <4.16 + , bytestring + , exceptions + , file-embed + , generic-lens + , generic-monoid + , ghcjs-dom + , jsaddle + , lens + , mtl + , random + , servant + , text >=1.2.3 && <1.3 + , time + , unliftio + if impl(ghcjs) + build-depends: + servant-client-js + else + build-depends: + servant-client + , servant-server + default-language: Haskell2010 diff --git a/widgets/Shpadoinkle-widgets.cabal b/widgets/Shpadoinkle-widgets.cabal new file mode 100644 index 00000000..1c8a91be --- /dev/null +++ b/widgets/Shpadoinkle-widgets.cabal @@ -0,0 +1,98 @@ +cabal-version: 1.12 + +-- This file has been generated from package.yaml by hpack version 0.33.0. +-- +-- see: https://github.com/sol/hpack +-- +-- hash: e6c6b8bf44e2aa29a7027ab735f1a7536c88828837f7f3af7126653a946758f2 + +name: Shpadoinkle-widgets +version: 0.2.0.0 +synopsis: A collection of common reusable types and components. +description: There are many shared abstractions between various UIs that represent themselves in frameworks and applications. This package provides a useful subset of these concepts as types along with implementations consuming these types. +category: Web +author: Isaac Shapira +maintainer: fresheyeball@protonmail.com +license: BSD3 +license-file: LICENSE +build-type: Simple +extra-source-files: + README.md + CHANGELOG.md + +source-repository head + type: git + location: https://gitlab.com/platonic/shpadoinkle.git + +flag testing + description: Provide test kit around QuickCheck + manual: True + default: False + +library + exposed-modules: + Shpadoinkle.Widgets.Form.Input + Shpadoinkle.Widgets.Form.Dropdown + Shpadoinkle.Widgets.Table + Shpadoinkle.Widgets.Table.Lazy + Shpadoinkle.Widgets.Types + Shpadoinkle.Widgets.Types.Choice + Shpadoinkle.Widgets.Types.ConsideredChoice + Shpadoinkle.Widgets.Types.Core + Shpadoinkle.Widgets.Types.Form + Shpadoinkle.Widgets.Types.Physical + Shpadoinkle.Widgets.Types.Remote + Shpadoinkle.Widgets.Types.Search + Shpadoinkle.Widgets.Validation + other-modules: + Paths_Shpadoinkle_widgets + hs-source-dirs: + ./. + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + build-depends: + Shpadoinkle + , Shpadoinkle-html + , aeson >=1.4.4 && <1.6 + , base >=4.12.0 && <4.16 + , compactable >=0.1.2 && <0.2 + , containers >=0.6.0 && <0.7 + , edit-distance >=0.2.2 && <0.3 + , email-validate >=2.3.2 && <2.4 + , jsaddle >=0.9.7 && <0.20 + , mtl >=2.2.2 && <2.3 + , stm >=2.5.0 && <2.6 + , template-haskell >=2.14.0 && <2.17 + , text >=1.2.3 && <1.3 + , unliftio >=0.2.12 && <0.3 + if flag(testing) + exposed-modules: + Test.QuickCheck.Classes.Hspec + Test.QuickCheck.Classes.FoldableOrd + cpp-options: -DTESTING + build-depends: + QuickCheck + , hspec + , quickcheck-classes + , quickcheck-classes-base + , transformers + else + default-language: Haskell2010 + +test-suite unit + type: exitcode-stdio-1.0 + main-is: Test.hs + other-modules: + Paths_Shpadoinkle_widgets + hs-source-dirs: + tests + ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities + cpp-options: -DTESTING + build-depends: + QuickCheck + , Shpadoinkle-widgets + , base >=4.12.0 && <4.16 + , containers + , hspec + , quickcheck-classes + , quickcheck-classes-base + default-language: Haskell2010 -- GitLab From 6643ebbfbb692f9f86938a025cc154b9a236d323 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 19 Apr 2021 14:46:15 -0600 Subject: [PATCH 048/116] fix servant crud --- examples/Shpadoinkle-examples.cabal | 3 + examples/servant-crud/Server.hs | 18 ++--- examples/servant-crud/Types.hs | 121 ++++++++++++---------------- examples/servant-crud/View.hs | 55 +++++++------ html/Shpadoinkle-html.cabal | 3 + nix/overlay-shpadoinkle.nix | 31 ------- template/Shpadoinkle-template.cabal | 4 + 7 files changed, 99 insertions(+), 136 deletions(-) diff --git a/examples/Shpadoinkle-examples.cabal b/examples/Shpadoinkle-examples.cabal index 512de995..d3140930 100644 --- a/examples/Shpadoinkle-examples.cabal +++ b/examples/Shpadoinkle-examples.cabal @@ -176,6 +176,7 @@ executable servant-crud-client , bytestring , containers , exceptions + , generic-lens , jsaddle , lens , mtl @@ -227,6 +228,7 @@ executable servant-crud-dev , containers , exceptions , file-embed + , generic-lens , generic-monoid , jsaddle , lens @@ -277,6 +279,7 @@ executable servant-crud-server , bytestring , containers , file-embed + , generic-lens , lens , mtl , optparse-applicative diff --git a/examples/servant-crud/Server.hs b/examples/servant-crud/Server.hs index 08dbf58a..3456df0c 100644 --- a/examples/servant-crud/Server.hs +++ b/examples/servant-crud/Server.hs @@ -85,22 +85,22 @@ newtype Noop a = Noop (JSM a) instance CRUDSpaceCraft App where listSpaceCraft = - runSql . runSelectReturningList . select . all_ $ _roster db + runSql . runSelectReturningList . select . all_ $ roster db getSpaceCraft i = runSql . runSelectReturningOne . select - . filter_ (\s -> val_ (SpaceCraftKey i) ==. primaryKey s) . all_ $ _roster db + . filter_ (\s -> val_ (SpaceCraftKey i) ==. primaryKey s) . all_ $ roster db updateSpaceCraft i SpaceCraftUpdate {..} = - runSql . runUpdate $ save (_roster db) $ - SpaceCraft i _sku _description _serial _squadron _operable + runSql . runUpdate $ save (roster db) $ + SpaceCraft i sku description serial squadron operable deleteSpaceCraft i = - runSql . runDelete $ delete (_roster db) $ \sc -> val_ (SpaceCraftKey i) ==. primaryKey sc + runSql . runDelete $ delete (roster db) $ \sc -> val_ (SpaceCraftKey i) ==. primaryKey sc createSpaceCraft SpaceCraftUpdate {..} = - runSql . fmap (_identity . head) . runInsertReturningList . insert (_roster db) $ insertExpressions - [ SpaceCraft default_ (val_ _sku) (val_ _description) (val_ _serial) (val_ _squadron) (val_ _operable) ] + runSql . fmap (identity . head) . runInsertReturningList . insert (roster db) $ insertExpressions + [ SpaceCraft default_ (val_ sku) (val_ description) (val_ serial) (val_ squadron) (val_ operable) ] app :: Env -> Connection -> FilePath -> Application @@ -117,8 +117,8 @@ app ev conn root = serve (Proxy @ (API :<|> SPA App)) $ serveApi :<|> serveSpa serveSpa :: Server (SPA App) serveSpa = serveUI @ (SPA App) root - (\r -> toHandler conn $ do - i <- start r; return . template ev i $ view @ Noop i) routes + (\r -> toHandler conn $ start r >>= \i -> + return . template ev i $ view @ Noop i) routes application :: Env -> FilePath -> IO Application diff --git a/examples/servant-crud/Types.hs b/examples/servant-crud/Types.hs index cb54ad52..622719e6 100644 --- a/examples/servant-crud/Types.hs +++ b/examples/servant-crud/Types.hs @@ -10,6 +10,7 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE InstanceSigs #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuantifiedConstraints #-} {-# LANGUAGE RankNTypes #-} @@ -33,11 +34,11 @@ import Control.Lens as Lens (Identity, makeLenses, makePrisms, view, (^.)) -import Control.Lens.TH () import Control.Monad.Except (MonadError (throwError), MonadTrans (..)) import Data.Aeson (FromJSON, ToJSON) import Data.Function (on) +import Data.Generics.Labels () import Data.Maybe (fromMaybe) import Data.Proxy (Proxy (Proxy)) import Data.Text (Text) @@ -82,12 +83,12 @@ import Types.Prim (Description (..), data SpaceCraftT f = SpaceCraft - { _identity :: Columnar f SpaceCraftId - , _sku :: Columnar f SKU - , _description :: Columnar (Nullable f) Description - , _serial :: Columnar f SerialNumber - , _squadron :: Columnar f Squadron - , _operable :: Columnar f Operable + { identity :: Columnar f SpaceCraftId + , sku :: Columnar f SKU + , description :: Columnar (Nullable f) Description + , serial :: Columnar f SerialNumber + , squadron :: Columnar f Squadron + , operable :: Columnar f Operable } deriving (Generic, Beamable) @@ -96,7 +97,7 @@ instance NFData (SpaceCraftT Identity) instance Table SpaceCraftT where newtype PrimaryKey SpaceCraftT f = SpaceCraftKey (Columnar f SpaceCraftId) deriving (Generic) deriving anyclass (Beamable) - primaryKey = SpaceCraftKey . _identity + primaryKey = SpaceCraftKey . identity type SpaceCraft = SpaceCraftT Identity @@ -107,10 +108,7 @@ deriving instance ToJSON SpaceCraft deriving instance FromJSON SpaceCraft -makeFieldsNoPrefix ''SpaceCraftT - - -newtype DB f = DB { _roster :: f (TableEntity SpaceCraftT) } deriving (Generic) deriving anyclass (Database be) +newtype DB f = DB { roster :: f (TableEntity SpaceCraftT) } deriving (Generic) deriving anyclass (Database be) db :: DatabaseSettings be DB @@ -118,11 +116,11 @@ db = defaultDbSettings data SpaceCraftUpdate s = SpaceCraftUpdate - { _sku :: Field s Text Input SKU - , _description :: Field s Text Input (Maybe Description) - , _serial :: Field s Text Input SerialNumber - , _squadron :: Field s Text (Dropdown 'One) Squadron - , _operable :: Field s Text (Dropdown 'AtleastOne) Operable + { sku :: Field s Text Input SKU + , description :: Field s Text Input (Maybe Description) + , serial :: Field s Text Input SerialNumber + , squadron :: Field s Text (Dropdown 'One) Squadron + , operable :: Field s Text (Dropdown 'AtleastOne) Operable } deriving Generic @@ -151,11 +149,11 @@ deriving instance Show (SpaceCraftUpdate 'Errors) instance Validate SpaceCraftUpdate where rules = SpaceCraftUpdate - { _sku = positive <> nonZero - , _description = nonMEmpty - , _serial = between (30, maxBound) - , _squadron = maybe (throwError "Cannot be empty") pure - , _operable = pure + { sku = positive <> nonZero + , description = nonMEmpty + , serial = between (30, maxBound) + , squadron = maybe (throwError "Cannot be empty") pure + , operable = pure } @@ -163,9 +161,9 @@ makeFieldsNoPrefix ''SpaceCraftUpdate data Roster = Roster - { _sort :: SortCol [SpaceCraft] - , _search :: Input Search - , _table :: [SpaceCraft] + { sort :: SortCol [SpaceCraft] + , search :: Input Search + , table :: [SpaceCraft] } @@ -180,16 +178,13 @@ instance (ToJSON (Table.Column [SpaceCraft])) => ToJSON Roster instance (FromJSON (Table.Column [SpaceCraft])) => FromJSON Roster -makeFieldsNoPrefix ''Roster - - emptyEditForm :: SpaceCraftUpdate 'Edit emptyEditForm = SpaceCraftUpdate - { _sku = Input Clean 0 - , _description = Input Clean Nothing - , _serial = Input Clean 0 - , _squadron = fullOptions - , _operable = fullOptionsMin + { sku = Input Clean 0 + , description = Input Clean Nothing + , serial = Input Clean 0 + , squadron = fullOptions + , operable = fullOptionsMin } @@ -201,20 +196,9 @@ data ViewModel deriving (Eq, Ord, Show, Generic) -data Frontend = Frontend - { route :: Route - , model :: ViewModel - } - - -instance (ToJSON (Column [SpaceCraft])) => ToJSON Frontend -instance (FromJSON (Column [SpaceCraft])) => FromJSON Frontend -instance - ( NFData (Column [SpaceCraft]) - , NFData (SpaceCraftT Identity)) => NFData Frontend - - -makePrisms ''Frontend +instance (ToJSON (Column [SpaceCraft])) => ToJSON ViewModel +instance (FromJSON (Column [SpaceCraft])) => FromJSON ViewModel +instance (NFData (Column [SpaceCraft])) => NFData ViewModel data Route @@ -222,10 +206,7 @@ data Route | RList (Input Search) | RNew | RExisting SpaceCraftId - deriving (Eq, Ord, Show, Generic) - - -makeLenses ''Route + deriving (Eq, Ord, Show, Generic, NFData, ToJSON, FromJSON) type API = "api" :> "space-craft" :> Get '[JSON] [SpaceCraft] @@ -236,9 +217,9 @@ type API = "api" :> "space-craft" :> Get '[JSON] [SpaceCraft] type SPA m = "app" :> "echo" :> QueryParam "echo" Text :> View m Text - :<|> "app" :> "new" :> View m Frontend - :<|> "app" :> "edit" :> Capture "id" SpaceCraftId :> View m Frontend - :<|> "app" :> QueryParam "search" Search :> View m Frontend + :<|> "app" :> "new" :> View m ViewModel + :<|> "app" :> "edit" :> Capture "id" SpaceCraftId :> View m ViewModel + :<|> "app" :> QueryParam "search" Search :> View m ViewModel :<|> Raw @@ -257,9 +238,9 @@ deriving newtype instance FromHttpApiData Search instance Routed (SPA m) Route where redirect = \case REcho t -> Redirect (Proxy @("app" :> "echo" :> QueryParam "echo" Text :> View m Text)) ($ t) - RNew -> Redirect (Proxy @("app" :> "new" :> View m Frontend)) id - RExisting i -> Redirect (Proxy @("app" :> "edit" :> Capture "id" SpaceCraftId :> View m Frontend)) ($ i) - RList s -> Redirect (Proxy @("app" :> QueryParam "search" Search :> View m Frontend)) ($ Just (_value s)) + RNew -> Redirect (Proxy @("app" :> "new" :> View m ViewModel)) id + RExisting i -> Redirect (Proxy @("app" :> "edit" :> Capture "id" SpaceCraftId :> View m ViewModel)) ($ i) + RList s -> Redirect (Proxy @("app" :> QueryParam "search" Search :> View m ViewModel)) ($ Just (_value s)) class CRUDSpaceCraft m where @@ -305,28 +286,28 @@ instance Tabular [SpaceCraft] where toCell :: forall m. Effect [SpaceCraft] m => [SpaceCraft] -> Row [SpaceCraft] -> Column [SpaceCraft] -> [Html m [SpaceCraft]] toCell _ (SpaceCraftRow SpaceCraft {..}) = \case - SKUT -> present _sku - DescriptionT -> present _description - SerialNumberT -> present _serial - SquadronT -> present _squadron - OperableT -> present _operable + SKUT -> present sku + DescriptionT -> present description + SerialNumberT -> present serial + SquadronT -> present squadron + OperableT -> present operable ToolsT -> [ H.div "btn-group" [ H.button [ H.className "btn btn-sm btn-secondary", - H.onClickM_ $ navigate @ (SPA m) (RExisting _identity) ] [ "Edit" ] + H.onClickM_ $ navigate @ (SPA m) (RExisting identity) ] [ "Edit" ] , H.button [ H.className "btn btn-sm btn-secondary", H.onClickM $ do - deleteSpaceCraft _identity - return . Prelude.filter $ \x -> x ^. identity /= _identity ] [ "Delete" ] + deleteSpaceCraft identity + return . Prelude.filter $ \x -> x ^. #identity /= identity ] [ "Delete" ] ] ] sortTable (SortCol c d) = f $ case c of - SKUT -> g sku - DescriptionT -> g description - SerialNumberT -> g serial - SquadronT -> g squadron - OperableT -> g operable + SKUT -> g #sku + DescriptionT -> g #description + SerialNumberT -> g #serial + SquadronT -> g #squadron + OperableT -> g #operable ToolsT -> \_ _ -> EQ where f = case d of ASC -> id; DESC -> flip g l = compare `on` Lens.view l . unRow diff --git a/examples/servant-crud/View.hs b/examples/servant-crud/View.hs index 27d2c227..d774cb8c 100644 --- a/examples/servant-crud/View.hs +++ b/examples/servant-crud/View.hs @@ -8,9 +8,11 @@ {-# LANGUAGE ImpredicativeTypes #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} @@ -23,6 +25,7 @@ module View where import Control.Lens hiding (view) import Control.Lens.Unsound (lensProduct) import Data.Coerce (Coercible) +import Data.Generics.Labels () import Data.Maybe (fromMaybe, isNothing) import Data.String (IsString) import Data.Text as T @@ -56,11 +59,11 @@ default (Text, []) toEditForm :: SpaceCraft -> SpaceCraftUpdate 'Edit toEditForm sc = SpaceCraftUpdate - { _sku = pure $ sc ^. sku - , _description = pure $ sc ^. description - , _serial = pure $ sc ^. serial - , _squadron = (sc ^. squadron) `withOptions'` fullset - , _operable = (sc ^. operable) `withOptions'` fullset + { sku = pure $ sc ^. #sku + , description = pure $ sc ^. #description + , serial = pure $ sc ^. #serial + , squadron = (sc ^. #squadron) `withOptions'` fullset + , operable = (sc ^. #operable) `withOptions'` fullset } @@ -155,11 +158,11 @@ toHtmlName = toLower . replace " " "-" editForm :: forall m. (CRUDSpaceCraft m, MonadJSM m) => Maybe SpaceCraftId -> SpaceCraftUpdate 'Edit -> Html m (SpaceCraftUpdate 'Edit) editForm mid ef = H.div_ - [ intControl @SKU sku "SKU" errs ef - , textControl @Description description "Description" errs ef - , intControl @SerialNumber serial "Serial Number" errs ef - , selectControl @'One @Squadron squadron "Squadron" errs ef - , selectControl @'AtleastOne @Operable operable "Operable" errs ef + [ intControl @SKU #sku "SKU" errs ef + , textControl @Description #description "Description" errs ef + , intControl @SerialNumber #serial "Serial Number" errs ef + , selectControl @'One @Squadron #squadron "Squadron" errs ef + , selectControl @'AtleastOne @Operable #operable "Operable" errs ef , H.div "d-flex flex-row justify-content-end" [ H.button @@ -179,12 +182,12 @@ editForm mid ef = H.div_ ] [ "Save" ] ] - ] where errs = validate ef + ] where errs = validate ef isValid = getValid errs -start :: (Monad m, CRUDSpaceCraft m) => Route -> m (Toggle, Frontend) -start = (,) mempty . \case +start :: (Monad m, CRUDSpaceCraft m) => Route -> m ViewModel +start = \case RList s -> MList . Roster (SortCol SKUT ASC) s <$> listSpaceCraft REcho t -> return $ MEcho t RNew -> return $ MDetail Nothing emptyEditForm @@ -206,35 +209,35 @@ tableCfg = mempty fuzzy :: [SpaceCraft -> Text] fuzzy = flip (^.) <$> - [ sku . to humanize - , description . to humanize - , serial . to humanize - , squadron . to humanize - , operable . to humanize + [ #sku . to humanize + , #description . to humanize + , #serial . to humanize + , #squadron . to humanize + , #operable . to humanize ] -view :: forall m. (MonadJSM m, CRUDSpaceCraft m) => Frontend -> Html m Frontend +view :: forall m. (MonadJSM m, CRUDSpaceCraft m) => ViewModel -> Html m ViewModel view fe = case fe of - MList r -> onSum _MList $ H.div "container-fluid" + MList r -> onSum #_MList $ H.div "container-fluid" [ H.div "row justify-content-between align-items-center" [ H.h2_ [ "Space Craft Roster" ] , H.div [ H.class' "input-group" , H.textProperty "style" ("width:300px" :: Text) ] - [ r <% search $ Input.search [ H.class' "form-control", H.placeholder "Search" ] + [ r <% #search $ Input.search [ H.class' "form-control", H.placeholder "Search" ] , H.div "input-group-append mr-3" [ H.button [ H.onClickM_ $ navigate @(SPA m) RNew, H.class' "btn btn-primary" ] [ "Register" ] ] ] ] - , onRecord (lensProduct table sort) $ Table.viewWith tableCfg - (r ^. table . to (fuzzySearch fuzzy $ r ^. search . value)) - (r ^. sort) + , onRecord (lensProduct #table #sort) $ Table.viewWith tableCfg + (r ^. #table . to (fuzzySearch fuzzy $ r ^. #search . value)) + (r ^. #sort) ] - MDetail sid form -> onSum (_MDetail . _2) $ H.div "row" + MDetail sid form -> onSum (#_MDetail . _2) $ H.div "row" [ H.div "col-sm-8 offset-sm-2" [ H.h2_ [ text $ maybe "Register New Space Craft" (const "Edit Space Craft") sid ] @@ -250,7 +253,7 @@ view fe = case fe of M404 -> text "404" -template :: Env -> Frontend -> Html m a -> Html m a +template :: Env -> ViewModel -> Html m a -> Html m a template ev fe stage = H.html_ [ H.head_ [ H.link' diff --git a/html/Shpadoinkle-html.cabal b/html/Shpadoinkle-html.cabal index c8d1037d..3490f648 100644 --- a/html/Shpadoinkle-html.cabal +++ b/html/Shpadoinkle-html.cabal @@ -33,6 +33,7 @@ library Shpadoinkle.Html.Utils Shpadoinkle.Html.LocalStorage Shpadoinkle.Html.TH + Shpadoinkle.Html.TH.AssetLink Shpadoinkle.Html.TH.CSS Shpadoinkle.WebWorker Shpadoinkle.Keyboard @@ -60,9 +61,11 @@ library , bytestring , compactable , containers >=0.6.0 && <0.7 + , directory , ghcjs-dom , jsaddle >=0.9.7 && <0.20 , lens + , process , raw-strings-qq , stm >=2.5.0 && <2.6 , template-haskell >=2.14.0 && <2.17 diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index 931ec2b8..737c7e56 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -182,7 +182,6 @@ in { Shpadoinkle-backend-pardiff = call "Shpadoinkle-backend-pardiff" ../backends/pardiff; Shpadoinkle-console = call "Shpadoinkle-console" ../console; Shpadoinkle-developer-tools = addDev (call "Shpadoinkle-developer-tools" ../developer-tools); -<<<<<<< HEAD Shpadoinkle-disembodied = call "Shpadoinkle-disembodied" ../disembodied; Shpadoinkle-lens = call "Shpadoinkle-lens" ../lens; Shpadoinkle-website = websiteOverride (call "Shpadoinkle-website" ../website); @@ -194,36 +193,6 @@ in { Shpadoinkle-examples = call "Shpadoinkle-examples" ../examples; Shpadoinkle-isreal = call "Shpadoinkle-isreal" ../isreal; - bitcoin-hash = hsuper.callPackage (import "${bitcoin-hash}/bitcoin-hash/pkg-${if isJS then "ghcjs" else "ghc"}.nix") {}; -||||||| 8e0efbb - Shpadoinkle-disembodied = call "Shpadoinkle-disembodied" ../disembodied; - Shpadoinkle-lens = call "Shpadoinkle-lens" ../lens; - Shpadoinkle-marketing = call "Shpadoinkle-marketing" ../marketing; - Shpadoinkle-html = call "Shpadoinkle-html" ../html; - Shpadoinkle-router = call "Shpadoinkle-router" ../router; - Shpadoinkle-streaming = call "Shpadoinkle-streaming" ../streaming; - Shpadoinkle-widgets = addTest (call "Shpadoinkle-widgets" ../widgets) hpkgs; - Shpadoinkle-template = call "Shpadoinkle-template" ../template; - - Shpadoinkle-tests = super.haskell.packages.${compiler}.callCabal2nix "tests" (gitignore ../tests) {}; - Shpadoinkle-examples = call "Shpadoinkle-examples" ../examples; - - Shpadoinkle-isreal = call "Shpadoinkle-isreal" ../isreal; - -======= - Shpadoinkle-disembodied = call "Shpadoinkle-disembodied" ../disembodied; - Shpadoinkle-lens = call "Shpadoinkle-lens" ../lens; - Shpadoinkle-html = call "Shpadoinkle-html" ../html; - Shpadoinkle-router = call "Shpadoinkle-router" ../router; - Shpadoinkle-streaming = call "Shpadoinkle-streaming" ../streaming; - Shpadoinkle-widgets = addTest (call "Shpadoinkle-widgets" ../widgets) hpkgs; - Shpadoinkle-template = call "Shpadoinkle-template" ../template; - - Shpadoinkle-examples = call "Shpadoinkle-examples" ../examples; - - Shpadoinkle-isreal = call "Shpadoinkle-isreal" ../isreal; - ->>>>>>> be72e7bd2c01cf2969c4b31e3124cf292e0bad3d ease = hself.callCabal2nix "ease" ease {}; ghcjs-base-stub = hself.callCabal2nix "ghcjs-base-stub" ghcjs-base-stub-src {}; hpack = if isJS then super.haskell.packages.${compiler}.hpack else hsuper.hpack; diff --git a/template/Shpadoinkle-template.cabal b/template/Shpadoinkle-template.cabal index b7868b48..6b613247 100644 --- a/template/Shpadoinkle-template.cabal +++ b/template/Shpadoinkle-template.cabal @@ -45,6 +45,8 @@ library Shpadoinkle , Shpadoinkle-backend-static , base >=4.12.0 && <4.16 + , directory + , process , html-parse , template-haskell , text >=1.2.3 && <1.3 @@ -70,6 +72,8 @@ test-suite sample , Shpadoinkle-backend-static , Shpadoinkle-template , base >=4.12.0 && <4.16 + , directory + , process , file-embed , html-parse , template-haskell -- GitLab From c1d8fd350b599560a97b71525050cf24c750099d Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 19 Apr 2021 14:50:43 -0600 Subject: [PATCH 049/116] hlint --- examples/servant-crud/View.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/servant-crud/View.hs b/examples/servant-crud/View.hs index d774cb8c..cefa0166 100644 --- a/examples/servant-crud/View.hs +++ b/examples/servant-crud/View.hs @@ -12,7 +12,6 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} -- GitLab From 655504499b6c6712d8aa758dc4a205f08e62d59a Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 19 Apr 2021 14:51:28 -0600 Subject: [PATCH 050/116] more lints --- template/Test.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/template/Test.hs b/template/Test.hs index 1300c0a3..498681dd 100644 --- a/template/Test.hs +++ b/template/Test.hs @@ -21,7 +21,7 @@ testHtmlIngestion = in if x == y then pure () else do print x print y - error $ "test.html did not parse correctly. Got: " + error "test.html did not parse correctly. Got: " testTemplate :: IO () @@ -34,7 +34,7 @@ testTemplate = in if x == y then pure () else do print x print y - error $ "template did not interpolate." + error "template did not interpolate." main :: IO () -- GitLab From 2a2bfdc6233d619d097a4c16a2a24f020ae66305 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 19 Apr 2021 15:57:14 -0600 Subject: [PATCH 051/116] include dedupe properly --- developer-tools/Shpadoinkle-developer-tools.cabal | 1 - nix/overlay-shpadoinkle.nix | 5 ++--- snowman/template/snowman.cabal | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/developer-tools/Shpadoinkle-developer-tools.cabal b/developer-tools/Shpadoinkle-developer-tools.cabal index 3fc55e6a..f341496d 100644 --- a/developer-tools/Shpadoinkle-developer-tools.cabal +++ b/developer-tools/Shpadoinkle-developer-tools.cabal @@ -85,7 +85,6 @@ executable devtools -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities - -dedupe -O2 build-depends: diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index 737c7e56..7f06651f 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -109,9 +109,8 @@ addFlags = x: with super.haskell.lib; overrideCabal - (appendConfigureFlags (appendBuildFlags x (if isJS then - [ "--ghcjs-option=-dedupe" ] else [])) - [ "--ghc-option=-Werror" ]) + (appendConfigureFlags x + [ "--ghc-option=-Werror" "--ghcjs-options=-dedupe" ]) (drv: { haddockFlags = ["--css=${../etc/linuwial.css}"]; }); diff --git a/snowman/template/snowman.cabal b/snowman/template/snowman.cabal index 38a7c682..1087ad6f 100644 --- a/snowman/template/snowman.cabal +++ b/snowman/template/snowman.cabal @@ -33,7 +33,6 @@ executable snowman -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities - -dedupe -O2 -- GitLab From 0d184a6b456c8d7e93cfb2e51e669f8713ede959 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 19 Apr 2021 16:12:50 -0600 Subject: [PATCH 052/116] turn on advanced, and dedupe --- nix/overlay-shpadoinkle.nix | 8 ++++++-- nix/util.nix | 1 + snowman/template/default.nix | 8 +++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index 7f06651f..20d6779f 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -109,8 +109,12 @@ addFlags = x: with super.haskell.lib; overrideCabal - (appendConfigureFlags x - [ "--ghc-option=-Werror" "--ghcjs-options=-dedupe" ]) + (appendConfigureFlags x [ + "--ghc-option=-Werror" + "--ghcjs-options=-dedupe" + "--ghcjs-options=-DGHCJS_GC_INTERVAL=60000" + "--ghcjs-options=-O2" + ]) (drv: { haddockFlags = ["--css=${../etc/linuwial.css}"]; }); diff --git a/nix/util.nix b/nix/util.nix index fa0f5d4e..19d7994c 100644 --- a/nix/util.nix +++ b/nix/util.nix @@ -27,6 +27,7 @@ rec --create_source_map $output.map \ --source_map_format=V3 \ --source_map_include_content \ + --compilation_level ADVANCED_OPTIMIZATIONS \ --warning_level=QUIET \ $input ''; diff --git a/snowman/template/default.nix b/snowman/template/default.nix index ded71d24..cdf46b6b 100644 --- a/snowman/template/default.nix +++ b/snowman/template/default.nix @@ -89,7 +89,13 @@ let # We can name him George - snowman = pkgs.haskell.packages.${compilerjs}.callCabal2nix "snowman" (gitignore ignorance ./.) {}; + snowman = pkgs.haskell.lib.appendConfigureFlags + (pkgs.haskell.packages.${compilerjs}.callCabal2nix "snowman" (gitignore ignorance ./.) {}) + (if optimize then [ + "--ghcjs-options=-dedupe" + "--ghcjs-options=-DGHCJS_GC_INTERVAL=60000" + "--ghcjs-options=-O2" + ] else []); in with pkgs; with lib; -- GitLab From c137eb58c70efee1b98e6250e1ed69f0d3fd2c66 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 19 Apr 2021 16:34:44 -0600 Subject: [PATCH 053/116] add back titles to docs --- website/Shpadoinkle/Website/Page/Documentation.hs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/website/Shpadoinkle/Website/Page/Documentation.hs b/website/Shpadoinkle/Website/Page/Documentation.hs index 6119585d..76e086b1 100644 --- a/website/Shpadoinkle/Website/Page/Documentation.hs +++ b/website/Shpadoinkle/Website/Page/Documentation.hs @@ -26,11 +26,11 @@ import Shpadoinkle.Widgets.Types (Humanize (..)) concepts :: MonadJSM m => Html m a -concepts = docsWrap $(embedAsciidoc "./docs/concepts.adoc") +concepts = docsWrap ("Concepts" :: Text) $(embedAsciidoc "./docs/concepts.adoc") gettingStarted :: MonadJSM m => GettingStarted.Route -> Html m a -gettingStarted = docsWrap . \case +gettingStarted r = docsWrap r $ case r of GettingStarted.RGSIndex -> $(embedAsciidoc "./docs/getting-started/index.adoc") GettingStarted.RGSAddingToYourProject -> @@ -40,7 +40,7 @@ gettingStarted = docsWrap . \case packages :: MonadJSM m => Packages.Route -> Html m a -packages = docsWrap . \case +packages r = docsWrap r $ case r of Packages.RPIndex -> $(embedAsciidoc "./docs/packages/index.adoc") Packages.RPCore -> $(embedAsciidoc "./docs/packages/core.adoc") Packages.RPConsole -> $(embedAsciidoc "./docs/packages/console.adoc") @@ -52,7 +52,7 @@ packages = docsWrap . \case tutorial :: MonadJSM m => Tutorial.Route -> Html m a -tutorial = docsWrap . \case +tutorial r = docsWrap r $ case r of Tutorial.RTIndex -> $(embedAsciidoc "./docs/tutorial/index.adoc") Tutorial.RTCalculator -> $(embedAsciidoc "./docs/tutorial/calculator.adoc") Tutorial.RTImmediateExecution -> $(embedAsciidoc "./docs/tutorial/immediate-execution.adoc") @@ -85,12 +85,12 @@ sideNavLink toR r = li_ ] -docsWrap :: MonadJSM m => [Html m a] -> Html m a -docsWrap content' = section [class' flex ] +docsWrap :: (MonadJSM m, Humanize r) => r -> [Html m a] -> Html m a +docsWrap r content' = section [class' flex ] [ nav [ class' side_nav ] [ expandable RGettingStarted "Getting Started" , expandable RTutorial "Tutorial" , expandable RPackages "Reference" ] - , h "main" [ class' docs ] content' + , h "main" [ class' docs ] $ h1_ [ text $ humanize r ] : content' ] -- GitLab From 36ace4b8fec0dbfbd4d173cfa7f0194b3f83ae56 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 19 Apr 2021 17:18:25 -0600 Subject: [PATCH 054/116] don't accidentlly strip tags from asciidoctor --- template/Shpadoinkle/Template/TH.hs | 4 +++- website/Shpadoinkle/Website/Partials/Template.hs | 2 +- website/Shpadoinkle/Website/Types/Routes/GettingStarted.hs | 2 +- website/Shpadoinkle/Website/Types/Routes/Packages.hs | 2 +- website/Shpadoinkle/Website/Types/Routes/Tutorial.hs | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/template/Shpadoinkle/Template/TH.hs b/template/Shpadoinkle/Template/TH.hs index 2d8d10e5..12676076 100644 --- a/template/Shpadoinkle/Template/TH.hs +++ b/template/Shpadoinkle/Template/TH.hs @@ -78,7 +78,9 @@ tokenToExp = attrToExp :: Attr -> Exp attrToExp (Attr name value) = TupE [name', AppE textProp value'] where textProp = UnboundVarE $ mkName "textProp" - name' = asText name + name' = asText $ case name of + "class" -> "className" + _ -> name value' = asText value diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index c54a34fe..ab5ba5c6 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -31,7 +31,7 @@ codemirrorCDN = mappend "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.59. headView :: Env -> Frontend -> Html m a headView ev fe = head_ [ meta' [ charset "UTF-8" ] - , link' [ href "favicon.svg", rel "icon", type' "image/svg+xml" ] + -- , link' [ href $(assetLink "/assets/favicon.svg"), rel "icon", type' "image/svg+xml" ] , meta' [ content "width=device-width, initial-scale=1.0", name' "viewport" ] , link' [ rel "stylesheet" diff --git a/website/Shpadoinkle/Website/Types/Routes/GettingStarted.hs b/website/Shpadoinkle/Website/Types/Routes/GettingStarted.hs index 4c0c566a..8c41fa2f 100644 --- a/website/Shpadoinkle/Website/Types/Routes/GettingStarted.hs +++ b/website/Shpadoinkle/Website/Types/Routes/GettingStarted.hs @@ -40,6 +40,6 @@ routes instance Humanize Route where humanize = \case - RGSIndex -> "" + RGSIndex -> "Getting Started" RGSAddingToYourProject -> "Adding to your project" RGSExtendAnExample -> "Extend an example" diff --git a/website/Shpadoinkle/Website/Types/Routes/Packages.hs b/website/Shpadoinkle/Website/Types/Routes/Packages.hs index 05044a7d..b7222d04 100644 --- a/website/Shpadoinkle/Website/Types/Routes/Packages.hs +++ b/website/Shpadoinkle/Website/Types/Routes/Packages.hs @@ -54,7 +54,7 @@ routes instance Humanize Route where humanize = \case - RPIndex -> "" + RPIndex -> "Reference" RPCore -> "Core" RPConsole -> "Console" RPBackends -> "Backends" diff --git a/website/Shpadoinkle/Website/Types/Routes/Tutorial.hs b/website/Shpadoinkle/Website/Types/Routes/Tutorial.hs index 0696eec7..e41e5702 100644 --- a/website/Shpadoinkle/Website/Types/Routes/Tutorial.hs +++ b/website/Shpadoinkle/Website/Types/Routes/Tutorial.hs @@ -42,7 +42,7 @@ routes instance Humanize Route where humanize = \case - RTIndex -> "" + RTIndex -> "Tutorial" RTCalculator -> "Calculator" RTImmediateExecution -> "Immediate Execution" RTComposing -> "Composing" -- GitLab From 4aaf038e8cae3ff33ab64e50b1835e8c67d75ce0 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 19 Apr 2021 17:27:49 -0600 Subject: [PATCH 055/116] keybase --- nix/build-pages.sh | 2 +- website/keybase.txt | 56 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 website/keybase.txt diff --git a/nix/build-pages.sh b/nix/build-pages.sh index 12ddf077..c136daa1 100755 --- a/nix/build-pages.sh +++ b/nix/build-pages.sh @@ -13,7 +13,7 @@ echo "Building Haddocks and copying in" rm result* echo "Proving to Keybase" -cp docs/keybase.txt public/keybase.txt +cp website/keybase.txt public/keybase.txt echo "Building Website" nix-build website --fallback diff --git a/website/keybase.txt b/website/keybase.txt new file mode 100644 index 00000000..c23b7da0 --- /dev/null +++ b/website/keybase.txt @@ -0,0 +1,56 @@ +================================================================== +https://keybase.io/fresheyeball +-------------------------------------------------------------------- + +I hereby claim: + + * I am an admin of https://Shpadoinkle.org + * I am fresheyeball (https://keybase.io/fresheyeball) on keybase. + * I have a public key ASAW_kJQ9MCVx8q5IgY7_oKz8GSy8M9M5M9J3-RsGZIv3wo + +To do so, I am signing this object: + +{ + "body": { + "key": { + "eldest_kid": "0120fd5f1b1e1b7d8163fe90b4c426b1f7dff5c9878b1a35b29bf5ccb6d5071d7e2b0a", + "host": "keybase.io", + "kid": "012016fe4250f4c095c7cab922063bfe82b3f064b2f0cf4ce4cf49dfe46c19922fdf0a", + "uid": "0d25a821296d8aa10b2cb6e7e4ae2119", + "username": "fresheyeball" + }, + "merkle_root": { + "ctime": 1608662200, + "hash": "a666500fc01da0b494570a409ed1769a644356ea2374091c8e85397f9a2a9264201fc1fda1ff58fefe4865db2e2b919501e485362d7894aec1bc245150697f2e", + "hash_meta": "fd81b897c1bb0f717ad189419ecd7fb36c75a267214658180f92d0656703ccb6", + "seqno": 18478745 + }, + "service": { + "entropy": "gYF78QZ9uOBHqTVnQG9iVYQa", + "hostname": "Shpadoinkle.org", + "protocol": "https:" + }, + "type": "web_service_binding", + "version": 2 + }, + "client": { + "name": "keybase.io go client", + "version": "5.5.0" + }, + "ctime": 1608662236, + "expire_in": 504576000, + "prev": "3f992af92bb272af327eb0c6073215fff99769b586349bd4b1e1225a94d1f240", + "seqno": 85, + "tag": "signature" +} + +which yields the signature: + +hKRib2R5hqhkZXRhY2hlZMOpaGFzaF90eXBlCqNrZXnEIwEgFv5CUPTAlcfKuSIGO/6Cs/BksvDPTOTPSd/kbBmSL98Kp3BheWxvYWTESpcCVcQgP5kq+Suycq8yfrDGBzIV//mXabWGNJvUseEiWpTR8kDEIESkpF0/VJ8NRvYmPapgDKgMe0nb7KE+57mvfwefMmT1AgHCo3NpZ8RAIUlGGlR6ml7TB8lEsC0tR3bZBaXSsD+FuW04Yee6CV+iMeuYVJm6DGW2h3z9YekCR9sJ1FbaT9tWrZhVOyPyCahzaWdfdHlwZSCkaGFzaIKkdHlwZQildmFsdWXEIB9v/kUGkEacZ6ELj2ONn5hxdX2DzhY/U1ZHf0pfI7E4o3RhZ80CAqd2ZXJzaW9uAQ== + +And finally, I am proving ownership of this host by posting or +appending to this document. + +View my publicly-auditable identity here: https://keybase.io/fresheyeball + +================================================================== -- GitLab From a4ffe33e64cbaa564a3dc04775eb74e8fe1c72a4 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 19 Apr 2021 17:46:20 -0600 Subject: [PATCH 056/116] fix licsense --- isreal/Shpadoinkle-isreal.cabal | 6 +- isreal/swan/swan.cabal | 9 +- website/CHANGELOG.md | 0 website/Shpadoinkle-website.cabal | 178 +++++++++++++++++------------- website/assets/logo.png | Bin 55356 -> 0 bytes website/package.yaml | 148 ------------------------- 6 files changed, 111 insertions(+), 230 deletions(-) delete mode 100644 website/CHANGELOG.md delete mode 100644 website/assets/logo.png delete mode 100644 website/package.yaml diff --git a/isreal/Shpadoinkle-isreal.cabal b/isreal/Shpadoinkle-isreal.cabal index 9023aa91..b8331388 100644 --- a/isreal/Shpadoinkle-isreal.cabal +++ b/isreal/Shpadoinkle-isreal.cabal @@ -4,7 +4,7 @@ version: 0.0.0.1 category: Web author: Isaac Shapira maintainer: isaac.shapira@platonic.systems -license: BSD-3-Clause +license: GPL-3.0-or-later license-file: LICENSE build-type: Simple extra-source-files: @@ -36,7 +36,7 @@ common ghc-options library - imports: ghc-options + import: ghc-options exposed-modules: Shpadoinkle.Isreal.Types @@ -62,7 +62,7 @@ library executable isreal - imports: ghc-options + import: ghc-options main-is: Main.hs diff --git a/isreal/swan/swan.cabal b/isreal/swan/swan.cabal index 31dc1fbc..4d5a7fb9 100644 --- a/isreal/swan/swan.cabal +++ b/isreal/swan/swan.cabal @@ -1,7 +1,8 @@ -cabal-version: >=1.10 -name: swan -version: 0.1.0.0 -build-type: Simple +cabal-version: 2.2 +name: swan +version: 0.1.0.0 +build-type: Simple +license: GPL-3.0-or-later executable swan main-is: Main.hs diff --git a/website/CHANGELOG.md b/website/CHANGELOG.md deleted file mode 100644 index e69de29b..00000000 diff --git a/website/Shpadoinkle-website.cabal b/website/Shpadoinkle-website.cabal index 194b1e07..cb03fae1 100644 --- a/website/Shpadoinkle-website.cabal +++ b/website/Shpadoinkle-website.cabal @@ -1,55 +1,76 @@ -cabal-version: 1.12 - --- This file has been generated from package.yaml by hpack version 0.33.0. --- --- see: https://github.com/sol/hpack --- --- hash: 2ae0448f068492147c5322ad2ee5c0ebb17bb81f05e5c22bea7872d3c2267f2e - -name: Shpadoinkle-website -version: 0.0.0.1 -synopsis: Shpadoinkle website -description: Website for Shpadoinkle, includeing splash pages, sandboxes, and documentation. -category: Web -author: Isaac Shapira -maintainer: fresheyeball@protonmail.com -license: GPL-3 -license-file: LICENSE -build-type: Simple +cabal-version: 2.2 +name: Shpadoinkle-website +version: 0.0.0.1 +category: Web +author: Isaac Shapira +maintainer: fresheyeball@protonmail.com +license: GPL-3.0-or-later +license-file: LICENSE +build-type: Simple extra-source-files: - README.md - CHANGELOG.md + README.md +synopsis: + Shpadoinkle website +description: + Website for Shpadoinkle, includeing splash pages, sandboxes, and documentation. + + +common ghc-options + ghc-options: + -Wall + -Wcompat + -fwarn-redundant-constraints + -fwarn-incomplete-uni-patterns + -fwarn-tabs + -fwarn-incomplete-record-updates + -fwarn-identities + + +common ghcjs-options + ghcjs-options: + -Wall + -Wcompat + -fno-warn-missing-home-modules + -fwarn-redundant-constraints + -fwarn-incomplete-uni-patterns + -fwarn-tabs + -fwarn-incomplete-record-updates + -fwarn-identities + -O2 + executable disembodied + import: ghc-options, ghcjs-options + main-is: Shpadoinkle/Website/Disembodied.hs + other-modules: - Shpadoinkle.Website.Style - Shpadoinkle.Website.Page.Home - Shpadoinkle.Website.Page.Documentation - Shpadoinkle.Website.Partials.Template - Shpadoinkle.Website.Types.Effects.Example - Shpadoinkle.Website.Types.Effects.Hooglable - Shpadoinkle.Website.Types.Effects.Swan - Shpadoinkle.Website.Types.Frontend - Shpadoinkle.Website.Types.CurrentYear - Shpadoinkle.Website.Types.Home - Shpadoinkle.Website.Types.Routes - Shpadoinkle.Website.View - Shpadoinkle.Website.Component.LiveExample - Shpadoinkle.Website.Partials.Footer - Shpadoinkle.Website.Partials.Nav - Shpadoinkle.Website.Types.Example - Shpadoinkle.Website.Types.ExampleState - Shpadoinkle.Website.Types.Hoogle - Shpadoinkle.Website.Types.Hoogle.Target - Shpadoinkle.Website.Types.Nav - Shpadoinkle.Website.Types.Routes.GettingStarted - Shpadoinkle.Website.Types.Routes.Packages - Shpadoinkle.Website.Types.Routes.Tutorial - hs-source-dirs: - ./. - ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities - ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -O2 + Shpadoinkle.Website.Style + Shpadoinkle.Website.Page.Home + Shpadoinkle.Website.Page.Documentation + Shpadoinkle.Website.Partials.Template + Shpadoinkle.Website.Types.Effects.Example + Shpadoinkle.Website.Types.Effects.Hooglable + Shpadoinkle.Website.Types.Effects.Swan + Shpadoinkle.Website.Types.Frontend + Shpadoinkle.Website.Types.CurrentYear + Shpadoinkle.Website.Types.Home + Shpadoinkle.Website.Types.Routes + Shpadoinkle.Website.View + Shpadoinkle.Website.Component.LiveExample + Shpadoinkle.Website.Partials.Footer + Shpadoinkle.Website.Partials.Nav + Shpadoinkle.Website.Types.Example + Shpadoinkle.Website.Types.ExampleState + Shpadoinkle.Website.Types.Hoogle + Shpadoinkle.Website.Types.Hoogle.Target + Shpadoinkle.Website.Types.Nav + Shpadoinkle.Website.Types.Routes.GettingStarted + Shpadoinkle.Website.Types.Routes.Packages + Shpadoinkle.Website.Types.Routes.Tutorial + + hs-source-dirs: ./. + build-depends: Shpadoinkle , Shpadoinkle-console @@ -77,43 +98,48 @@ executable disembodied , text >=1.2.3 && <1.3 , time , unliftio + if impl(ghcjs) buildable: False else buildable: True + default-language: Haskell2010 + executable run + import: ghc-options, ghcjs-options + main-is: Shpadoinkle/Website/Run.hs + other-modules: - Shpadoinkle.Website.Component.LiveExample - Shpadoinkle.Website.Partials.Template - Shpadoinkle.Website.Partials.Nav - Shpadoinkle.Website.Partials.Footer - Shpadoinkle.Website.Page.Home - Shpadoinkle.Website.Page.Documentation - Shpadoinkle.Website.Types.CurrentYear - Shpadoinkle.Website.Types.Nav - Shpadoinkle.Website.Types.Home - Shpadoinkle.Website.Types.Hoogle - Shpadoinkle.Website.Types.Hoogle.API - Shpadoinkle.Website.Types.Hoogle.Target - Shpadoinkle.Website.Types.Example - Shpadoinkle.Website.Types.Frontend - Shpadoinkle.Website.Types.ExampleState - Shpadoinkle.Website.Types.Effects.Example - Shpadoinkle.Website.Types.Effects.Hooglable - Shpadoinkle.Website.Types.Effects.Swan - Shpadoinkle.Website.Types.Routes.GettingStarted - Shpadoinkle.Website.Types.Routes.Tutorial - Shpadoinkle.Website.Types.Routes.Packages - Shpadoinkle.Website.Types.Routes - Shpadoinkle.Website.Style - Shpadoinkle.Website.View - hs-source-dirs: - ./. - ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities - ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -O2 + Shpadoinkle.Website.Component.LiveExample + Shpadoinkle.Website.Partials.Template + Shpadoinkle.Website.Partials.Nav + Shpadoinkle.Website.Partials.Footer + Shpadoinkle.Website.Page.Home + Shpadoinkle.Website.Page.Documentation + Shpadoinkle.Website.Types.CurrentYear + Shpadoinkle.Website.Types.Nav + Shpadoinkle.Website.Types.Home + Shpadoinkle.Website.Types.Hoogle + Shpadoinkle.Website.Types.Hoogle.API + Shpadoinkle.Website.Types.Hoogle.Target + Shpadoinkle.Website.Types.Example + Shpadoinkle.Website.Types.Frontend + Shpadoinkle.Website.Types.ExampleState + Shpadoinkle.Website.Types.Effects.Example + Shpadoinkle.Website.Types.Effects.Hooglable + Shpadoinkle.Website.Types.Effects.Swan + Shpadoinkle.Website.Types.Routes.GettingStarted + Shpadoinkle.Website.Types.Routes.Tutorial + Shpadoinkle.Website.Types.Routes.Packages + Shpadoinkle.Website.Types.Routes + Shpadoinkle.Website.Style + Shpadoinkle.Website.View + + hs-source-dirs: ./. + build-depends: Shpadoinkle , Shpadoinkle-backend-snabbdom @@ -141,6 +167,7 @@ executable run , text >=1.2.3 && <1.3 , time , unliftio + if impl(ghcjs) build-depends: servant-client-js @@ -148,4 +175,5 @@ executable run build-depends: servant-client , servant-server + default-language: Haskell2010 diff --git a/website/assets/logo.png b/website/assets/logo.png deleted file mode 100644 index fbbcfa2f85170b4f2237177b2084ce7834424f79..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55356 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 4.12.0 && < 4.16 - - text >= 1.2.3 && < 1.3 - - aeson - - bytestring - - time - - random - - servant - - exceptions - - unliftio - - lens - - generic-lens - - generic-monoid - - mtl - - file-embed - - ghcjs-dom - - jsaddle - - - Shpadoinkle - - Shpadoinkle-router - - Shpadoinkle-html - - Shpadoinkle-isreal - - Shpadoinkle-lens - - Shpadoinkle-widgets - - Shpadoinkle-console - - Shpadoinkle-template - - -executables: - run: - main: Shpadoinkle/Website/Run.hs - other-modules: - - Shpadoinkle.Website.Component.LiveExample - - Shpadoinkle.Website.Partials.Template - - Shpadoinkle.Website.Partials.Nav - - Shpadoinkle.Website.Partials.Footer - - Shpadoinkle.Website.Page.Home - - Shpadoinkle.Website.Page.Documentation - - Shpadoinkle.Website.Types.CurrentYear - - Shpadoinkle.Website.Types.Nav - - Shpadoinkle.Website.Types.Home - - Shpadoinkle.Website.Types.Hoogle - - Shpadoinkle.Website.Types.Hoogle.API - - Shpadoinkle.Website.Types.Hoogle.Target - - Shpadoinkle.Website.Types.Example - - Shpadoinkle.Website.Types.Frontend - - Shpadoinkle.Website.Types.ExampleState - - Shpadoinkle.Website.Types.Effects.Example - - Shpadoinkle.Website.Types.Effects.Hooglable - - Shpadoinkle.Website.Types.Effects.Swan - - Shpadoinkle.Website.Types.Routes.GettingStarted - - Shpadoinkle.Website.Types.Routes.Tutorial - - Shpadoinkle.Website.Types.Routes.Packages - - Shpadoinkle.Website.Types.Routes - - Shpadoinkle.Website.Style - - Shpadoinkle.Website.View - source-dirs: . - dependencies: - - Shpadoinkle-backend-snabbdom - - Shpadoinkle-developer-tools - - when: - - condition: impl(ghcjs) - then: - dependencies: - - servant-client-js - else: - dependencies: - - servant-client - - servant-server - - disembodied: - when: - - condition: impl(ghcjs) - then: - buildable: False - else: - buildable: True - main: Shpadoinkle/Website/Disembodied.hs - other-modules: - - Shpadoinkle.Website.Style - - Shpadoinkle.Website.Page.Home - - Shpadoinkle.Website.Page.Documentation - - Shpadoinkle.Website.Partials.Template - - Shpadoinkle.Website.Types.Effects.Example - - Shpadoinkle.Website.Types.Effects.Hooglable - - Shpadoinkle.Website.Types.Effects.Swan - - Shpadoinkle.Website.Types.Frontend - - Shpadoinkle.Website.Types.CurrentYear - - Shpadoinkle.Website.Types.Home - - Shpadoinkle.Website.Types.Routes - - Shpadoinkle.Website.View - - Shpadoinkle.Website.Component.LiveExample - - Shpadoinkle.Website.Partials.Footer - - Shpadoinkle.Website.Partials.Nav - - Shpadoinkle.Website.Types.Example - - Shpadoinkle.Website.Types.ExampleState - - Shpadoinkle.Website.Types.Hoogle - - Shpadoinkle.Website.Types.Hoogle.Target - - Shpadoinkle.Website.Types.Nav - - Shpadoinkle.Website.Types.Routes.GettingStarted - - Shpadoinkle.Website.Types.Routes.Packages - - Shpadoinkle.Website.Types.Routes.Tutorial - source-dirs: . - dependencies: - - Shpadoinkle-disembodied - - servant-server - -- GitLab From f928cdc8a143ae50966b160a40ed90ade23e9f50 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 19 Apr 2021 20:29:28 -0600 Subject: [PATCH 057/116] fix html entities in asciidoc template parsing --- template/Shpadoinkle-template.cabal | 2 ++ template/Shpadoinkle/Template/TH.hs | 46 ++++++++++++++++++++++++----- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/template/Shpadoinkle-template.cabal b/template/Shpadoinkle-template.cabal index 6b613247..de7f1ada 100644 --- a/template/Shpadoinkle-template.cabal +++ b/template/Shpadoinkle-template.cabal @@ -50,6 +50,7 @@ library , html-parse , template-haskell , text >=1.2.3 && <1.3 + , html-entities default-language: Haskell2010 @@ -78,5 +79,6 @@ test-suite sample , html-parse , template-haskell , text >=1.2.3 && <1.3 + , html-entities default-language: Haskell2010 diff --git a/template/Shpadoinkle/Template/TH.hs b/template/Shpadoinkle/Template/TH.hs index 12676076..3c4c7fea 100644 --- a/template/Shpadoinkle/Template/TH.hs +++ b/template/Shpadoinkle/Template/TH.hs @@ -6,10 +6,15 @@ module Shpadoinkle.Template.TH where import Control.Monad (unless, when) -import Data.Text (Text, cons, pack, replace, unpack) +import Data.Maybe (fromMaybe) +import Data.Text (Text, breakOn, cons, head, null, + pack, replace, tail, uncons, + unpack) import Data.Text.IO +import Debug.Trace (trace) +import HTMLEntities.Decoder (htmlEntityBody) import Language.Haskell.TH.Syntax -import Prelude hiding (readFile) +import Prelude hiding (head, null, readFile, tail) import System.Directory (doesFileExist, removeFile) import System.Exit (ExitCode (..)) import System.Process (proc, @@ -77,12 +82,37 @@ tokenToExp = attrToExp :: Attr -> Exp attrToExp (Attr name value) = TupE [name', AppE textProp value'] - where textProp = UnboundVarE $ mkName "textProp" - name' = asText $ case name of - "class" -> "className" - _ -> name - value' = asText value + where + textProp = UnboundVarE $ mkName "textProp" + name' = asText $ case name of + "class" -> "className" + _ -> name + value' = asText value asText :: Text -> Exp -asText = AppE (UnboundVarE $ mkName "pack") . LitE . StringL . unpack +asText = AppE (UnboundVarE $ mkName "pack") . LitE . StringL . unpack . decodeHtml + + +decodeHtml :: Text -> Text +decodeHtml s = case uncons s of + Nothing -> "" + Just ('&', xs) -> fromMaybe ('&' `cons` decodeHtml xs) $ do + (before, after) <- breakCharMaybe ';' xs + c <- hush $ htmlEntityBody before + return $ c <> decodeHtml after + Just (x, xs) -> x `cons` decodeHtml xs + + +hush :: Either a b -> Maybe b +hush (Left _) = Nothing +hush (Right x) = Just x + + +breakCharMaybe :: Char -> Text -> Maybe (Text, Text) +breakCharMaybe c s + | null s = Nothing + | c == head s = Just ("", tail s) + | otherwise = do + (next, rest) <- breakCharMaybe c (tail s) + Just (cons (head s) next, rest) -- GitLab From 6953f86866e9dc33c4eb6ad3268ea519ca14ac55 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Tue, 20 Apr 2021 12:06:20 -0600 Subject: [PATCH 058/116] ok now, no --- website/assets/landing_logo.svg | 5 +- website/brand.nix | 19 ++++++ website/colors.nix | 5 ++ website/logo.nix | 105 ++++++++++++++++++++++++++++++++ website/thumbnail.nix | 85 ++++++++++++++++++++++++++ 5 files changed, 215 insertions(+), 4 deletions(-) create mode 100644 website/brand.nix create mode 100644 website/colors.nix create mode 100644 website/logo.nix create mode 100644 website/thumbnail.nix diff --git a/website/assets/landing_logo.svg b/website/assets/landing_logo.svg index 0e466088..72dbd5f9 100644 --- a/website/assets/landing_logo.svg +++ b/website/assets/landing_logo.svg @@ -1,8 +1,5 @@ - - Group 18 - Created with Sketch. @@ -103,4 +100,4 @@ - \ No newline at end of file + diff --git a/website/brand.nix b/website/brand.nix new file mode 100644 index 00000000..4415d59d --- /dev/null +++ b/website/brand.nix @@ -0,0 +1,19 @@ +{ chan ? (import ../nix/chan.nix) +}: +let + pkgs = import ../nix/pkgs.nix { inherit chan; }; + mkLogo = import ./logo.nix; + mkThumbnail = import ./thumbnail.nix; + colors = import ./colors.nix; +in import (pkgs.fetchgit { + url = https://gitlab.com/fresheyeball/nix-branding.git; + rev = "12d425344dba09c1b360b957172b31db93c2d0c4"; + sha256 = "1wm2dmmkcg6i7a34wafkscjs82mm96a4p1wvdhafgfx2zmvwn4d3"; + }) + { + inherit pkgs mkLogo mkThumbnail colors; + primary-color = "bright_yellow"; + background-color = "dim_yello"; + name = "Shpadoinkle"; + twitterHandle = "@shpadoinkleUI"; + } diff --git a/website/colors.nix b/website/colors.nix new file mode 100644 index 00000000..b1583610 --- /dev/null +++ b/website/colors.nix @@ -0,0 +1,5 @@ +{ + bright_yellow = { r = 237; g = 225; b = 113; }; + dim_yellow = { r = 217; g = 206; b = 95; }; + dark = { r = 35; g = 31; b = 32; }; +} diff --git a/website/logo.nix b/website/logo.nix new file mode 100644 index 00000000..4ccc4445 --- /dev/null +++ b/website/logo.nix @@ -0,0 +1,105 @@ +{ color, attrs ? "", width ? 329, height ? 63 }: '' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ® + + + + + +'' diff --git a/website/thumbnail.nix b/website/thumbnail.nix new file mode 100644 index 00000000..3ea02f7c --- /dev/null +++ b/website/thumbnail.nix @@ -0,0 +1,85 @@ +{ color, background, attrs ? "", height ? 63, width ? 63 }: '' + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + +'' -- GitLab From 0c8848d207337fab7c50de2ba78ed8839ae7c25a Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Tue, 20 Apr 2021 14:22:28 -0600 Subject: [PATCH 059/116] favicons from nix-branding --- .gitignore | 1 + nix/overlay-shpadoinkle.nix | 3 +- website/Shpadoinkle-website.cabal | 2 + .../Website/Partials/SocialHTML.hs | 15 ++++++++ .../Shpadoinkle/Website/Partials/Template.hs | 38 ++++++++++--------- website/brand.nix | 8 ++-- website/default.nix | 2 + website/genFavicons.nix | 9 +++++ website/logo.nix | 2 +- website/thumbnail.nix | 13 +++---- 10 files changed, 63 insertions(+), 30 deletions(-) create mode 100644 website/Shpadoinkle/Website/Partials/SocialHTML.hs create mode 100644 website/genFavicons.nix diff --git a/.gitignore b/.gitignore index 3fbf96dc..ebba1f98 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ roster.db *.tar.gz */docs/**/*.html tags +/website/favicon.html diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index 20d6779f..ed1c02d3 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -170,8 +170,9 @@ in { websiteOverride = x: x.overrideAttrs (old: { buildInputs = old.buildInputs ++ [ super.asciidoctor ]; - prePatch = '' + prePatch = let brand = import ../website/brand.nix {}; in '' ${old.prePatch} + cp ${brand.icons.faviconHTML} favicon.html cp -r ${../website/docs} docs chmod -R +rw docs ''; diff --git a/website/Shpadoinkle-website.cabal b/website/Shpadoinkle-website.cabal index cb03fae1..792dcde1 100644 --- a/website/Shpadoinkle-website.cabal +++ b/website/Shpadoinkle-website.cabal @@ -60,6 +60,7 @@ executable disembodied Shpadoinkle.Website.Component.LiveExample Shpadoinkle.Website.Partials.Footer Shpadoinkle.Website.Partials.Nav + Shpadoinkle.Website.Partials.SocialHTML Shpadoinkle.Website.Types.Example Shpadoinkle.Website.Types.ExampleState Shpadoinkle.Website.Types.Hoogle @@ -117,6 +118,7 @@ executable run Shpadoinkle.Website.Partials.Template Shpadoinkle.Website.Partials.Nav Shpadoinkle.Website.Partials.Footer + Shpadoinkle.Website.Partials.SocialHTML Shpadoinkle.Website.Page.Home Shpadoinkle.Website.Page.Documentation Shpadoinkle.Website.Types.CurrentYear diff --git a/website/Shpadoinkle/Website/Partials/SocialHTML.hs b/website/Shpadoinkle/Website/Partials/SocialHTML.hs new file mode 100644 index 00000000..2f3a0315 --- /dev/null +++ b/website/Shpadoinkle/Website/Partials/SocialHTML.hs @@ -0,0 +1,15 @@ +{-# LANGUAGE ExtendedDefaultRules #-} +{-# OPTIONS_GHC -fno-warn-type-defaults #-} + + +module Shpadoinkle.Website.Partials.SocialHTML where + + +import Data.Text +import Shpadoinkle.Html + + +default (Text) + +view :: [Html m a] +view = [] diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index ab5ba5c6..666763b4 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -8,30 +8,35 @@ module Shpadoinkle.Website.Partials.Template ) where -import Control.Lens (_1, _2) -import Data.Text (Text) -import Prelude hiding (div) -import Shpadoinkle (MonadJSM) +import Control.Lens (_1, _2) +import Data.Text (Text, pack) +import Prelude hiding (div) +import Shpadoinkle (MonadJSM) import Shpadoinkle.Html -import Shpadoinkle.Html.TH.AssetLink (assetLink) -import Shpadoinkle.Lens (generalize) -import Shpadoinkle.Router (toHydration) -import Shpadoinkle.Run (Env (..), entrypoint) -import qualified Shpadoinkle.Website.Partials.Footer as Footer -import qualified Shpadoinkle.Website.Partials.Nav as Nav -import Shpadoinkle.Website.Types.CurrentYear (CurrentYear) -import Shpadoinkle.Website.Types.Frontend (Frontend) -import Shpadoinkle.Widgets.Types.Physical (Toggle (..)) +import Shpadoinkle.Html.TH.AssetLink (assetLink) +import Shpadoinkle.Lens (generalize) +import Shpadoinkle.Router (toHydration) +import Shpadoinkle.Run (Env (..), entrypoint) +import Shpadoinkle.Template.TH (embedHtml) +import qualified Shpadoinkle.Website.Partials.Footer as Footer +import qualified Shpadoinkle.Website.Partials.Nav as Nav +import qualified Shpadoinkle.Website.Partials.SocialHTML as SocialHTML +import Shpadoinkle.Website.Types.CurrentYear (CurrentYear) +import Shpadoinkle.Website.Types.Frontend (Frontend) +import Shpadoinkle.Widgets.Types.Physical (Toggle (..)) codemirrorCDN :: Text -> Text codemirrorCDN = mappend "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.59.4/" +favicons :: [Html m a] +favicons = $(embedHtml "./favicon.html") + + headView :: Env -> Frontend -> Html m a -headView ev fe = head_ +headView ev fe = head_ $ [ meta' [ charset "UTF-8" ] - -- , link' [ href $(assetLink "/assets/favicon.svg"), rel "icon", type' "image/svg+xml" ] , meta' [ content "width=device-width, initial-scale=1.0", name' "viewport" ] , link' [ rel "stylesheet" @@ -46,11 +51,10 @@ headView ev fe = head_ , href $(assetLink "/assets/style.css") ] , toHydration fe - , h "title" [] [ "Shpadoinkle" ] , script [ src $ entrypoint ev ] [] , script [ src $ codemirrorCDN "codemirror.min.js" ] [] , script [ src $ codemirrorCDN "mode/haskell/haskell.min.js" ] [] - ] + ] <> SocialHTML.view <> favicons template :: Env -> Frontend -> Html m a -> Html m a diff --git a/website/brand.nix b/website/brand.nix index 4415d59d..2647af32 100644 --- a/website/brand.nix +++ b/website/brand.nix @@ -7,13 +7,13 @@ let colors = import ./colors.nix; in import (pkgs.fetchgit { url = https://gitlab.com/fresheyeball/nix-branding.git; - rev = "12d425344dba09c1b360b957172b31db93c2d0c4"; - sha256 = "1wm2dmmkcg6i7a34wafkscjs82mm96a4p1wvdhafgfx2zmvwn4d3"; + rev = "bf9543ff076d1cc921421bfd14c475b53d11806f"; + sha256 = "01j25xnq11x8q4gk7yfwrslk6c0754b8wbyh72havjs9l91a6hdr"; }) { inherit pkgs mkLogo mkThumbnail colors; - primary-color = "bright_yellow"; - background-color = "dim_yello"; + primary-color = "dark"; + background-color = "dim_yellow"; name = "Shpadoinkle"; twitterHandle = "@shpadoinkleUI"; } diff --git a/website/default.nix b/website/default.nix index 71876c94..3383aa9e 100644 --- a/website/default.nix +++ b/website/default.nix @@ -8,6 +8,7 @@ let pkgs = import ../nix/pkgs.nix { inherit compiler system chan; isJS = false; }; pkgsJS = import ../nix/pkgs.nix { inherit compiler system chan; isJS = true; }; util = import ../nix/util.nix { inherit pkgs compiler; isJS = true; }; + brand = import ./brand.nix { inherit chan; }; opti = if optimize then util.doCannibalize else (x: x); file = if optimize then "all.min.js" else "all.js"; in @@ -15,6 +16,7 @@ in LANG = "C.UTF-8"; } '' mkdir $out + ${brand.icons.mkIcons brand.logo.thumbnail "$out"} ln -s ${./assets} $out/assets ln -s ${opti pkgsJS.haskell.packages.${util.compilerjs}.Shpadoinkle-website}/bin/run.jsexe/${file} $out/all.min.js ${pkgs.haskell.packages.${compiler}.Shpadoinkle-website}/bin/disembodied -o $out diff --git a/website/genFavicons.nix b/website/genFavicons.nix new file mode 100644 index 00000000..ef3b0c38 --- /dev/null +++ b/website/genFavicons.nix @@ -0,0 +1,9 @@ +{ chan ? (import ../nix/chan.nix) +}: +let + brand = import ./brand.nix {}; + pkgs = import ../nix/pkgs.nix { inherit chan; }; +in pkgs.writeScriptBin "genFavicons.sh" '' + cp ${brand.icons.faviconHTML} ./website/favicon.html +'' + diff --git a/website/logo.nix b/website/logo.nix index 4ccc4445..9adc7c0b 100644 --- a/website/logo.nix +++ b/website/logo.nix @@ -1,4 +1,4 @@ -{ color, attrs ? "", width ? 329, height ? 63 }: '' +{ attrs ? "", width ? 329, height ? 63, ... }: '' diff --git a/website/thumbnail.nix b/website/thumbnail.nix index 3ea02f7c..0e603317 100644 --- a/website/thumbnail.nix +++ b/website/thumbnail.nix @@ -1,7 +1,6 @@ -{ color, background, attrs ? "", height ? 63, width ? 63 }: '' +{ attrs ? "", ... }: let width = 63; height = 63; in '' + height="${toString height}px" + width="${toString width}px"> @@ -38,7 +37,7 @@ id="Group-13-Copy"> + fill="#231f20" /> -- GitLab From e245f26f383a3a93b0b14a879f6188e6cdf913a7 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Tue, 20 Apr 2021 14:56:43 -0600 Subject: [PATCH 060/116] social html --- website/Shpadoinkle-website.cabal | 6 ++- .../Shpadoinkle/Website/Partials/Social.hs | 32 +++++++++++++ .../Website/Partials/SocialHTML.hs | 15 ------- .../Shpadoinkle/Website/Partials/Template.hs | 32 ++++++------- website/Shpadoinkle/Website/Types/Social.hs | 45 +++++++++++++++++++ website/default.nix | 4 +- 6 files changed, 99 insertions(+), 35 deletions(-) create mode 100644 website/Shpadoinkle/Website/Partials/Social.hs delete mode 100644 website/Shpadoinkle/Website/Partials/SocialHTML.hs create mode 100644 website/Shpadoinkle/Website/Types/Social.hs diff --git a/website/Shpadoinkle-website.cabal b/website/Shpadoinkle-website.cabal index 792dcde1..51abebee 100644 --- a/website/Shpadoinkle-website.cabal +++ b/website/Shpadoinkle-website.cabal @@ -60,7 +60,7 @@ executable disembodied Shpadoinkle.Website.Component.LiveExample Shpadoinkle.Website.Partials.Footer Shpadoinkle.Website.Partials.Nav - Shpadoinkle.Website.Partials.SocialHTML + Shpadoinkle.Website.Partials.Social Shpadoinkle.Website.Types.Example Shpadoinkle.Website.Types.ExampleState Shpadoinkle.Website.Types.Hoogle @@ -69,6 +69,7 @@ executable disembodied Shpadoinkle.Website.Types.Routes.GettingStarted Shpadoinkle.Website.Types.Routes.Packages Shpadoinkle.Website.Types.Routes.Tutorial + Shpadoinkle.Website.Types.Social hs-source-dirs: ./. @@ -118,7 +119,7 @@ executable run Shpadoinkle.Website.Partials.Template Shpadoinkle.Website.Partials.Nav Shpadoinkle.Website.Partials.Footer - Shpadoinkle.Website.Partials.SocialHTML + Shpadoinkle.Website.Partials.Social Shpadoinkle.Website.Page.Home Shpadoinkle.Website.Page.Documentation Shpadoinkle.Website.Types.CurrentYear @@ -137,6 +138,7 @@ executable run Shpadoinkle.Website.Types.Routes.Tutorial Shpadoinkle.Website.Types.Routes.Packages Shpadoinkle.Website.Types.Routes + Shpadoinkle.Website.Types.Social Shpadoinkle.Website.Style Shpadoinkle.Website.View diff --git a/website/Shpadoinkle/Website/Partials/Social.hs b/website/Shpadoinkle/Website/Partials/Social.hs new file mode 100644 index 00000000..4517150c --- /dev/null +++ b/website/Shpadoinkle/Website/Partials/Social.hs @@ -0,0 +1,32 @@ +{-# LANGUAGE ExtendedDefaultRules #-} +{-# LANGUAGE OverloadedLabels #-} +{-# LANGUAGE OverloadedStrings #-} +{-# OPTIONS_GHC -fno-warn-type-defaults #-} + + +module Shpadoinkle.Website.Partials.Social where + + +import Control.Lens ((^.)) +import Data.Generics.Labels () +import Data.Text (Text) +import Shpadoinkle.Html (Html, content, h, meta', + property, text, + textProperty) +import Shpadoinkle.Website.Types.Frontend (Frontend) +import Shpadoinkle.Website.Types.Social (toSocial) + + +default (Text) + + +view :: Frontend -> [Html m a] +view s' = let s = toSocial s' in + [ meta' [ property "og:type", content "website" ] + , meta' [ property "og:title", content $ s ^. #title ] + , meta' [ property "og:description", content $ s ^. #description ] + , meta' [ textProperty "name" "description", content $ s ^. #description ] + , h "title" [] [ text $ s ^. #title ] + ] <> case s ^. #image of + Just img -> [ meta' [ property "og:image", content img ] ] + Nothing -> [] diff --git a/website/Shpadoinkle/Website/Partials/SocialHTML.hs b/website/Shpadoinkle/Website/Partials/SocialHTML.hs deleted file mode 100644 index 2f3a0315..00000000 --- a/website/Shpadoinkle/Website/Partials/SocialHTML.hs +++ /dev/null @@ -1,15 +0,0 @@ -{-# LANGUAGE ExtendedDefaultRules #-} -{-# OPTIONS_GHC -fno-warn-type-defaults #-} - - -module Shpadoinkle.Website.Partials.SocialHTML where - - -import Data.Text -import Shpadoinkle.Html - - -default (Text) - -view :: [Html m a] -view = [] diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index 666763b4..b4c25b3b 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -8,22 +8,22 @@ module Shpadoinkle.Website.Partials.Template ) where -import Control.Lens (_1, _2) -import Data.Text (Text, pack) -import Prelude hiding (div) -import Shpadoinkle (MonadJSM) +import Control.Lens (_1, _2) +import Data.Text (Text, pack) +import Prelude hiding (div) +import Shpadoinkle (MonadJSM) import Shpadoinkle.Html -import Shpadoinkle.Html.TH.AssetLink (assetLink) -import Shpadoinkle.Lens (generalize) -import Shpadoinkle.Router (toHydration) -import Shpadoinkle.Run (Env (..), entrypoint) -import Shpadoinkle.Template.TH (embedHtml) -import qualified Shpadoinkle.Website.Partials.Footer as Footer -import qualified Shpadoinkle.Website.Partials.Nav as Nav -import qualified Shpadoinkle.Website.Partials.SocialHTML as SocialHTML -import Shpadoinkle.Website.Types.CurrentYear (CurrentYear) -import Shpadoinkle.Website.Types.Frontend (Frontend) -import Shpadoinkle.Widgets.Types.Physical (Toggle (..)) +import Shpadoinkle.Html.TH.AssetLink (assetLink) +import Shpadoinkle.Lens (generalize) +import Shpadoinkle.Router (toHydration) +import Shpadoinkle.Run (Env (..), entrypoint) +import Shpadoinkle.Template.TH (embedHtml) +import qualified Shpadoinkle.Website.Partials.Footer as Footer +import qualified Shpadoinkle.Website.Partials.Nav as Nav +import qualified Shpadoinkle.Website.Partials.Social as Social +import Shpadoinkle.Website.Types.CurrentYear (CurrentYear) +import Shpadoinkle.Website.Types.Frontend (Frontend) +import Shpadoinkle.Widgets.Types.Physical (Toggle (..)) codemirrorCDN :: Text -> Text @@ -54,7 +54,7 @@ headView ev fe = head_ $ , script [ src $ entrypoint ev ] [] , script [ src $ codemirrorCDN "codemirror.min.js" ] [] , script [ src $ codemirrorCDN "mode/haskell/haskell.min.js" ] [] - ] <> SocialHTML.view <> favicons + ] <> Social.view fe <> favicons template :: Env -> Frontend -> Html m a -> Html m a diff --git a/website/Shpadoinkle/Website/Types/Social.hs b/website/Shpadoinkle/Website/Types/Social.hs new file mode 100644 index 00000000..5958e83a --- /dev/null +++ b/website/Shpadoinkle/Website/Types/Social.hs @@ -0,0 +1,45 @@ +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} + + +module Shpadoinkle.Website.Types.Social where + + +import Data.Text (Text) +import GHC.Generics (Generic) +import Shpadoinkle.Website.Types.Frontend (Frontend (..)) + + +data Social = Social + { title :: Text + , description :: Text + , image :: Maybe Text + } deriving Generic + + +toSocial :: Frontend -> Social +toSocial = prependTitle . \case + MHome _ -> Social "" + "A new Functional UI programming paradigm" Nothing + + MConcepts -> Social "Concepts" + "Learn the basic ideas need to write Shpadoinkle applications" Nothing + + MGettingStarted _ -> Social "Getting Started" + "Instructions for getting stared using Shpadoinkle for application development" Nothing + + MPackages _ -> Social "Reference" + "Packages in the Shpadoinkle ecosystem" Nothing + + MTutorial _ -> Social "Tutorial" + "Step by step guide, writing a simple Shpadoinkle project" Nothing + + _ -> Social "404" + "This page is not found" Nothing + + +prependTitle :: Social -> Social +prependTitle s = s { title = "Shpadoinkle" <> f (title s) } + where f "" = "" + f x = " | " <> x diff --git a/website/default.nix b/website/default.nix index 3383aa9e..56a71a3c 100644 --- a/website/default.nix +++ b/website/default.nix @@ -9,8 +9,8 @@ let pkgsJS = import ../nix/pkgs.nix { inherit compiler system chan; isJS = true; }; util = import ../nix/util.nix { inherit pkgs compiler; isJS = true; }; brand = import ./brand.nix { inherit chan; }; - opti = if optimize then util.doCannibalize else (x: x); - file = if optimize then "all.min.js" else "all.js"; + opti = if optimize then util.doCannibalize else (x: x); + file = if optimize then "all.min.js" else "all.js"; in pkgs.runCommand "website" { LANG = "C.UTF-8"; -- GitLab From aee1af8f33e133e532ff97f0ac5b4c3e6548a008 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Tue, 20 Apr 2021 15:22:35 -0600 Subject: [PATCH 061/116] set title --- website/Shpadoinkle/Website/Page/Home.hs | 6 ++++-- website/Shpadoinkle/Website/View.hs | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/website/Shpadoinkle/Website/Page/Home.hs b/website/Shpadoinkle/Website/Page/Home.hs index 57af926b..4c276195 100644 --- a/website/Shpadoinkle/Website/Page/Home.hs +++ b/website/Shpadoinkle/Website/Page/Home.hs @@ -31,6 +31,8 @@ import Shpadoinkle.Website.Types.Example (Example) import Shpadoinkle.Website.Types.Home (ExampleLens, Examples, Home) +import Shpadoinkle.Website.Types.Nav (Nav (NHome), + goTo) import Shpadoinkle.Website.Types.Routes (Route (RGettingStarted), SPA) import Shpadoinkle.Website.Types.Routes.GettingStarted (Route (RGSIndex)) @@ -44,7 +46,7 @@ heroTitle :: Text -> Html m a heroTitle = p [ class' hero_title ] . pure . text -hero' :: Html m a +hero' :: MonadJSM m => Html m a hero' = div_ [ div [ class' hero ] [ div [ class' hero_title_wrapper ] $ heroTitle <$> @@ -54,7 +56,7 @@ hero' = div_ ] , div [ class' hero_button ] [ div [ class' $ split_button <> hero_button__content ] - [ button [ class' split_button__button, type' "button" ] + [ button [ class' split_button__button, type' "button", onClickM_ $ goTo NHome ] [ span [ class' $ split_button__split1 <> hero_button__split ] [ "Get Started" ] , span [ class' $ split_button__split2 <> hero_button__split ] diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index f0b0bb3f..58298bad 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -39,6 +39,7 @@ import Shpadoinkle.Website.Types.Example (Example) import Shpadoinkle.Website.Types.Frontend import Shpadoinkle.Website.Types.Home import Shpadoinkle.Website.Types.Routes +import Shpadoinkle.Website.Types.Social (toSocial) import Shpadoinkle.Widgets.Types.Physical (Toggle (..)) import UnliftIO.Async (Concurrently (..), async) @@ -106,6 +107,7 @@ startJS => TVar (Toggle, Frontend) -> Route -> m (Toggle, Frontend) startJS model r = do t@(_, fe) <- start r + setTitle $ toSocial fe ^. #title case fe of MHome home' -> void . async $ do corn <- compileExamples home' -- GitLab From 77dc4a2bf1b6b1a3c7ab0f256c29f05c2edf1b3d Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Tue, 20 Apr 2021 16:56:23 -0600 Subject: [PATCH 062/116] generate landing logo --- .gitignore | 1 + website/Shpadoinkle/Website/Page/Home.hs | 4 +- website/Shpadoinkle/Website/Types/Routes.hs | 4 +- website/assets/landing_logo.svg | 103 -------------------- website/default.nix | 17 ++++ website/genFavicons.nix | 9 -- website/generate-assets.nix | 29 ++++++ website/generate-assets.sh | 22 +++++ 8 files changed, 73 insertions(+), 116 deletions(-) delete mode 100644 website/assets/landing_logo.svg delete mode 100644 website/genFavicons.nix create mode 100644 website/generate-assets.nix create mode 100755 website/generate-assets.sh diff --git a/.gitignore b/.gitignore index ebba1f98..4a5d4d49 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ roster.db */docs/**/*.html tags /website/favicon.html +/website/assets/landing_logo.svg diff --git a/website/Shpadoinkle/Website/Page/Home.hs b/website/Shpadoinkle/Website/Page/Home.hs index 4c276195..20038626 100644 --- a/website/Shpadoinkle/Website/Page/Home.hs +++ b/website/Shpadoinkle/Website/Page/Home.hs @@ -31,7 +31,7 @@ import Shpadoinkle.Website.Types.Example (Example) import Shpadoinkle.Website.Types.Home (ExampleLens, Examples, Home) -import Shpadoinkle.Website.Types.Nav (Nav (NHome), +import Shpadoinkle.Website.Types.Nav (Nav (NGettingStarted), goTo) import Shpadoinkle.Website.Types.Routes (Route (RGettingStarted), SPA) @@ -56,7 +56,7 @@ hero' = div_ ] , div [ class' hero_button ] [ div [ class' $ split_button <> hero_button__content ] - [ button [ class' split_button__button, type' "button", onClickM_ $ goTo NHome ] + [ button [ class' split_button__button, type' "button", onClickM_ $ goTo NGettingStarted ] [ span [ class' $ split_button__split1 <> hero_button__split ] [ "Get Started" ] , span [ class' $ split_button__split2 <> hero_button__split ] diff --git a/website/Shpadoinkle/Website/Types/Routes.hs b/website/Shpadoinkle/Website/Types/Routes.hs index fd0f3411..080062ed 100644 --- a/website/Shpadoinkle/Website/Types/Routes.hs +++ b/website/Shpadoinkle/Website/Types/Routes.hs @@ -44,7 +44,7 @@ data Route type SPA m - = "home" :> View m (Toggle, Frontend) + = View m (Toggle, Frontend) :<|> "concepts" :> View m (Toggle, Frontend) :<|> "getting-started" :> GettingStarted.SPA m (Toggle, Frontend) :<|> "packages" :> Packages.SPA m (Toggle, Frontend) @@ -69,7 +69,7 @@ routes instance Routed (SPA m) Route where redirect = \case RHome -> - Redirect (Proxy @("home" :> View m (Toggle, Frontend))) id + Redirect (Proxy @(View m (Toggle, Frontend))) id RConcepts -> Redirect (Proxy @("concepts" :> View m (Toggle, Frontend))) id diff --git a/website/assets/landing_logo.svg b/website/assets/landing_logo.svg deleted file mode 100644 index 72dbd5f9..00000000 --- a/website/assets/landing_logo.svg +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ® - - - - - diff --git a/website/default.nix b/website/default.nix index 56a71a3c..c94e58e6 100644 --- a/website/default.nix +++ b/website/default.nix @@ -4,6 +4,23 @@ , optimize ? true }: + +# This file is part of Shpadoinkle Website. +# +# Shpadoinkle Website is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Shpadoinkle Website is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Shpadoinkle Website. If not, see . + + let pkgs = import ../nix/pkgs.nix { inherit compiler system chan; isJS = false; }; pkgsJS = import ../nix/pkgs.nix { inherit compiler system chan; isJS = true; }; diff --git a/website/genFavicons.nix b/website/genFavicons.nix deleted file mode 100644 index ef3b0c38..00000000 --- a/website/genFavicons.nix +++ /dev/null @@ -1,9 +0,0 @@ -{ chan ? (import ../nix/chan.nix) -}: -let - brand = import ./brand.nix {}; - pkgs = import ../nix/pkgs.nix { inherit chan; }; -in pkgs.writeScriptBin "genFavicons.sh" '' - cp ${brand.icons.faviconHTML} ./website/favicon.html -'' - diff --git a/website/generate-assets.nix b/website/generate-assets.nix new file mode 100644 index 00000000..a7b41d5b --- /dev/null +++ b/website/generate-assets.nix @@ -0,0 +1,29 @@ +{ chan ? (import ../nix/chan.nix) +}: + + +# This file is part of Shpadoinkle Website. +# +# Shpadoinkle Website is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Shpadoinkle Website is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Shpadoinkle Website. If not, see . + + +let + brand = import ./brand.nix {}; + pkgs = import ../nix/pkgs.nix { inherit chan; }; +in pkgs.writeScriptBin "generate-assets.sh" '' + rm -f ./website/favicon.html ./website/assets/landing_logo.svg + cp ${brand.icons.faviconHTML} ./website/favicon.html + cp ${brand.logo.full.svg { color = "bright_yellow"; }} ./website/assets/landing_logo.svg +'' + diff --git a/website/generate-assets.sh b/website/generate-assets.sh new file mode 100755 index 00000000..590f175e --- /dev/null +++ b/website/generate-assets.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + + +# This file is part of Shpadoinkle Website. +# +# Shpadoinkle Website is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Shpadoinkle Website is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Shpadoinkle Website. If not, see . + + +set -eu + +$(nix-build ./website/generate-assets.nix)/bin/generate-assets.sh -- GitLab From d051e435c55af1bee117409c8f159ea4ad1e9df3 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Tue, 20 Apr 2021 21:46:06 -0600 Subject: [PATCH 063/116] canonical --- website/Shpadoinkle-website.cabal | 3 + website/Shpadoinkle/Website/Disembodied.hs | 2 +- .../Shpadoinkle/Website/Partials/Canonical.hs | 28 +++++++++ .../Shpadoinkle/Website/Partials/Template.hs | 43 +++++++------- website/Shpadoinkle/Website/Run.hs | 2 +- website/Shpadoinkle/Website/Types/Routes.hs | 57 ++++++++++--------- website/Shpadoinkle/Website/View.hs | 7 +-- 7 files changed, 87 insertions(+), 55 deletions(-) create mode 100644 website/Shpadoinkle/Website/Partials/Canonical.hs diff --git a/website/Shpadoinkle-website.cabal b/website/Shpadoinkle-website.cabal index 51abebee..c9cc4825 100644 --- a/website/Shpadoinkle-website.cabal +++ b/website/Shpadoinkle-website.cabal @@ -58,6 +58,7 @@ executable disembodied Shpadoinkle.Website.Types.Routes Shpadoinkle.Website.View Shpadoinkle.Website.Component.LiveExample + Shpadoinkle.Website.Partials.Canonical Shpadoinkle.Website.Partials.Footer Shpadoinkle.Website.Partials.Nav Shpadoinkle.Website.Partials.Social @@ -95,6 +96,7 @@ executable disembodied , lens , mtl , random + , network-uri , servant , servant-server , text >=1.2.3 && <1.3 @@ -120,6 +122,7 @@ executable run Shpadoinkle.Website.Partials.Nav Shpadoinkle.Website.Partials.Footer Shpadoinkle.Website.Partials.Social + Shpadoinkle.Website.Partials.Canonical Shpadoinkle.Website.Page.Home Shpadoinkle.Website.Page.Documentation Shpadoinkle.Website.Types.CurrentYear diff --git a/website/Shpadoinkle/Website/Disembodied.hs b/website/Shpadoinkle/Website/Disembodied.hs index 221091af..3049fd85 100644 --- a/website/Shpadoinkle/Website/Disembodied.hs +++ b/website/Shpadoinkle/Website/Disembodied.hs @@ -42,7 +42,7 @@ newtype Noop a = Noop (JSM a) getStarts :: (MonadJSM m, ExampleEffects m) => IO (SiteSpec (SPA m)) getStarts = do cy <- getCurrentYear - traverseUnions (fmap (\fe -> template Prod (snd fe) $ view cy fe) . start) routes + traverseUnions (\r -> (\fe -> template Prod r (snd fe) $ view cy fe) <$> start r) routes main :: IO () diff --git a/website/Shpadoinkle/Website/Partials/Canonical.hs b/website/Shpadoinkle/Website/Partials/Canonical.hs new file mode 100644 index 00000000..b9b05cd2 --- /dev/null +++ b/website/Shpadoinkle/Website/Partials/Canonical.hs @@ -0,0 +1,28 @@ +{-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE OverloadedLabels #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} + + +module Shpadoinkle.Website.Partials.Canonical where + + +import Control.Lens ((.~)) +import Data.Generics.Labels () +import Data.Proxy (Proxy (..)) +import Data.Text (pack) +import Servant.API (safeLink) +import Servant.Links (linkURI) +import Shpadoinkle.Html (Html, href, link', rel) +import Shpadoinkle.Router (Redirect (..), redirect) +import Shpadoinkle.Website.Types.Routes (Route, SPA) + + +view :: forall m a. Route -> Html m a +view r = case redirect @(SPA m) r of + Redirect proxySub mkLink' -> link' + [ rel "canonical" + , href . pack . show . (#uriAuthority . #_Just . #uriRegName .~ "shpadoinkle.org") . linkURI . mkLink' + $ safeLink (Proxy @(SPA m)) proxySub + ] diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index b4c25b3b..3427e47b 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -8,22 +8,24 @@ module Shpadoinkle.Website.Partials.Template ) where -import Control.Lens (_1, _2) -import Data.Text (Text, pack) -import Prelude hiding (div) -import Shpadoinkle (MonadJSM) +import Control.Lens (_1, _2) +import Data.Text (Text, pack) +import Prelude hiding (div) +import Shpadoinkle (MonadJSM) import Shpadoinkle.Html -import Shpadoinkle.Html.TH.AssetLink (assetLink) -import Shpadoinkle.Lens (generalize) -import Shpadoinkle.Router (toHydration) -import Shpadoinkle.Run (Env (..), entrypoint) -import Shpadoinkle.Template.TH (embedHtml) -import qualified Shpadoinkle.Website.Partials.Footer as Footer -import qualified Shpadoinkle.Website.Partials.Nav as Nav -import qualified Shpadoinkle.Website.Partials.Social as Social -import Shpadoinkle.Website.Types.CurrentYear (CurrentYear) -import Shpadoinkle.Website.Types.Frontend (Frontend) -import Shpadoinkle.Widgets.Types.Physical (Toggle (..)) +import Shpadoinkle.Html.TH.AssetLink (assetLink) +import Shpadoinkle.Lens (generalize) +import Shpadoinkle.Router (toHydration) +import Shpadoinkle.Run (Env (..), entrypoint) +import Shpadoinkle.Template.TH (embedHtml) +import qualified Shpadoinkle.Website.Partials.Canonical as Canonical +import qualified Shpadoinkle.Website.Partials.Footer as Footer +import qualified Shpadoinkle.Website.Partials.Nav as Nav +import qualified Shpadoinkle.Website.Partials.Social as Social +import Shpadoinkle.Website.Types.CurrentYear (CurrentYear) +import Shpadoinkle.Website.Types.Frontend (Frontend) +import Shpadoinkle.Website.Types.Routes (Route) +import Shpadoinkle.Widgets.Types.Physical (Toggle (..)) codemirrorCDN :: Text -> Text @@ -34,8 +36,8 @@ favicons :: [Html m a] favicons = $(embedHtml "./favicon.html") -headView :: Env -> Frontend -> Html m a -headView ev fe = head_ $ +headView :: Env -> Route -> Frontend -> Html m a +headView ev r fe = head_ $ [ meta' [ charset "UTF-8" ] , meta' [ content "width=device-width, initial-scale=1.0", name' "viewport" ] , link' @@ -54,12 +56,13 @@ headView ev fe = head_ $ , script [ src $ entrypoint ev ] [] , script [ src $ codemirrorCDN "codemirror.min.js" ] [] , script [ src $ codemirrorCDN "mode/haskell/haskell.min.js" ] [] + , Canonical.view r ] <> Social.view fe <> favicons -template :: Env -> Frontend -> Html m a -> Html m a -template ev fe content' = html [ lang "en" ] - [ headView ev fe +template :: Env -> Route -> Frontend -> Html m a -> Html m a +template ev r fe content' = html [ lang "en" ] + [ headView ev r fe , body "top-border" [ content' ] ] diff --git a/website/Shpadoinkle/Website/Run.hs b/website/Shpadoinkle/Website/Run.hs index 5c85e261..9a366f8b 100644 --- a/website/Shpadoinkle/Website/Run.hs +++ b/website/Shpadoinkle/Website/Run.hs @@ -138,6 +138,6 @@ dev :: IO () dev = liveWithBackend 8080 app . pure $ Servant.serve (Proxy @ (SPA IO)) $ serveUI @(SPA IO) "." (\r -> do x <- start r cy <- getCurrentYear - return $ template @App Dev (snd x) (view cy x)) routes + return $ template @App Dev r (snd x) (view cy x)) routes #endif diff --git a/website/Shpadoinkle/Website/Types/Routes.hs b/website/Shpadoinkle/Website/Types/Routes.hs index 080062ed..f1008c4f 100644 --- a/website/Shpadoinkle/Website/Types/Routes.hs +++ b/website/Shpadoinkle/Website/Types/Routes.hs @@ -32,6 +32,9 @@ import qualified Shpadoinkle.Website.Types.Routes.Tutorial as Tutorial import Shpadoinkle.Widgets.Types.Physical (Toggle) +type TopLevelModel = (Route, Toggle, Frontend) + + data Route = RHome | RConcepts @@ -44,14 +47,14 @@ data Route type SPA m - = View m (Toggle, Frontend) - :<|> "concepts" :> View m (Toggle, Frontend) - :<|> "getting-started" :> GettingStarted.SPA m (Toggle, Frontend) - :<|> "packages" :> Packages.SPA m (Toggle, Frontend) - :<|> "tutorial" :> Tutorial.SPA m (Toggle, Frontend) - :<|> "sandbox" :> View m (Toggle, Frontend) - :<|> "404" :> View m (Toggle, Frontend) - :<|> View m (Toggle, Frontend) + = View m TopLevelModel + :<|> "concepts" :> View m TopLevelModel + :<|> "getting-started" :> GettingStarted.SPA m TopLevelModel + :<|> "packages" :> Packages.SPA m TopLevelModel + :<|> "tutorial" :> Tutorial.SPA m TopLevelModel + :<|> "sandbox" :> View m TopLevelModel + :<|> "404" :> View m TopLevelModel + :<|> View m TopLevelModel routes :: SPA m :>> Route @@ -69,46 +72,46 @@ routes instance Routed (SPA m) Route where redirect = \case RHome -> - Redirect (Proxy @(View m (Toggle, Frontend))) id + Redirect (Proxy @(View m TopLevelModel)) id RConcepts -> - Redirect (Proxy @("concepts" :> View m (Toggle, Frontend))) id + Redirect (Proxy @("concepts" :> View m TopLevelModel)) id RGettingStarted GettingStarted.RGSIndex -> - Redirect (Proxy @("getting-started" :> View m (Toggle, Frontend))) id + Redirect (Proxy @("getting-started" :> View m TopLevelModel)) id RGettingStarted GettingStarted.RGSAddingToYourProject -> - Redirect (Proxy @("getting-started" :> "adding-to-your-project" :> View m (Toggle, Frontend))) id + Redirect (Proxy @("getting-started" :> "adding-to-your-project" :> View m TopLevelModel)) id RGettingStarted GettingStarted.RGSExtendAnExample -> - Redirect (Proxy @("getting-started" :> "extend-an-example" :> View m (Toggle, Frontend))) id + Redirect (Proxy @("getting-started" :> "extend-an-example" :> View m TopLevelModel)) id RPackages Packages.RPIndex -> - Redirect (Proxy @("packages" :> View m (Toggle, Frontend))) id + Redirect (Proxy @("packages" :> View m TopLevelModel)) id RPackages Packages.RPCore -> - Redirect (Proxy @("packages" :> "core" :> View m (Toggle, Frontend))) id + Redirect (Proxy @("packages" :> "core" :> View m TopLevelModel)) id RPackages Packages.RPConsole -> - Redirect (Proxy @("packages" :> "console" :> View m (Toggle, Frontend))) id + Redirect (Proxy @("packages" :> "console" :> View m TopLevelModel)) id RPackages Packages.RPBackends -> - Redirect (Proxy @("packages" :> "backends" :> View m (Toggle, Frontend))) id + Redirect (Proxy @("packages" :> "backends" :> View m TopLevelModel)) id RPackages Packages.RPHtml -> - Redirect (Proxy @("packages" :> "html" :> View m (Toggle, Frontend))) id + Redirect (Proxy @("packages" :> "html" :> View m TopLevelModel)) id RPackages Packages.RPLens -> - Redirect (Proxy @("packages" :> "lens" :> View m (Toggle, Frontend))) id + Redirect (Proxy @("packages" :> "lens" :> View m TopLevelModel)) id RPackages Packages.RPRouter -> - Redirect (Proxy @("packages" :> "router" :> View m (Toggle, Frontend))) id + Redirect (Proxy @("packages" :> "router" :> View m TopLevelModel)) id RPackages Packages.RPWidgets -> - Redirect (Proxy @("packages" :> "widgets" :> View m (Toggle, Frontend))) id + Redirect (Proxy @("packages" :> "widgets" :> View m TopLevelModel)) id RTutorial Tutorial.RTIndex -> - Redirect (Proxy @("tutorial" :> View m (Toggle, Frontend))) id + Redirect (Proxy @("tutorial" :> View m TopLevelModel)) id RTutorial Tutorial.RTCalculator -> - Redirect (Proxy @("tutorial" :> "calculator" :> View m (Toggle, Frontend))) id + Redirect (Proxy @("tutorial" :> "calculator" :> View m TopLevelModel)) id RTutorial Tutorial.RTImmediateExecution -> - Redirect (Proxy @("tutorial" :> "immediate-execution" :> View m (Toggle, Frontend))) id + Redirect (Proxy @("tutorial" :> "immediate-execution" :> View m TopLevelModel)) id RTutorial Tutorial.RTComposing -> - Redirect (Proxy @("tutorial" :> "composing" :> View m (Toggle, Frontend))) id + Redirect (Proxy @("tutorial" :> "composing" :> View m TopLevelModel)) id RSandbox -> - Redirect (Proxy @("sandbox" :> View m (Toggle, Frontend))) id + Redirect (Proxy @("sandbox" :> View m TopLevelModel)) id RFourOhFour -> - Redirect (Proxy @("404" :> View m (Toggle, Frontend))) id + Redirect (Proxy @("404" :> View m TopLevelModel)) id diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index 58298bad..241b9d8c 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -21,7 +21,6 @@ import Control.Monad (void) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Reader import Data.Generics.Labels () -import Data.String import Data.Text (Text) import Prelude hiding (div) import Shpadoinkle @@ -48,10 +47,6 @@ import UnliftIO.Async (Concurrently (..), default (Text) -domain :: IsString s => s -domain = "https://shpadoinkle.org" - - fourOhFour :: Html m a fourOhFour = h2_ [ "404" ] @@ -92,7 +87,7 @@ genExampleTokens = liftIO $ Examples <$> genSnowToken <*> genSnowToken <*> genSn start :: MonadIO m => Route -> m (Toggle, Frontend) -start = fmap (mempty,) . \case +start = fmap (mempty, ) . \case RHome -> MHome . emptyHome <$> genExampleTokens RConcepts -> pure MConcepts RGettingStarted x -> pure $ MGettingStarted x -- GitLab From d93ad6ca9d3e8c13dc37872021838c0dc46ac5f4 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Wed, 21 Apr 2021 11:43:09 -0600 Subject: [PATCH 064/116] support all legacy uris --- disembodied/Shpadoinkle/Disembodied.hs | 5 +- router/Shpadoinkle/Router.hs | 18 +++-- website/Shpadoinkle-website.cabal | 3 +- .../Shpadoinkle/Website/Partials/Canonical.hs | 28 ------- .../Shpadoinkle/Website/Partials/Template.hs | 52 ++++++++----- website/Shpadoinkle/Website/Types/Routes.hs | 77 ++++++++++++------- .../Website/Types/Routes/GettingStarted.hs | 6 ++ .../Website/Types/Routes/Packages.hs | 11 +++ .../Website/Types/Routes/Tutorial.hs | 7 ++ 9 files changed, 121 insertions(+), 86 deletions(-) delete mode 100644 website/Shpadoinkle/Website/Partials/Canonical.hs diff --git a/disembodied/Shpadoinkle/Disembodied.hs b/disembodied/Shpadoinkle/Disembodied.hs index f30f5dbd..a4eb7c43 100644 --- a/disembodied/Shpadoinkle/Disembodied.hs +++ b/disembodied/Shpadoinkle/Disembodied.hs @@ -30,7 +30,7 @@ module Shpadoinkle.Disembodied ( import Control.Monad (void) import Data.Kind (Type) import Data.Proxy (Proxy (..)) -import Data.Text (unpack) +import Data.Text (isSuffixOf, pack, unpack) import Data.Text.IO as T (writeFile) import Servant.API import System.Directory (createDirectoryIfMissing) @@ -190,6 +190,9 @@ writeSite fs layout = go fs $ buildSite @layout layout where go curr (SChoice x y) = void $ go curr x `concurrently` go curr y go curr (SCapture f) = forConcurrently_ [ minBound .. maxBound ] $ \c -> go curr $ SPath (unpack $ toUrlPiece c) $ f c + go curr (SPath path (SIndex page)) | ".html" `isSuffixOf` pack path = do + createDirectoryIfMissing False curr + T.writeFile (curr path) $ renderStatic page go curr (SPath path site) = do createDirectoryIfMissing False (curr path) go (curr path) site diff --git a/router/Shpadoinkle/Router.hs b/router/Shpadoinkle/Router.hs index 6e61568d..9a2f2b93 100644 --- a/router/Shpadoinkle/Router.hs +++ b/router/Shpadoinkle/Router.hs @@ -37,7 +37,7 @@ module Shpadoinkle.Router ( -- * Shpadoinkle with SPA , fullPageSPAC, fullPageSPA, fullPageSPA' -- * Navigation - , navigate + , navigate, getURI -- * Rehydration , withHydration, toHydration -- * Re-Exports @@ -175,12 +175,18 @@ navigate :: forall a m r. (MonadJSM m, Routed a r) => r -> m () navigate r = do w <- currentWindowUnchecked history <- getHistory w + let uri = getURI @a @r r + pushState history () "" . Just . T.pack $ + "/" ++ uriPath uri ++ uriQuery uri ++ uriFragment uri + liftIO $ putMVar syncRoute () + + +-- | Get the cannonical URI for a given route +getURI :: forall a r. Routed a r => r -> URI +getURI r = case redirect r :: Redirect a of - Redirect pr mf -> do - let uri = linkURI . mf $ safeLink (Proxy @a) pr - pushState history () "" . Just . T.pack $ - "/" ++ uriPath uri ++ uriQuery uri ++ uriFragment uri - liftIO $ putMVar syncRoute () + Redirect pr mf -> linkURI . mf $ safeLink (Proxy @a) pr + -- | This method wraps @shpadoinkle@, providing for a convenient entrypoint diff --git a/website/Shpadoinkle-website.cabal b/website/Shpadoinkle-website.cabal index c9cc4825..e18f6ef9 100644 --- a/website/Shpadoinkle-website.cabal +++ b/website/Shpadoinkle-website.cabal @@ -58,7 +58,6 @@ executable disembodied Shpadoinkle.Website.Types.Routes Shpadoinkle.Website.View Shpadoinkle.Website.Component.LiveExample - Shpadoinkle.Website.Partials.Canonical Shpadoinkle.Website.Partials.Footer Shpadoinkle.Website.Partials.Nav Shpadoinkle.Website.Partials.Social @@ -122,7 +121,6 @@ executable run Shpadoinkle.Website.Partials.Nav Shpadoinkle.Website.Partials.Footer Shpadoinkle.Website.Partials.Social - Shpadoinkle.Website.Partials.Canonical Shpadoinkle.Website.Page.Home Shpadoinkle.Website.Page.Documentation Shpadoinkle.Website.Types.CurrentYear @@ -169,6 +167,7 @@ executable run , jsaddle , lens , mtl + , network-uri , random , servant , text >=1.2.3 && <1.3 diff --git a/website/Shpadoinkle/Website/Partials/Canonical.hs b/website/Shpadoinkle/Website/Partials/Canonical.hs deleted file mode 100644 index b9b05cd2..00000000 --- a/website/Shpadoinkle/Website/Partials/Canonical.hs +++ /dev/null @@ -1,28 +0,0 @@ -{-# LANGUAGE ExistentialQuantification #-} -{-# LANGUAGE OverloadedLabels #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeApplications #-} - - -module Shpadoinkle.Website.Partials.Canonical where - - -import Control.Lens ((.~)) -import Data.Generics.Labels () -import Data.Proxy (Proxy (..)) -import Data.Text (pack) -import Servant.API (safeLink) -import Servant.Links (linkURI) -import Shpadoinkle.Html (Html, href, link', rel) -import Shpadoinkle.Router (Redirect (..), redirect) -import Shpadoinkle.Website.Types.Routes (Route, SPA) - - -view :: forall m a. Route -> Html m a -view r = case redirect @(SPA m) r of - Redirect proxySub mkLink' -> link' - [ rel "canonical" - , href . pack . show . (#uriAuthority . #_Just . #uriRegName .~ "shpadoinkle.org") . linkURI . mkLink' - $ safeLink (Proxy @(SPA m)) proxySub - ] diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index 3427e47b..42e14432 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -1,5 +1,8 @@ -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE OverloadedLabels #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} module Shpadoinkle.Website.Partials.Template @@ -8,24 +11,25 @@ module Shpadoinkle.Website.Partials.Template ) where -import Control.Lens (_1, _2) -import Data.Text (Text, pack) -import Prelude hiding (div) -import Shpadoinkle (MonadJSM) +import Control.Lens (_1, _2, (&), (.~), (?~)) +import Data.Generics.Labels () +import Data.Text (Text, pack) +import Network.URI (nullURIAuth) +import Prelude hiding (div) +import Shpadoinkle (MonadJSM) import Shpadoinkle.Html -import Shpadoinkle.Html.TH.AssetLink (assetLink) -import Shpadoinkle.Lens (generalize) -import Shpadoinkle.Router (toHydration) -import Shpadoinkle.Run (Env (..), entrypoint) -import Shpadoinkle.Template.TH (embedHtml) -import qualified Shpadoinkle.Website.Partials.Canonical as Canonical -import qualified Shpadoinkle.Website.Partials.Footer as Footer -import qualified Shpadoinkle.Website.Partials.Nav as Nav -import qualified Shpadoinkle.Website.Partials.Social as Social -import Shpadoinkle.Website.Types.CurrentYear (CurrentYear) -import Shpadoinkle.Website.Types.Frontend (Frontend) -import Shpadoinkle.Website.Types.Routes (Route) -import Shpadoinkle.Widgets.Types.Physical (Toggle (..)) +import Shpadoinkle.Html.TH.AssetLink (assetLink) +import Shpadoinkle.Lens (generalize) +import Shpadoinkle.Router (getURI, toHydration) +import Shpadoinkle.Run (Env (..), entrypoint) +import Shpadoinkle.Template.TH (embedHtml) +import qualified Shpadoinkle.Website.Partials.Footer as Footer +import qualified Shpadoinkle.Website.Partials.Nav as Nav +import qualified Shpadoinkle.Website.Partials.Social as Social +import Shpadoinkle.Website.Types.CurrentYear (CurrentYear) +import Shpadoinkle.Website.Types.Frontend (Frontend) +import Shpadoinkle.Website.Types.Routes (Route, SPA) +import Shpadoinkle.Widgets.Types.Physical (Toggle (..)) codemirrorCDN :: Text -> Text @@ -36,7 +40,7 @@ favicons :: [Html m a] favicons = $(embedHtml "./favicon.html") -headView :: Env -> Route -> Frontend -> Html m a +headView :: forall m a. Env -> Route -> Frontend -> Html m a headView ev r fe = head_ $ [ meta' [ charset "UTF-8" ] , meta' [ content "width=device-width, initial-scale=1.0", name' "viewport" ] @@ -56,7 +60,13 @@ headView ev r fe = head_ $ , script [ src $ entrypoint ev ] [] , script [ src $ codemirrorCDN "codemirror.min.js" ] [] , script [ src $ codemirrorCDN "mode/haskell/haskell.min.js" ] [] - , Canonical.view r + , link' + [ rel "canonical" + , href . pack . show + . (#uriAuthority ?~ (nullURIAuth & #uriRegName .~ "shpadoinkle.org/")) + . (#uriScheme .~ "https:") + $ getURI @(SPA m) r + ] ] <> Social.view fe <> favicons diff --git a/website/Shpadoinkle/Website/Types/Routes.hs b/website/Shpadoinkle/Website/Types/Routes.hs index f1008c4f..0827fab5 100644 --- a/website/Shpadoinkle/Website/Types/Routes.hs +++ b/website/Shpadoinkle/Website/Types/Routes.hs @@ -32,7 +32,8 @@ import qualified Shpadoinkle.Website.Types.Routes.Tutorial as Tutorial import Shpadoinkle.Widgets.Types.Physical (Toggle) -type TopLevelModel = (Route, Toggle, Frontend) +type TopModel = (Toggle, Frontend) +type View' m = View m TopModel data Route @@ -47,23 +48,43 @@ data Route type SPA m - = View m TopLevelModel - :<|> "concepts" :> View m TopLevelModel - :<|> "getting-started" :> GettingStarted.SPA m TopLevelModel - :<|> "packages" :> Packages.SPA m TopLevelModel - :<|> "tutorial" :> Tutorial.SPA m TopLevelModel - :<|> "sandbox" :> View m TopLevelModel - :<|> "404" :> View m TopLevelModel - :<|> View m TopLevelModel + = View' m + :<|> "docs" :> "index.html" :> View' m + + :<|> "concepts" :> View' m + :<|> "docs" :> "concepts.html" :> View' m + + :<|> "getting-started" :> GettingStarted.SPA m TopModel + :<|> "docs" :> "getting-started" :> GettingStarted.LegacySPA m TopModel + + :<|> "packages" :> Packages.SPA m TopModel + :<|> "docs" :> "packages" :> Packages.LegacySPA m TopModel + + :<|> "tutorial" :> Tutorial.SPA m TopModel + :<|> "docs" :> "tutorial" :> Tutorial.LegacySPA m TopModel + + :<|> "sandbox" :> View' m + :<|> "404" :> View' m + :<|> View' m routes :: SPA m :>> Route routes = RHome + :<|> RHome + + :<|> RConcepts :<|> RConcepts + + :<|> mapUnions RGettingStarted GettingStarted.routes :<|> mapUnions RGettingStarted GettingStarted.routes + + :<|> mapUnions RPackages Packages.routes :<|> mapUnions RPackages Packages.routes + + :<|> mapUnions RTutorial Tutorial.routes :<|> mapUnions RTutorial Tutorial.routes + :<|> RSandbox :<|> RFourOhFour :<|> RHome @@ -72,46 +93,46 @@ routes instance Routed (SPA m) Route where redirect = \case RHome -> - Redirect (Proxy @(View m TopLevelModel)) id + Redirect (Proxy @(View' m)) id RConcepts -> - Redirect (Proxy @("concepts" :> View m TopLevelModel)) id + Redirect (Proxy @("concepts" :> View' m)) id RGettingStarted GettingStarted.RGSIndex -> - Redirect (Proxy @("getting-started" :> View m TopLevelModel)) id + Redirect (Proxy @("getting-started" :> View' m)) id RGettingStarted GettingStarted.RGSAddingToYourProject -> - Redirect (Proxy @("getting-started" :> "adding-to-your-project" :> View m TopLevelModel)) id + Redirect (Proxy @("getting-started" :> "adding-to-your-project" :> View' m)) id RGettingStarted GettingStarted.RGSExtendAnExample -> - Redirect (Proxy @("getting-started" :> "extend-an-example" :> View m TopLevelModel)) id + Redirect (Proxy @("getting-started" :> "extend-an-example" :> View' m)) id RPackages Packages.RPIndex -> - Redirect (Proxy @("packages" :> View m TopLevelModel)) id + Redirect (Proxy @("packages" :> View' m)) id RPackages Packages.RPCore -> - Redirect (Proxy @("packages" :> "core" :> View m TopLevelModel)) id + Redirect (Proxy @("packages" :> "core" :> View' m)) id RPackages Packages.RPConsole -> - Redirect (Proxy @("packages" :> "console" :> View m TopLevelModel)) id + Redirect (Proxy @("packages" :> "console" :> View' m)) id RPackages Packages.RPBackends -> - Redirect (Proxy @("packages" :> "backends" :> View m TopLevelModel)) id + Redirect (Proxy @("packages" :> "backends" :> View' m)) id RPackages Packages.RPHtml -> - Redirect (Proxy @("packages" :> "html" :> View m TopLevelModel)) id + Redirect (Proxy @("packages" :> "html" :> View' m)) id RPackages Packages.RPLens -> - Redirect (Proxy @("packages" :> "lens" :> View m TopLevelModel)) id + Redirect (Proxy @("packages" :> "lens" :> View' m)) id RPackages Packages.RPRouter -> - Redirect (Proxy @("packages" :> "router" :> View m TopLevelModel)) id + Redirect (Proxy @("packages" :> "router" :> View' m)) id RPackages Packages.RPWidgets -> - Redirect (Proxy @("packages" :> "widgets" :> View m TopLevelModel)) id + Redirect (Proxy @("packages" :> "widgets" :> View' m)) id RTutorial Tutorial.RTIndex -> - Redirect (Proxy @("tutorial" :> View m TopLevelModel)) id + Redirect (Proxy @("tutorial" :> View' m)) id RTutorial Tutorial.RTCalculator -> - Redirect (Proxy @("tutorial" :> "calculator" :> View m TopLevelModel)) id + Redirect (Proxy @("tutorial" :> "calculator" :> View' m)) id RTutorial Tutorial.RTImmediateExecution -> - Redirect (Proxy @("tutorial" :> "immediate-execution" :> View m TopLevelModel)) id + Redirect (Proxy @("tutorial" :> "immediate-execution" :> View' m)) id RTutorial Tutorial.RTComposing -> - Redirect (Proxy @("tutorial" :> "composing" :> View m TopLevelModel)) id + Redirect (Proxy @("tutorial" :> "composing" :> View' m)) id RSandbox -> - Redirect (Proxy @("sandbox" :> View m TopLevelModel)) id + Redirect (Proxy @("sandbox" :> View' m)) id RFourOhFour -> - Redirect (Proxy @("404" :> View m TopLevelModel)) id + Redirect (Proxy @("404" :> View' m)) id diff --git a/website/Shpadoinkle/Website/Types/Routes/GettingStarted.hs b/website/Shpadoinkle/Website/Types/Routes/GettingStarted.hs index 8c41fa2f..144cf025 100644 --- a/website/Shpadoinkle/Website/Types/Routes/GettingStarted.hs +++ b/website/Shpadoinkle/Website/Types/Routes/GettingStarted.hs @@ -25,6 +25,12 @@ data Route deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, FromJSON, ToJSON, NFData) +type LegacySPA m a + = "index.html" :> View m a + :<|> "extend-an-example.html" :> View m a + :<|> "adding-to-your-project.html" :> View m a + + type SPA m a = View m a :<|> "adding-to-your-project" :> View m a diff --git a/website/Shpadoinkle/Website/Types/Routes/Packages.hs b/website/Shpadoinkle/Website/Types/Routes/Packages.hs index b7222d04..5382e8f3 100644 --- a/website/Shpadoinkle/Website/Types/Routes/Packages.hs +++ b/website/Shpadoinkle/Website/Types/Routes/Packages.hs @@ -29,6 +29,17 @@ data Route deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, FromJSON, ToJSON, NFData) +type LegacySPA m a + = "index.html" :> View m a + :<|> "core.html" :> View m a + :<|> "backends.html" :> View m a + :<|> "console.html" :> View m a + :<|> "lens.html" :> View m a + :<|> "html.html" :> View m a + :<|> "router.html" :> View m a + :<|> "widgets.html" :> View m a + + type SPA m a = View m a :<|> "core" :> View m a diff --git a/website/Shpadoinkle/Website/Types/Routes/Tutorial.hs b/website/Shpadoinkle/Website/Types/Routes/Tutorial.hs index e41e5702..5d30805b 100644 --- a/website/Shpadoinkle/Website/Types/Routes/Tutorial.hs +++ b/website/Shpadoinkle/Website/Types/Routes/Tutorial.hs @@ -25,6 +25,13 @@ data Route deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, FromJSON, ToJSON, NFData) +type LegacySPA m a + = "index.html" :> View m a + :<|> "calculator.html" :> View m a + :<|> "immediate-execution.html" :> View m a + :<|> "composing.html" :> View m a + + type SPA m a = View m a :<|> "calculator" :> View m a -- GitLab From f3d7a82db4b54fef74512e2bdbb03c8edf6e22ab Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Wed, 21 Apr 2021 12:47:04 -0600 Subject: [PATCH 065/116] scroll to top in spa --- router/Shpadoinkle/Router.hs | 6 +++--- website/Shpadoinkle/Website/Types/Routes.hs | 22 ++++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/router/Shpadoinkle/Router.hs b/router/Shpadoinkle/Router.hs index 9a2f2b93..6bf11de7 100644 --- a/router/Shpadoinkle/Router.hs +++ b/router/Shpadoinkle/Router.hs @@ -70,7 +70,8 @@ import GHCJS.DOM.History (pushState) import GHCJS.DOM.Location (getPathname, getSearch) import GHCJS.DOM.PopStateEvent (PopStateEvent) import GHCJS.DOM.Types (JSM, MonadJSM, liftJSM) -import GHCJS.DOM.Window (Window, getHistory, getLocation) +import GHCJS.DOM.Window (Window, getHistory, getLocation, + scrollTo) import Language.Javascript.JSaddle (fromJSVal, jsg) #ifndef ghcjs_HOST_OS import Servant.API (Accept (contentTypes), Capture, @@ -339,8 +340,7 @@ listenStateChange router handle = do liftIO $ takeMVar syncRoute getRoute w router $ maybe (return ()) handle syncPoint - () <- liftIO $ return () - return () + scrollTo w 0 0 return () diff --git a/website/Shpadoinkle/Website/Types/Routes.hs b/website/Shpadoinkle/Website/Types/Routes.hs index 0827fab5..16494c75 100644 --- a/website/Shpadoinkle/Website/Types/Routes.hs +++ b/website/Shpadoinkle/Website/Types/Routes.hs @@ -48,24 +48,24 @@ data Route type SPA m - = View' m - :<|> "docs" :> "index.html" :> View' m + = View' m + :<|> "docs" :> "index.html" :> View' m - :<|> "concepts" :> View' m - :<|> "docs" :> "concepts.html" :> View' m + :<|> "concepts" :> View' m + :<|> "docs" :> "concepts.html" :> View' m :<|> "getting-started" :> GettingStarted.SPA m TopModel :<|> "docs" :> "getting-started" :> GettingStarted.LegacySPA m TopModel - :<|> "packages" :> Packages.SPA m TopModel - :<|> "docs" :> "packages" :> Packages.LegacySPA m TopModel + :<|> "packages" :> Packages.SPA m TopModel + :<|> "docs" :> "packages" :> Packages.LegacySPA m TopModel - :<|> "tutorial" :> Tutorial.SPA m TopModel - :<|> "docs" :> "tutorial" :> Tutorial.LegacySPA m TopModel + :<|> "tutorial" :> Tutorial.SPA m TopModel + :<|> "docs" :> "tutorial" :> Tutorial.LegacySPA m TopModel - :<|> "sandbox" :> View' m - :<|> "404" :> View' m - :<|> View' m + :<|> "sandbox" :> View' m + :<|> "404" :> View' m + :<|> View' m routes :: SPA m :>> Route -- GitLab From 115b9191cd85f2e20d3b284ed0f29e33b77e53cc Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Wed, 21 Apr 2021 16:05:10 -0600 Subject: [PATCH 066/116] routes are hyper links when js is disabled --- .../Shpadoinkle/Website/Page/Documentation.hs | 12 +++++------ website/Shpadoinkle/Website/Page/Home.hs | 6 +++--- .../Shpadoinkle/Website/Partials/Footer.hs | 11 +++++----- website/Shpadoinkle/Website/Partials/Nav.hs | 5 +++-- website/Shpadoinkle/Website/Types/Nav.hs | 13 ++++++------ website/Shpadoinkle/Website/Types/Routes.hs | 21 +++++++++++++++++-- website/Shpadoinkle/Website/View.hs | 2 +- 7 files changed, 43 insertions(+), 27 deletions(-) diff --git a/website/Shpadoinkle/Website/Page/Documentation.hs b/website/Shpadoinkle/Website/Page/Documentation.hs index 76e086b1..4fc2d6df 100644 --- a/website/Shpadoinkle/Website/Page/Documentation.hs +++ b/website/Shpadoinkle/Website/Page/Documentation.hs @@ -14,11 +14,10 @@ import Data.Text (Text, pack) import Prelude hiding (div) import Shpadoinkle (MonadJSM) import Shpadoinkle.Html as H -import Shpadoinkle.Router (navigate) import Shpadoinkle.Template.TH (embedAsciidoc) import Shpadoinkle.Website.Style hiding (nav) import Shpadoinkle.Website.Types.Routes (Route (..), - SPA) + goTo) import qualified Shpadoinkle.Website.Types.Routes.GettingStarted as GettingStarted import qualified Shpadoinkle.Website.Types.Routes.Packages as Packages import qualified Shpadoinkle.Website.Types.Routes.Tutorial as Tutorial @@ -64,24 +63,23 @@ expandable . (MonadJSM m, Bounded r, Enum r, Humanize r) => (r -> Route) -> Text -> Html m a expandable toR t = div [ class' side_nav__expand ] - [ div [ class' side_nav__expand__button - , onClickM_ $ navigate @(SPA m) (toR $ minBound @r) + [ a [ class' side_nav__expand__button + , goTo (toR $ minBound @r) ] [ text t , H.span [ class' side_nav__expand__icon ] [ ] ] - , div [ class' side_nav__expand ] [ ul [ class' side_nav__expand__list ] $ sideNavLink toR <$> [ succ minBound .. maxBound @r ] ] ] -sideNavLink :: forall r m a. MonadJSM m => Humanize r => (r -> Route) -> r -> Html m a +sideNavLink :: MonadJSM m => Humanize r => (r -> Route) -> r -> Html m a sideNavLink toR r = li_ - [ div [ class' side_nav__link, onClickM_ $ navigate @(SPA m) (toR r) ] [ text $ humanize r ] + [ a [ class' side_nav__link, goTo (toR r) ] [ text $ humanize r ] ] diff --git a/website/Shpadoinkle/Website/Page/Home.hs b/website/Shpadoinkle/Website/Page/Home.hs index 20038626..a162fc34 100644 --- a/website/Shpadoinkle/Website/Page/Home.hs +++ b/website/Shpadoinkle/Website/Page/Home.hs @@ -32,9 +32,9 @@ import Shpadoinkle.Website.Types.Home (ExampleLens, Examples, Home) import Shpadoinkle.Website.Types.Nav (Nav (NGettingStarted), - goTo) + toRoute) import Shpadoinkle.Website.Types.Routes (Route (RGettingStarted), - SPA) + SPA, goTo) import Shpadoinkle.Website.Types.Routes.GettingStarted (Route (RGSIndex)) @@ -56,7 +56,7 @@ hero' = div_ ] , div [ class' hero_button ] [ div [ class' $ split_button <> hero_button__content ] - [ button [ class' split_button__button, type' "button", onClickM_ $ goTo NGettingStarted ] + [ button [ class' split_button__button, type' "button", goTo $ toRoute NGettingStarted ] [ span [ class' $ split_button__split1 <> hero_button__split ] [ "Get Started" ] , span [ class' $ split_button__split2 <> hero_button__split ] diff --git a/website/Shpadoinkle/Website/Partials/Footer.hs b/website/Shpadoinkle/Website/Partials/Footer.hs index 27f278d5..ac4cb570 100644 --- a/website/Shpadoinkle/Website/Partials/Footer.hs +++ b/website/Shpadoinkle/Website/Partials/Footer.hs @@ -10,18 +10,19 @@ import Shpadoinkle (MonadJSM) import Shpadoinkle.Html (Html, a, alt, class', div, div_, footer_, href, img', li_, nav_, - newTab, onClickM_, p, - src, text, ul) + newTab, p, src, text, + ul) import Shpadoinkle.Html.TH.AssetLink (assetLink) import Shpadoinkle.Website.Style as Style import Shpadoinkle.Website.Types.CurrentYear (CurrentYear) -import Shpadoinkle.Website.Types.Nav (Nav (NHome), goTo) +import Shpadoinkle.Website.Types.Nav (Nav (NHome), toRoute) +import Shpadoinkle.Website.Types.Routes (goTo) import Shpadoinkle.Widgets.Types (humanize) footerLink :: MonadJSM m => Nav -> Html m a footerLink n = li_ - [ a [ onClickM_ $ goTo n ] + [ a [ goTo $ toRoute n ] [ text $ humanize n ] ] @@ -30,7 +31,7 @@ view :: MonadJSM m => CurrentYear -> Html m a view cy = footer_ [ div [ class' footer__wrapper ] - [ a [ onClickM_ $ goTo NHome ] + [ a [ goTo $ toRoute NHome ] [ img' [ alt "shpadoinkle logo", src $(assetLink "/assets/try_shpadoinkle_footer_logo.svg") ] ] , div [ class' $ flex <> flex_col <> justify_between <> w_full <> gap_8 <> pt_12 <> leading_8 <> "md:flex-row" <> "md:w-2/5" <> "md:gap-0" ] diff --git a/website/Shpadoinkle/Website/Partials/Nav.hs b/website/Shpadoinkle/Website/Partials/Nav.hs index b4448c5a..65e68b8a 100644 --- a/website/Shpadoinkle/Website/Partials/Nav.hs +++ b/website/Shpadoinkle/Website/Partials/Nav.hs @@ -13,6 +13,7 @@ import Shpadoinkle.Html.TH.AssetLink import Shpadoinkle.Website.Style as Style import Shpadoinkle.Website.Types.Frontend import Shpadoinkle.Website.Types.Nav +import Shpadoinkle.Website.Types.Routes (goTo) import Shpadoinkle.Widgets.Types import Shpadoinkle.Widgets.Types.Physical (Toggle (..)) @@ -28,7 +29,7 @@ toActive = \case navLinkDesktop :: MonadJSM m => Frontend -> Nav -> Html m a navLinkDesktop fe n = li [ class' nav__link ] $ - a [ onClickM_ $ goTo n ] [ text $ humanize n ] + a [ goTo $ toRoute n ] [ text $ humanize n ] : [ img' [ class' nav__link_underline, src $(assetLink "/assets/nav_line.svg") ] | toActive fe == Just n ] @@ -39,7 +40,7 @@ navLinkMobile n = a [ class' nav_mobile__link, href "#" ] [ text $ humanize n ] view :: MonadJSM m => Toggle -> Frontend -> [Html m Toggle] view t r = pure $ div [ class' started_header ] [ H.nav [ class' Style.nav ] - [ a [ href "/" ] + [ a [ goTo $ toRoute NHome ] [ img' [ class' nav__logo, src $(assetLink "/assets/landing_logo.svg") ] ] , img' [ class' nav__menu_icon, onClick toggle, src $ case t of diff --git a/website/Shpadoinkle/Website/Types/Nav.hs b/website/Shpadoinkle/Website/Types/Nav.hs index aaaab15a..84056776 100644 --- a/website/Shpadoinkle/Website/Types/Nav.hs +++ b/website/Shpadoinkle/Website/Types/Nav.hs @@ -1,16 +1,13 @@ +{-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeApplications #-} module Shpadoinkle.Website.Types.Nav where -import Shpadoinkle (MonadJSM) -import Shpadoinkle.Router (navigate) -import Shpadoinkle.Website.Types.Routes (Route (RGettingStarted, RHome, RSandbox, RTutorial), - SPA) +import Shpadoinkle.Website.Types.Routes as R (Route (RGettingStarted, RHome, RSandbox, RTutorial)) import Shpadoinkle.Website.Types.Routes.GettingStarted (Route (RGSIndex)) import Shpadoinkle.Website.Types.Routes.Tutorial (Route (RTIndex)) import Shpadoinkle.Widgets.Types (Humanize (..)) @@ -32,9 +29,11 @@ instance Humanize Nav where NSandbox -> "Sandbox" -goTo :: forall m. MonadJSM m => Nav -> m () -goTo = navigate @(SPA m) . \case +toRoute :: Nav -> R.Route +toRoute = \case NHome -> RHome NGettingStarted -> RGettingStarted RGSIndex NTutorial -> RTutorial RTIndex NSandbox -> RSandbox + + diff --git a/website/Shpadoinkle/Website/Types/Routes.hs b/website/Shpadoinkle/Website/Types/Routes.hs index 16494c75..cdfc21e4 100644 --- a/website/Shpadoinkle/Website/Types/Routes.hs +++ b/website/Shpadoinkle/Website/Types/Routes.hs @@ -1,13 +1,17 @@ {-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeOperators #-} +{-# OPTIONS_GHC -fno-warn-unused-imports #-} +{-# OPTIONS_GHC -fno-warn-redundant-constraints #-} module Shpadoinkle.Website.Types.Routes where @@ -16,14 +20,19 @@ module Shpadoinkle.Website.Types.Routes where import Data.Aeson (FromJSON, ToJSON) import Data.Proxy (Proxy (Proxy)) +import Data.Text (Text, pack) import GHC.Generics (Generic) import Servant.API (type (:<|>) (..), type (:>)) -import Shpadoinkle (NFData) +import Shpadoinkle (MonadJSM, + NFData, Prop) +import Shpadoinkle.Html (href, + onClickM_) import Shpadoinkle.Router (Redirect (..), Routed (..), - View, + View, getURI, mapUnions, + navigate, type (:>>)) import Shpadoinkle.Website.Types.Frontend (Frontend) import qualified Shpadoinkle.Website.Types.Routes.GettingStarted as GettingStarted @@ -136,3 +145,11 @@ instance Routed (SPA m) Route where RFourOhFour -> Redirect (Proxy @("404" :> View' m)) id + + +goTo :: forall m a. MonadJSM m => Route -> (Text, Prop m a) +#ifndef ghcjs_HOST_OS +goTo = href . ("/" <>) . pack . show . getURI @(SPA m) +#else +goTo = onClickM_ . navigate @(SPA m) +#endif diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index 241b9d8c..e7346e45 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -9,7 +9,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-} - +{-# OPTIONS_GHC -fno-warn-redundant-constraints #-} module Shpadoinkle.Website.View where -- GitLab From 32a2889d955b7615ef57b36faa5b379a72157e5f Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Wed, 21 Apr 2021 20:09:59 -0600 Subject: [PATCH 067/116] fix mobile toggle logic to hide everything if menu is open --- website/Shpadoinkle/Website/Partials/Nav.hs | 7 +++---- website/Shpadoinkle/Website/Partials/Template.hs | 9 +++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/website/Shpadoinkle/Website/Partials/Nav.hs b/website/Shpadoinkle/Website/Partials/Nav.hs index 65e68b8a..830dc1c4 100644 --- a/website/Shpadoinkle/Website/Partials/Nav.hs +++ b/website/Shpadoinkle/Website/Partials/Nav.hs @@ -38,7 +38,7 @@ navLinkMobile n = a [ class' nav_mobile__link, href "#" ] [ text $ humanize n ] view :: MonadJSM m => Toggle -> Frontend -> [Html m Toggle] -view t r = pure $ div [ class' started_header ] +view t r = pure $ div [ class' started_header ] $ [ H.nav [ class' Style.nav ] [ a [ goTo $ toRoute NHome ] [ img' [ class' nav__logo, src $(assetLink "/assets/landing_logo.svg") ] @@ -49,8 +49,7 @@ view t r = pure $ div [ class' started_header ] ] , ul [ class' nav__links ] $ navLinkDesktop r <$> [minBound..maxBound] ] - , H.nav [ class' nav_mobile ] $ case t of + ] <> case t of Closed _ -> [] - Open -> [ div [ class' nav_mobile__wrapper ] $ navLinkMobile <$> [minBound..maxBound] + Open -> [ H.nav [ class' nav_mobile ] [ div [ class' nav_mobile__wrapper ] $ navLinkMobile <$> [minBound..maxBound] ] ] - ] diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index 42e14432..6a16a5c4 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -78,7 +78,8 @@ template ev r fe content' = html [ lang "en" ] wrapper :: MonadJSM m => CurrentYear -> Toggle -> Frontend -> Html m a -> [Html m (Toggle, a)] -wrapper yc t fe content' = (generalize _1 <$> Nav.view t fe) <> (generalize _2 <$> - [ content' - , Footer.view yc - ]) +wrapper yc t fe content' + = (generalize _1 <$> Nav.view t fe) + <> (generalize _2 + <$> case t of Open -> [] + Closed _ ->[ content', Footer.view yc ]) -- GitLab From ea7efaa7ae2e2454c6b8596c0ee6fa1c5f6aa790 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Wed, 21 Apr 2021 22:41:50 -0600 Subject: [PATCH 068/116] simple little animation --- html/Shpadoinkle/Html/Property.hs | 22 ++++++------ website/Shpadoinkle/Website/Page/Home.hs | 4 +-- .../Shpadoinkle/Website/Partials/Template.hs | 1 + website/assets/animate.js | 36 +++++++++++++++++++ website/assets/style.css | 11 ++++++ 5 files changed, 62 insertions(+), 12 deletions(-) create mode 100644 website/assets/animate.js diff --git a/html/Shpadoinkle/Html/Property.hs b/html/Shpadoinkle/Html/Property.hs index ae6a7bf3..c4962792 100644 --- a/html/Shpadoinkle/Html/Property.hs +++ b/html/Shpadoinkle/Html/Property.hs @@ -37,11 +37,11 @@ import Shpadoinkle.Html.TH -- | How do we take a non-textual value, and make it text which JavaScript will -- cast appropriately? -class ToPropText a where toPropText :: a -> Text -instance ToPropText Text where toPropText = id -instance ToPropText Int where toPropText = pack . show +class ToPropText a where toPropText :: a -> Text +instance ToPropText Text where toPropText = id +instance ToPropText Int where toPropText = pack . show instance ToPropText Float where toPropText = pack . show -instance ToPropText Bool where toPropText = \case True -> "true"; False -> "false" +instance ToPropText Bool where toPropText = \case True -> "true"; False -> "false" textProperty :: ToPropText a => Text -> a -> (Text, Prop m b) @@ -53,15 +53,17 @@ textProperty' k = (,) k . textProp newtype ClassList = ClassList { unClassList :: Set.Set Text } deriving (Eq, Ord, Show, Semigroup, Monoid) -class ClassListRep a where asClass :: a -> ClassList -instance ClassListRep Text where asClass = ClassList . Set.fromList . split (== ' ') -instance ClassListRep ClassList where asClass = id -instance ClassListRep (Text, Bool) where asClass (a, b) = if b then asClass a else mempty -instance ClassListRep (ClassList, Bool) where asClass = \case (cl, True) -> cl; _ -> mempty -instance ClassListRep cl => ClassListRep [cl] where asClass = foldMap asClass instance IsString ClassList where fromString = ClassList . Set.fromList . split (== ' ') . pack +class ClassListRep a where asClass :: a -> ClassList +instance ClassListRep Text where asClass = ClassList . Set.fromList . split (== ' ') +instance ClassListRep ClassList where asClass = id +instance ClassListRep (Text, Bool) where asClass (a, b) = if b then asClass a else mempty +instance ClassListRep (ClassList, Bool) where asClass = \case (cl, True) -> cl; _ -> mempty +instance ClassListRep cl => ClassListRep [cl] where asClass = foldMap asClass + + flagProperty :: Text -> Bool -> (Text, Prop m a) flagProperty t = (,) t . flagProp diff --git a/website/Shpadoinkle/Website/Page/Home.hs b/website/Shpadoinkle/Website/Page/Home.hs index a162fc34..51c6c878 100644 --- a/website/Shpadoinkle/Website/Page/Home.hs +++ b/website/Shpadoinkle/Website/Page/Home.hs @@ -66,8 +66,8 @@ hero' = div_ ] ] ] - , img' [ alt "desert cactus", class' hero_bg, src $(assetLink "/assets/hero_image.svg") ] - , img' [ alt "desert cactus", class' hero_bg_mobile, src $(assetLink "/assets/mobile/hero_image.svg") ] + , img' [ width 2112, height 996, alt "desert cactus", class' hero_bg, src $(assetLink "/assets/hero_image.svg") ] + , img' [ width 322, height 379, alt "desert cactus", class' hero_bg_mobile, src $(assetLink "/assets/mobile/hero_image.svg") ] ] diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index 6a16a5c4..8a4f497c 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -60,6 +60,7 @@ headView ev r fe = head_ $ , script [ src $ entrypoint ev ] [] , script [ src $ codemirrorCDN "codemirror.min.js" ] [] , script [ src $ codemirrorCDN "mode/haskell/haskell.min.js" ] [] + , script [ src $(assetLink "/assets/animate.js") ] [] , link' [ rel "canonical" , href . pack . show diff --git a/website/assets/animate.js b/website/assets/animate.js new file mode 100644 index 00000000..a287b8eb --- /dev/null +++ b/website/assets/animate.js @@ -0,0 +1,36 @@ +const animClasses = + [ ".feature__card" + , ".feature-button" + , ".feature__title" + , ".footer__wrapper img" + , ".component__info" + , ".component__example" + , ".footer__nav__category" + , ".footer__nav__links li" + , ".hero-title" + , ".hero-button" + ] + + +const observer = new IntersectionObserver((entries, _) => + entries.forEach(entry => entry.target.classList.toggle("animate-show", entry.isIntersecting)), + { root: null, rootMargin: '0px', threshold: 0.3 }) + + +const init = () => animClasses.forEach(x => { + for (elm of document.querySelectorAll(x)){ + elm.classList.add("animate") + observer.observe(elm) + } +}) + + +window.history.pushState = new Proxy(window.history.pushState, { + apply: (target, thisArg, argArray) => { + setTimeout(init,300) + return target.apply(thisArg, argArray) + } +}) + + +setTimeout(init, 0) diff --git a/website/assets/style.css b/website/assets/style.css index 02805edd..188b3a7f 100644 --- a/website/assets/style.css +++ b/website/assets/style.css @@ -20,6 +20,15 @@ -o-tab-size: 4; tab-size: 4; } +.animate{ + opacity: 0; + transform: translateY(20px); + transition: all ease-out 0.5s; +} +.animate-show{ + opacity: 1; + transform: translateY(0); +} html { line-height: 1.15; -webkit-text-size-adjust: 100%; @@ -1490,6 +1499,8 @@ video { } } + + } } } -- GitLab From 2cc8a6fca47ed2fac451326218ce61c84c00ab45 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 22 Apr 2021 12:53:36 -0600 Subject: [PATCH 069/116] fix broken css --- .gitignore | 1 + nix/base.nix | 2 +- website/assets/style.css | 2362 +++++++++++++----------------- website/default.nix | 9 +- website/docs/concepts.adoc | 2 - website/docs/index.adoc | 35 - website/docs/packages/core.adoc | 2 - website/docs/packages/index.adoc | 2 - 8 files changed, 1004 insertions(+), 1411 deletions(-) delete mode 100644 website/docs/index.adoc diff --git a/.gitignore b/.gitignore index 4a5d4d49..9df25952 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ roster.db tags /website/favicon.html /website/assets/landing_logo.svg +.cache diff --git a/nix/base.nix b/nix/base.nix index b4709f8a..f6206ad6 100644 --- a/nix/base.nix +++ b/nix/base.nix @@ -92,7 +92,7 @@ let inherit withHoogle; packages = _: if pack == "all" then attrValues packages else [ packages.${pack} ]; COMPILER = util.compilerjs; - buildInputs = ghcTools ++ [ asciidoctor ack util.cannibalize nixops ]; + buildInputs = ghcTools ++ [ asciidoctor ack util.cannibalize nixops nodePackages.parcel-bundler ]; shellHook = '' cat ${../etc/figlet} ''; diff --git a/website/assets/style.css b/website/assets/style.css index 188b3a7f..75530f11 100644 --- a/website/assets/style.css +++ b/website/assets/style.css @@ -22,12 +22,13 @@ } .animate{ opacity: 0; - transform: translateY(20px); + transform: translate3d(0, 20px, 0); transition: all ease-out 0.5s; + transition-property: opacity, transform; } .animate-show{ opacity: 1; - transform: translateY(0); + transform: translate3d(0, 0, 0); } html { line-height: 1.15; @@ -391,1481 +392,1108 @@ video { .bottom-0 { bottom: 0; } -* { - * --tw-shadow: 0 0 #0000; - * } - * * { - * --tw-ring-inset: var(--tw-empty); /*!*/ /*!*/ - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgba(59, 130, 246, 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - } - .text-white { - --tw-text-opacity: 1; - color: rgba(255, 255, 255, var(--tw-text-opacity)); - } - .w-full { - width: 100%; - } - .gap-8 { - gap: 2rem; - } - .gap-10 { - gap: 2.5rem; - } - .grid-cols-4 { - grid-template-columns: repeat(4, minmax(0, 1fr)); - } - .transform { - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - transform: translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .scale-90 { - --tw-scale-x: 0.9; - --tw-scale-y: 0.9; - } - .scale-100 { - --tw-scale-x: 1; - --tw-scale-y: 1; - } - .transition { - transition-property: background-color, border-color, color, fill, stroke, - opacity, box-shadow, transform; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; - } - .ease-in { - transition-timing-function: cubic-bezier(0.4, 0, 1, 1); - } - .ease-out { - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - } - @-webkit-keyframes spin { - to { - transform: rotate(360deg); - } - } - @keyframes spin { - to { - transform: rotate(360deg); - } - } - @-webkit-keyframes ping { - 100%, - 75% { - transform: scale(2); - opacity: 0; - } - } - @keyframes ping { - 100%, - 75% { - transform: scale(2); - opacity: 0; - } - } - @-webkit-keyframes pulse { - 50% { - opacity: 0.5; - } - } - @keyframes pulse { - 50% { - opacity: 0.5; - } - } - @-webkit-keyframes bounce { - 0%, - 100% { - transform: translateY(-25%); - -webkit-animation-timing-function: cubic-bezier(0.8, 0, 1, 1); - animation-timing-function: cubic-bezier(0.8, 0, 1, 1); - } - 50% { - transform: none; - -webkit-animation-timing-function: cubic-bezier(0, 0, 0.2, 1); - animation-timing-function: cubic-bezier(0, 0, 0.2, 1); - } - } - @keyframes bounce { - 0%, - 100% { - transform: translateY(-25%); - -webkit-animation-timing-function: cubic-bezier(0.8, 0, 1, 1); - animation-timing-function: cubic-bezier(0.8, 0, 1, 1); - } - 50% { - transform: none; - -webkit-animation-timing-function: cubic-bezier(0, 0, 0.2, 1); - animation-timing-function: cubic-bezier(0, 0, 0.2, 1); - } - } - #preamble > .sectionbody > .paragraph > p { - padding: 1rem 0; - } - .listingblock { - padding: 2rem 0; - overflow-x: scroll; - } - .listingblock > .content > .highlight > code { - border: 2px solid #e2e0d5; - background-color: transparent; - padding: 15px 10px; - } - div.admonitionblock { - position: relative; - margin: 3rem 0; - } - div.admonitionblock > table > tbody > tr > td.icon { - border-radius: 18px; - color: #fff; - display: flex; - height: 30px; - justify-content: flex-start; - left: 5px; - padding: 0 1.5rem; - position: absolute; - top: -15px; - width: 135px; - } - div.admonitionblock > table > tbody > tr > td.content { - padding: 20px 10px 15px 1rem; - } - div.admonitionblock.caution { - border: 2px dashed #ac2a34; +:root { + --tw-shadow: 0 0 #0000; + --tw-ring-inset: var(--tw-empty); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgba(59, 130, 246, 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; +} +.text-white { + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); +} +.w-full { + width: 100%; +} +.gap-8 { + gap: 2rem; +} +.gap-10 { + gap: 2.5rem; +} +.grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} +.transform { + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + transform: translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) + rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) + scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.scale-90 { + --tw-scale-x: 0.9; + --tw-scale-y: 0.9; +} +.scale-100 { + --tw-scale-x: 1; + --tw-scale-y: 1; +} +.transition { + transition-property: background-color, border-color, color, fill, stroke, + opacity, box-shadow, transform; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} +.ease-in { + transition-timing-function: cubic-bezier(0.4, 0, 1, 1); +} +.ease-out { + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); +} +@-webkit-keyframes spin { + to { + transform: rotate(360deg); } - div.admonitionblock.caution > table > tbody > tr > td.icon { - background: #ac2a34; +} +@keyframes spin { + to { + transform: rotate(360deg); } - div.admonitionblock.caution > table > tbody > tr > td.content { - color: #ac2a34; +} +@-webkit-keyframes ping { + 100%, + 75% { + transform: scale(2); + opacity: 0; } - div.admonitionblock.note { - border: 2px dashed #0f406b; +} +@keyframes ping { + 100%, + 75% { + transform: scale(2); + opacity: 0; } - div.admonitionblock.note > table > tbody > tr > td.icon { - background: #0f406b; +} +@-webkit-keyframes pulse { + 50% { + opacity: 0.5; } - div.admonitionblock.note > table > tbody > tr > td.content { - color: #0f406b; +} +@keyframes pulse { + 50% { + opacity: 0.5; } - div.admonitionblock.warning { - border: 2px dashed #e37743; +} +@-webkit-keyframes bounce { + 0%, + 100% { + transform: translateY(-25%); + -webkit-animation-timing-function: cubic-bezier(0.8, 0, 1, 1); + animation-timing-function: cubic-bezier(0.8, 0, 1, 1); } - div.admonitionblock.warning > table > tbody > tr > td.icon { - background: #e37743; + 50% { + transform: none; + -webkit-animation-timing-function: cubic-bezier(0, 0, 0.2, 1); + animation-timing-function: cubic-bezier(0, 0, 0.2, 1); } - div.admonitionblock.warning > table > tbody > tr > td.content { - color: #e37743; +} +@keyframes bounce { + 0%, + 100% { + transform: translateY(-25%); + -webkit-animation-timing-function: cubic-bezier(0.8, 0, 1, 1); + animation-timing-function: cubic-bezier(0.8, 0, 1, 1); } - h1, - h2, - h3, - h4, - h5 { - padding: 1rem 0; + 50% { + transform: none; + -webkit-animation-timing-function: cubic-bezier(0, 0, 0.2, 1); + animation-timing-function: cubic-bezier(0, 0, 0.2, 1); } +} +#preamble > .sectionbody > .paragraph > p { + padding: 1rem 0; +} +.listingblock { + padding: 2rem 0; + overflow-x: scroll; +} +.listingblock > .content > .highlight > code { + border: 2px solid #e2e0d5; + background-color: transparent; + padding: 15px 10px; +} +div.admonitionblock { + position: relative; + margin: 3rem 0; +} +div.admonitionblock > table > tbody > tr > td.icon { + border-radius: 18px; + color: #fff; + display: flex; + height: 30px; + justify-content: flex-start; + left: 5px; + padding: 0 1.5rem; + position: absolute; + top: -15px; + width: 135px; +} +div.admonitionblock > table > tbody > tr > td.content { + padding: 20px 10px 15px 1rem; +} +div.admonitionblock.caution { + border: 2px dashed #ac2a34; +} +div.admonitionblock.caution > table > tbody > tr > td.icon { + background: #ac2a34; +} +div.admonitionblock.caution > table > tbody > tr > td.content { + color: #ac2a34; +} +div.admonitionblock.note { + border: 2px dashed #0f406b; +} +div.admonitionblock.note > table > tbody > tr > td.icon { + background: #0f406b; +} +div.admonitionblock.note > table > tbody > tr > td.content { + color: #0f406b; +} +div.admonitionblock.warning { + border: 2px dashed #e37743; +} +div.admonitionblock.warning > table > tbody > tr > td.icon { + background: #e37743; +} +div.admonitionblock.warning > table > tbody > tr > td.content { + color: #e37743; +} +h1, +h2, +h3, +h4, +h5 { + padding: 1rem 0; +} +h1 { + font-size: 1.875rem; +} +h2 { + font-size: 1.375rem; +} +h3 { + font-size: 1.5rem; +} +@media (min-width: 768px) { h1 { - font-size: 1.875rem; + font-size: 3.75rem; } h2 { - font-size: 1.375rem; + font-size: 2.625rem; } h3 { - font-size: 1.5rem; - } - @media (min-width: 768px) { - h1 { - font-size: 3.75rem; - } - h2 { - font-size: 2.625rem; - } - h3 { - font-size: 1.325rem; - } - } - .split-button:active { - transform: translate(2px, 2px); + font-size: 1.325rem; + } +} +.split-button:active { + transform: translate(2px, 2px); +} +.started-header { + background-image: url(/assets/get_started_header_bg.svg); + height: 96px; + background-position: center; + background-repeat: no-repeat; + background-size: cover; + margin-bottom: 3rem; + position: relative; + z-index: 50; +} +@media (min-width: 768px) { + .started-header { + margin-bottom: -3.5rem; } +} +@media (min-width: 768px) { .started-header { background-image: url(/assets/get_started_header_bg.svg); - height: 96px; - background-position: center; - background-repeat: no-repeat; - background-size: cover; - margin-bottom: 3rem; - position: relative; - z-index: 50; - } - @media (min-width: 768px) { - .started-header { - margin-bottom: -3.5rem; - } - } - @media (min-width: 768px) { - .started-header { - background-image: url(/assets/get_started_header_bg.svg); - height: 156px; - } - } - body { - --tw-bg-opacity: 1; - background-color: rgba(237, 225, 113, var(--tw-bg-opacity)); - --tw-border-opacity: 1; - border-color: rgba(217, 206, 95, var(--tw-border-opacity)); - border-style: solid; - font-family: Adelle, ui-serif; + height: 156px; } +} +body { + --tw-bg-opacity: 1; + background-color: rgba(237, 225, 113, var(--tw-bg-opacity)); + --tw-border-opacity: 1; + border-color: rgba(217, 206, 95, var(--tw-border-opacity)); + border-style: solid; + font-family: Adelle, ui-serif; +} +.nav { + width: 100%; +} +@media (min-width: 640px) { .nav { - width: 100%; - } - @media (min-width: 640px) { - .nav { - max-width: 640px; - } - } - @media (min-width: 768px) { - .nav { - max-width: 768px; - } + max-width: 640px; } - @media (min-width: 1024px) { - .nav { - max-width: 1024px; - } +} +@media (min-width: 768px) { + .nav { + max-width: 768px; } - @media (min-width: 1280px) { - .nav { - max-width: 1280px; - } +} +@media (min-width: 1024px) { + .nav { + max-width: 1024px; } - @media (min-width: 1536px) { - .nav { - max-width: 1536px; - } +} +@media (min-width: 1280px) { + .nav { + max-width: 1280px; } +} +@media (min-width: 1536px) { .nav { - display: flex; - justify-content: space-between; - margin-left: auto; - margin-right: auto; - padding-left: 1rem; - padding-right: 1rem; - padding-top: 2rem; - position: relative; - width: 100%; + max-width: 1536px; } +} +.nav { + display: flex; + justify-content: space-between; + margin-left: auto; + margin-right: auto; + padding-left: 1rem; + padding-right: 1rem; + padding-top: 2rem; + position: relative; + width: 100%; +} +.nav__logo { + width: 75%; +} +@media (min-width: 768px) { .nav__logo { - width: 75%; - } - @media (min-width: 768px) { - .nav__logo { - width: 70%; - } + width: 70%; } +} +.nav__menu-icon { + display: block; + padding-right: 0.75rem; + width: 2.5rem; +} +@media (min-width: 768px) { .nav__menu-icon { - display: block; - padding-right: 0.75rem; - width: 2.5rem; - } - @media (min-width: 768px) { - .nav__menu-icon { - display: none; - } - } - .nav__links { display: none; - position: absolute; - right: 0; - text-transform: uppercase; - gap: 1.25rem; - } - @media (min-width: 768px) { - .nav__links { - display: flex; - } } - .nav__link { +} +.nav__links { + display: none; + position: absolute; + right: 0; + text-transform: uppercase; + gap: 1.25rem; +} +@media (min-width: 768px) { + .nav__links { display: flex; - flex-direction: column; - justify-content: center; - position: relative; - text-align: center; - width: 7rem; - } - .nav__link-underline { - position: absolute; - top: 1.25rem; - } - .nav__link__underline-docs { - padding-top: 0.5rem; - position: absolute; - top: 1.25rem; } +} +.nav__link { + display: flex; + flex-direction: column; + justify-content: center; + position: relative; + text-align: center; + width: 7rem; +} +.nav__link-underline { + position: absolute; + top: 1.25rem; +} +.nav__link__underline-docs { + padding-top: 0.5rem; + position: absolute; + top: 1.25rem; +} +.nav-mobile { + padding-top: 3rem; +} +@media (min-width: 640px) { .nav-mobile { - padding-top: 3rem; - } - @media (min-width: 640px) { - .nav-mobile { - display: none; - } - } - .nav-mobile__wrapper > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); - } - .nav-mobile__wrapper { - padding-top: 0.5rem; - padding-bottom: 1rem; - } - .nav-mobile__link { - border-color: transparent; - display: block; - font-size: 1.875rem; - line-height: 2.25rem; - padding-top: 0.5rem; - padding-bottom: 0.5rem; - padding-right: 1rem; - padding-left: 1rem; - --tw-text-opacity: 1; - color: rgba(18, 18, 18, var(--tw-text-opacity)); - } - .nav-mobile__links > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); - } - .nav-mobile__links { - padding-top: 0.5rem; - padding-bottom: 1rem; - } - .nav-mobile__heading { - --tw-bg-opacity: 1; - background-color: rgba(0, 0, 0, var(--tw-bg-opacity)); - display: flex; - flex-direction: column; - justify-content: flex-end; - height: 5rem; - font-size: 1.5rem; - line-height: 2rem; - padding-bottom: 0.75rem; - padding-left: 1rem; - --tw-text-opacity: 1; - color: rgba(255, 255, 255, var(--tw-text-opacity)); - width: 100%; - } - .nav-mobile__heading__title { - display: flex; - justify-content: space-between; - padding-right: 1rem; + display: none; } +} +.nav-mobile__wrapper > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); +} +.nav-mobile__wrapper { + padding-top: 0.5rem; + padding-bottom: 1rem; +} +.nav-mobile__link { + border-color: transparent; + display: block; + font-size: 1.875rem; + line-height: 2.25rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-right: 1rem; + padding-left: 1rem; + --tw-text-opacity: 1; + color: rgba(18, 18, 18, var(--tw-text-opacity)); +} +.nav-mobile__links > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); +} +.nav-mobile__links { + padding-top: 0.5rem; + padding-bottom: 1rem; +} +.nav-mobile__heading { + --tw-bg-opacity: 1; + background-color: rgba(0, 0, 0, var(--tw-bg-opacity)); + display: flex; + flex-direction: column; + justify-content: flex-end; + height: 5rem; + font-size: 1.5rem; + line-height: 2rem; + padding-bottom: 0.75rem; + padding-left: 1rem; + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); + width: 100%; +} +.nav-mobile__heading__title { + display: flex; + justify-content: space-between; + padding-right: 1rem; +} +.nav-mobile__heading__close { + display: block; + padding-right: 0.75rem; +} +@media (min-width: 768px) { .nav-mobile__heading__close { - display: block; - padding-right: 0.75rem; - } - @media (min-width: 768px) { - .nav-mobile__heading__close { - display: none; - } - } - .nav-mobile__links__level1 { - padding-left: 3rem; - } - .nav-mobile__links__level2 { - padding-left: 1rem; - } - .hero-bg { display: none; - margin-top: -28rem; - width: 100%; - z-index: 50; - position: relative; - } - @media (min-width: 768px) { - .hero-bg { - display: block; - } } - .hero-bg-mobile { +} +.nav-mobile__links__level1 { + padding-left: 3rem; +} +.nav-mobile__links__level2 { + padding-left: 1rem; +} +.hero-bg { + display: none; + margin-top: -28rem; + width: 100%; + z-index: 50; + position: relative; +} +@media (min-width: 768px) { + .hero-bg { display: block; - margin-top: -16rem; - width: 100%; } - @media (min-width: 768px) { - .hero-bg-mobile { - display: none; - } +} +.hero-bg-mobile { + display: block; + margin-top: -16rem; + width: 100%; +} +@media (min-width: 768px) { + .hero-bg-mobile { + display: none; } +} +.hero { + width: 100%; +} +@media (min-width: 640px) { .hero { - width: 100%; - } - @media (min-width: 640px) { - .hero { - max-width: 640px; - } - } - @media (min-width: 768px) { - .hero { - max-width: 768px; - } - } - @media (min-width: 1024px) { - .hero { - max-width: 1024px; - } + max-width: 640px; } - @media (min-width: 1280px) { - .hero { - max-width: 1280px; - } +} +@media (min-width: 768px) { + .hero { + max-width: 768px; } - @media (min-width: 1536px) { - .hero { - max-width: 1536px; - } +} +@media (min-width: 1024px) { + .hero { + max-width: 1024px; } +} +@media (min-width: 1280px) { .hero { - position: relative; - z-index: 60; - margin-left: auto; - margin-right: auto; - margin-top: 3rem; - padding-left: 1rem; - padding-right: 1rem; - padding-bottom: 6rem; + max-width: 1280px; } - @media (min-width: 768px) { - .hero { - margin-top: 6rem; - } +} +@media (min-width: 1536px) { + .hero { + max-width: 1536px; } - .hero-title-wrapper { - justify-content: space-between; - position: relative; +} +.hero { + position: relative; + z-index: 60; + margin-left: auto; + margin-right: auto; + margin-top: 3rem; + padding-left: 1rem; + padding-right: 1rem; + padding-bottom: 6rem; +} +@media (min-width: 768px) { + .hero { + margin-top: 6rem; } +} +.hero-title-wrapper { + justify-content: space-between; + position: relative; +} +.hero-title { + font-size: 1.875rem; + line-height: 2.25rem; +} +@media (min-width: 768px) { .hero-title { - font-size: 1.875rem; - line-height: 2.25rem; - } - @media (min-width: 768px) { - .hero-title { - font-size: 2.25rem; - line-height: 2.5rem; - line-height: 1.625; - } - } - @media (min-width: 1024px) { - .hero-title { - font-size: 3rem; - line-height: 1; - } - } - @media (min-width: 1440px) { - .hero-title { - font-size: 5rem; - } - } - @media (min-width: 1800px) { - .hero-title { - font-size: 6rem; - } - } - .hero-button { - display: flex; - margin-bottom: 3rem; - padding-top: 1.25rem; + font-size: 2.25rem; + line-height: 2.5rem; + line-height: 1.625; } - @media (min-width: 768px) { - .hero-button { - padding-top: 2.5rem; - } +} +@media (min-width: 1024px) { + .hero-title { + font-size: 3rem; + line-height: 1; } - .hero-button__content { - --tw-border-opacity: 1; - border-color: rgba(210, 199, 91, var(--tw-border-opacity)); - width: 13rem; +} +@media (min-width: 1440px) { + .hero-title { + font-size: 5rem; } - .hero-button__split { - --tw-bg-opacity: 1; - background-color: rgba(18, 18, 18, var(--tw-bg-opacity)); - --tw-text-opacity: 1; - color: rgba(255, 255, 255, var(--tw-text-opacity)); +} +@media (min-width: 1800px) { + .hero-title { + font-size: 6rem; } - .feature-button { - display: none; - justify-content: center; - margin-bottom: 3rem; - } - @media (min-width: 768px) { - .feature-button { - display: flex; - padding-top: 1.5rem; - } - } - .feature-button__content { - --tw-border-opacity: 1; - border-color: rgba(196, 91, 40, var(--tw-border-opacity)); - width: 24rem; - } - .feature-button__split { - --tw-bg-opacity: 1; - background-color: rgba(241, 226, 85, var(--tw-bg-opacity)); - --tw-text-opacity: 1; - color: rgba(0, 0, 0, var(--tw-text-opacity)); - } - .split-button { - background-color: transparent; - border-radius: 0.75rem; - border-style: solid; - border-width: 2px; - display: flex; - height: 3rem; - position: relative; +} +.hero-button { + display: flex; + margin-bottom: 3rem; + padding-top: 1.25rem; +} +@media (min-width: 768px) { + .hero-button { + padding-top: 2.5rem; } - .split-button__button { +} +.hero-button__content { + --tw-border-opacity: 1; + border-color: rgba(210, 199, 91, var(--tw-border-opacity)); + width: 13rem; +} +.hero-button__split { + --tw-bg-opacity: 1; + background-color: rgba(18, 18, 18, var(--tw-bg-opacity)); + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); +} +.feature-button { + display: none; + justify-content: center; + margin-bottom: 3rem; +} +@media (min-width: 768px) { + .feature-button { display: flex; - position: absolute; - left: -0.5rem; - top: -0.75rem; - } - .split-button__split1 { - border-radius: 0.75rem; - display: inline-flex; - align-items: center; - font-weight: 500; - height: 3rem; - margin-right: 0.5rem; - padding-top: 0.75rem; - padding-bottom: 0.75rem; - padding-left: 1.5rem; - padding-right: 1.5rem; - } - .split-button__split2 { - border-radius: 0.75rem; - display: inline-flex; - align-items: center; - font-weight: 500; - height: 3rem; - font-size: 1.125rem; - line-height: 1.75rem; - padding-top: 0.75rem; - padding-bottom: 0.75rem; - padding-left: 0.75rem; - padding-right: 0.75rem; - --tw-text-opacity: 1; - color: rgba(0, 0, 0, var(--tw-text-opacity)); - } - .transition { - width: 100%; - } - .feature__section { - width: full; - margin-top: -0.5rem; - background-color: #e37743; + padding-top: 1.5rem; } +} +.feature-button__content { + --tw-border-opacity: 1; + border-color: rgba(196, 91, 40, var(--tw-border-opacity)); + width: 24rem; +} +.feature-button__split { + --tw-bg-opacity: 1; + background-color: rgba(241, 226, 85, var(--tw-bg-opacity)); + --tw-text-opacity: 1; + color: rgba(0, 0, 0, var(--tw-text-opacity)); +} +.split-button { + background-color: transparent; + border-radius: 0.75rem; + border-style: solid; + border-width: 2px; + display: flex; + height: 3rem; + position: relative; +} +.split-button__button { + display: flex; + position: absolute; + left: -0.5rem; + top: -0.75rem; +} +.split-button__split1 { + border-radius: 0.75rem; + display: inline-flex; + align-items: center; + font-weight: 500; + height: 3rem; + margin-right: 0.5rem; + padding-top: 0.75rem; + padding-bottom: 0.75rem; + padding-left: 1.5rem; + padding-right: 1.5rem; +} +.split-button__split2 { + border-radius: 0.75rem; + display: inline-flex; + align-items: center; + font-weight: 500; + height: 3rem; + font-size: 1.125rem; + line-height: 1.75rem; + padding-top: 0.75rem; + padding-bottom: 0.75rem; + padding-left: 0.75rem; + padding-right: 0.75rem; + --tw-text-opacity: 1; + color: rgba(0, 0, 0, var(--tw-text-opacity)); +} +.transition { + width: 100%; +} +.feature__section { + width: full; + margin-top: -0.5rem; + background-color: #e37743; +} +.feature__wrapper { + width: 100%; +} +@media (min-width: 640px) { .feature__wrapper { - width: 100%; - } - @media (min-width: 640px) { - .feature__wrapper { - max-width: 640px; - } - } - @media (min-width: 768px) { - .feature__wrapper { - max-width: 768px; - } - } - @media (min-width: 1024px) { - .feature__wrapper { - max-width: 1024px; - } + max-width: 640px; } - @media (min-width: 1280px) { - .feature__wrapper { - max-width: 1280px; - } +} +@media (min-width: 768px) { + .feature__wrapper { + max-width: 768px; } - @media (min-width: 1536px) { - .feature__wrapper { - max-width: 1536px; - } +} +@media (min-width: 1024px) { + .feature__wrapper { + max-width: 1024px; } +} +@media (min-width: 1280px) { .feature__wrapper { - margin-left: auto; - margin-right: auto; - padding-left: 0; - padding-right: 0; - padding-top: 3rem; + max-width: 1280px; } - @media (min-width: 768px) { - .feature__wrapper { - padding-left: 1rem; - padding-right: 1rem; - } +} +@media (min-width: 1536px) { + .feature__wrapper { + max-width: 1536px; } - .feature__title { - font-size: 1.875rem; - line-height: 2.25rem; - margin-left: auto; - margin-right: auto; +} +.feature__wrapper { + margin-left: auto; + margin-right: auto; + padding-left: 0; + padding-right: 0; + padding-top: 3rem; +} +@media (min-width: 768px) { + .feature__wrapper { padding-left: 1rem; padding-right: 1rem; - width: 100%; - } - @media (min-width: 768px) { - .feature__title { - font-size: 2.25rem; - line-height: 2.5rem; - padding-left: 0; - padding-right: 0; - text-align: center; - width: 60%; - } - } - .feature__cards { - display: flex; - flex-wrap: wrap; - justify-content: center; - padding-top: 3rem; } - .feature__card { - display: flex; - justify-content: center; - padding-bottom: 1.5rem; - width: 100%; +} +.feature__title { + font-size: 1.875rem; + line-height: 2.25rem; + margin-left: auto; + margin-right: auto; + padding-left: 1rem; + padding-right: 1rem; + width: 100%; +} +@media (min-width: 768px) { + .feature__title { + font-size: 2.25rem; + line-height: 2.5rem; + padding-left: 0; + padding-right: 0; + text-align: center; + width: 60%; } - @media (min-width: 768px) { - .feature__card { - margin-bottom: 3.5rem; - width: 50%; - } +} +.feature__cards { + display: flex; + flex-wrap: wrap; + justify-content: center; + padding-top: 3rem; +} +.feature__card { + display: flex; + justify-content: center; + padding-bottom: 1.5rem; + width: 100%; +} +@media (min-width: 768px) { + .feature__card { + margin-bottom: 3.5rem; + width: 50%; } - @media (min-width: 1024px) { - .feature__card { - width: 33.333333%; - } +} +@media (min-width: 1024px) { + .feature__card { + width: 33.333333%; } +} +.feature__card__content { + --tw-bg-opacity: 1; + background-color: rgba(208, 79, 34, var(--tw-bg-opacity)); + border-radius: 0; + padding-top: 1rem; + padding-bottom: 1rem; + padding-left: 2rem; + padding-right: 2rem; + width: 100%; +} +@media (min-width: 768px) { .feature__card__content { - --tw-bg-opacity: 1; - background-color: rgba(208, 79, 34, var(--tw-bg-opacity)); - border-radius: 0; - padding-top: 1rem; - padding-bottom: 1rem; - padding-left: 2rem; - padding-right: 2rem; - width: 100%; - } - @media (min-width: 768px) { - .feature__card__content { - border-radius: 1.5rem; - height: 20rem; - width: 20rem; - } - } - .feature__card__heading { - display: inline-flex; - align-items: center; - padding-bottom: 1.25rem; - } - .feature__card__title { - font-size: 1.875rem; - line-height: 2.25rem; - margin-left: 1rem; - margin-bottom: -0.5rem; - } - .feature__card__description { - font-size: 1.125rem; - line-height: 1.75rem; - } - .components__section { - --tw-bg-opacity: 1; - background-color: rgba(58, 52, 83, var(--tw-bg-opacity)); - flex-direction: column; - align-items: center; - margin-top: -0.5rem; - padding-top: 3rem; - --tw-text-opacity: 1; - color: rgba(255, 255, 255, var(--tw-text-opacity)); - width: 100%; + border-radius: 1.5rem; + height: 20rem; + width: 20rem; } +} +.feature__card__heading { + display: inline-flex; + align-items: center; + padding-bottom: 1.25rem; +} +.feature__card__title { + font-size: 1.875rem; + line-height: 2.25rem; + margin-left: 1rem; + margin-bottom: -0.5rem; +} +.feature__card__description { + font-size: 1.125rem; + line-height: 1.75rem; +} +.components__section { + --tw-bg-opacity: 1; + background-color: rgba(58, 52, 83, var(--tw-bg-opacity)); + flex-direction: column; + align-items: center; + margin-top: -0.5rem; + padding-top: 3rem; + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); + width: 100%; +} +.component { + width: 100%; +} +@media (min-width: 640px) { .component { - width: 100%; - } - @media (min-width: 640px) { - .component { - max-width: 640px; - } - } - @media (min-width: 768px) { - .component { - max-width: 768px; - } + max-width: 640px; } - @media (min-width: 1024px) { - .component { - max-width: 1024px; - } +} +@media (min-width: 768px) { + .component { + max-width: 768px; } - @media (min-width: 1280px) { - .component { - max-width: 1280px; - } +} +@media (min-width: 1024px) { + .component { + max-width: 1024px; } - @media (min-width: 1536px) { - .component { - max-width: 1536px; - } +} +@media (min-width: 1280px) { + .component { + max-width: 1280px; } +} +@media (min-width: 1536px) { .component { - display: flex; - flex-wrap: wrap; - align-items: center; - margin-left: auto; - margin-right: auto; - margin-bottom: 3rem; - padding-left: 1rem; - padding-right: 1rem; + max-width: 1536px; } +} +.component { + display: flex; + flex-wrap: wrap; + align-items: center; + margin-left: auto; + margin-right: auto; + margin-bottom: 3rem; + padding-left: 1rem; + padding-right: 1rem; +} +.component__info { + padding-right: 0; + padding-bottom: 1rem; + width: 100%; +} +@media (min-width: 1024px) { .component__info { - padding-right: 0; - padding-bottom: 1rem; - width: 100%; - } - @media (min-width: 1024px) { - .component__info { - padding-bottom: 0; - padding-right: 2rem; - width: 33.333333%; - } - } - .component__info__title { - padding-bottom: 0.75rem; + padding-bottom: 0; + padding-right: 2rem; + width: 33.333333%; } +} +.component__info__title { + padding-bottom: 0.75rem; +} +.component__example { + width: 100%; +} +@media (min-width: 1024px) { .component__example { - width: 100%; - } - @media (min-width: 1024px) { - .component__example { - width: 66.666667%; - } - } - .component__example__code { - --tw-bg-opacity: 1; - background-color: rgba(255, 255, 255, var(--tw-bg-opacity)); - border-radius: 0.75rem; - height: 12rem; - } - footer { - --tw-bg-opacity: 1; - background-color: rgba(0, 0, 0, var(--tw-bg-opacity)); - margin-top: -0.5rem; + width: 66.666667%; } +} +.component__example__code { + --tw-bg-opacity: 1; + background-color: rgba(255, 255, 255, var(--tw-bg-opacity)); + border-radius: 0.75rem; + height: 12rem; +} +footer { + --tw-bg-opacity: 1; + background-color: rgba(0, 0, 0, var(--tw-bg-opacity)); + margin-top: -0.5rem; +} +.footer__wrapper { + width: 100%; +} +@media (min-width: 640px) { .footer__wrapper { - width: 100%; - } - @media (min-width: 640px) { - .footer__wrapper { - max-width: 640px; - } - } - @media (min-width: 768px) { - .footer__wrapper { - max-width: 768px; - } + max-width: 640px; } - @media (min-width: 1024px) { - .footer__wrapper { - max-width: 1024px; - } +} +@media (min-width: 768px) { + .footer__wrapper { + max-width: 768px; } - @media (min-width: 1280px) { - .footer__wrapper { - max-width: 1280px; - } +} +@media (min-width: 1024px) { + .footer__wrapper { + max-width: 1024px; } - @media (min-width: 1536px) { - .footer__wrapper { - max-width: 1536px; - } +} +@media (min-width: 1280px) { + .footer__wrapper { + max-width: 1280px; } +} +@media (min-width: 1536px) { .footer__wrapper { - margin-left: auto; - margin-right: auto; - padding-left: 1rem; - padding-right: 1rem; + max-width: 1536px; } +} +.footer__wrapper { + margin-left: auto; + margin-right: auto; + padding-left: 1rem; + padding-right: 1rem; +} +.footer__nav { + display: flex; + flex-direction: column; + justify-content: space-between; + line-height: 2rem; + padding-top: 3rem; + width: 100%; + gap: 2rem; +} +@media (min-width: 768px) { .footer__nav { - display: flex; - flex-direction: column; - justify-content: space-between; - line-height: 2rem; - padding-top: 3rem; - width: 100%; - gap: 2rem; - } - @media (min-width: 768px) { - .footer__nav { - flex-direction: row; - width: 40%; - gap: 0; - } - } - .footer__nav__category { - --tw-text-opacity: 1; - color: rgba(91, 56, 233, var(--tw-text-opacity)); - } - .footer__nav__links { - --tw-text-opacity: 1; - color: rgba(255, 255, 255, var(--tw-text-opacity)); - text-transform: uppercase; - } - .footer_copyright { - font-size: 0.875rem; - line-height: 1.25rem; - padding-top: 3rem; - padding-bottom: 3rem; - --tw-text-opacity: 1; - color: rgba(76, 76, 76, var(--tw-text-opacity)); - } - .documentation { - --tw-bg-opacity: 1; - background-color: rgba(250, 247, 230, var(--tw-bg-opacity)); - --tw-border-opacity: 1; - border-color: rgba(217, 206, 95, var(--tw-border-opacity)); - border-style: solid; - line-height: 2.25rem; - } - .mobile-mode { - background-color: #ede171 !important; - } - .top-border { - border-top-width: 4px; - } - .sidebar { - display: flex; - } - .side-nav > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1rem * var(--tw-space-y-reverse)); + flex-direction: row; + width: 40%; + gap: 0; } +} +.footer__nav__category { + --tw-text-opacity: 1; + color: rgba(91, 56, 233, var(--tw-text-opacity)); +} +.footer__nav__links { + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); + text-transform: uppercase; +} +.footer_copyright { + font-size: 0.875rem; + line-height: 1.25rem; + padding-top: 3rem; + padding-bottom: 3rem; + --tw-text-opacity: 1; + color: rgba(76, 76, 76, var(--tw-text-opacity)); +} +.documentation { + --tw-bg-opacity: 1; + background-color: rgba(250, 247, 230, var(--tw-bg-opacity)); + --tw-border-opacity: 1; + border-color: rgba(217, 206, 95, var(--tw-border-opacity)); + border-style: solid; + line-height: 2.25rem; +} +.mobile-mode { + background-color: #ede171 !important; +} +.top-border { + border-top-width: 4px; +} +.sidebar { + display: flex; +} +.side-nav > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1rem * var(--tw-space-y-reverse)); +} +.side-nav { + --tw-bg-opacity: 1; + background-color: rgba(242, 239, 217, var(--tw-bg-opacity)); + display: none; + flex-direction: column; + padding-left: 3.5rem; + padding-top: 5rem; + position: relative; + width: 21rem; + z-index: 0; +} +@media (min-width: 768px) { .side-nav { - --tw-bg-opacity: 1; - background-color: rgba(242, 239, 217, var(--tw-bg-opacity)); - display: none; - flex-direction: column; - padding-left: 3.5rem; - padding-top: 5rem; - position: relative; - width: 21rem; - z-index: 0; - } - @media (min-width: 768px) { - .side-nav { - display: flex; - } - } - .side-nav__expand > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.25rem * var(--tw-space-y-reverse)); - } - .side-nav__expand__button { - border-radius: 0.75rem; - cursor: pointer; display: flex; - align-items: center; - height: 3.5rem; - font-size: 1.125rem; - line-height: 1.75rem; - padding-left: 1rem; - padding-right: 1rem; - } - .side-nav__expand__button:hover { - --tw-text-opacity: 1; - color: rgba(76, 76, 76, var(--tw-text-opacity)); - } - .side-nav__expand__button { - width: 100%; - } - .side-nav__link:hover { - --tw-text-opacity: 1; - color: rgba(76, 76, 76, var(--tw-text-opacity)); - } - .side-nav__expand__icon { - margin-left: auto; } - .side-nav__expand__list { - padding-left: 2rem; - } - .side-nav__expand__list2 { - padding-left: 1.5rem; +} +.side-nav__expand > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.25rem * var(--tw-space-y-reverse)); +} +.side-nav__expand__button { + border-radius: 0.75rem; + cursor: pointer; + display: flex; + align-items: center; + height: 3.5rem; + font-size: 1.125rem; + line-height: 1.75rem; + padding-left: 1rem; + padding-right: 1rem; +} +.side-nav__expand__button:hover { + --tw-text-opacity: 1; + color: rgba(76, 76, 76, var(--tw-text-opacity)); +} +.side-nav__expand__button { + width: 100%; +} +.side-nav__link:hover { + --tw-text-opacity: 1; + color: rgba(76, 76, 76, var(--tw-text-opacity)); +} +.side-nav__expand__icon { + margin-left: auto; +} +.side-nav__expand__list { + padding-left: 2rem; +} +.side-nav__expand__list2 { + padding-left: 1.5rem; +} +.docs { + width: 100%; +} +@media (min-width: 640px) { + .docs { + max-width: 640px; } +} +@media (min-width: 768px) { .docs { - width: 100%; + max-width: 768px; } - @media (min-width: 640px) { - .docs { - max-width: 640px; - } +} +@media (min-width: 1024px) { + .docs { + max-width: 1024px; } - @media (min-width: 768px) { - .docs { - max-width: 768px; - } +} +@media (min-width: 1280px) { + .docs { + max-width: 1280px; } - @media (min-width: 1024px) { - .docs { - max-width: 1024px; - } +} +@media (min-width: 1536px) { + .docs { + max-width: 1536px; } - @media (min-width: 1280px) { - .docs { - max-width: 1280px; - } +} +.docs { + margin-left: auto; + margin-right: auto; + margin-left: 0; + margin-bottom: 6rem; + padding-left: 1rem; + padding-right: 1rem; + padding-top: 0; +} +@media (min-width: 768px) { + .docs { + margin-left: 2rem; + padding-top: 8rem; } - @media (min-width: 1536px) { - .docs { - max-width: 1536px; - } +} +.separator { + --tw-border-opacity: 1; + border-color: rgba(226, 224, 213, var(--tw-border-opacity)); + border-style: solid; + border-top-width: 2px; + margin-top: 2.5rem; + margin-bottom: 2.5rem; +} +.code-block { + --tw-border-opacity: 1; + border-color: rgba(226, 224, 213, var(--tw-border-opacity)); + border-style: solid; + border-width: 2px; + margin-top: 2.5rem; + margin-bottom: 2.5rem; + width: 100%; +} +.code-block__content { + margin: 1.5rem; +} +.bullet-list { + margin-top: 0.5rem; + margin-bottom: 0.5rem; + margin-left: 1rem; +} +.bullet-list__callout > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.5rem * var(--tw-space-x-reverse)); + margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); +} +.bullet-list__callout { + display: flex; +} +code.doc { + overflow-x: auto; + background-color: transparent; + --tw-border-opacity: 1; + border-color: rgba(226, 224, 213, var(--tw-border-opacity)); + border-width: 2px; + display: block; + margin-top: 1rem; + margin-bottom: 1rem; + padding: 1rem; +} +.admon { + border-style: dashed; + border-width: 2px; + margin-top: 2.5rem; + margin-bottom: 2.5rem; + width: 100%; +} +.admon-warning { + --tw-border-opacity: 1; + border-color: rgba(220, 38, 38, var(--tw-border-opacity)); +} +.admon__button { + border-radius: 9999px; + display: flex; + align-items: center; + justify-content: center; + height: 2rem; + margin-left: 1rem; + margin-top: -1rem; + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); + width: 8rem; + gap: 0.5rem; +} +.admon__button-warning { + --tw-bg-opacity: 1; + background-color: rgba(220, 38, 38, var(--tw-bg-opacity)); +} +.admon__button-note { + --tw-bg-opacity: 1; + background-color: rgba(30, 58, 138, var(--tw-bg-opacity)); +} +.admon-note { + --tw-border-opacity: 1; + border-color: rgba(30, 58, 138, var(--tw-border-opacity)); +} +.secondary-nav { + margin-bottom: 1.5rem; +} +@media (min-width: 768px) { + .secondary-nav { + display: none; } - .docs { - margin-left: auto; - margin-right: auto; - margin-left: 0; - margin-bottom: 6rem; - padding-left: 1rem; - padding-right: 1rem; - padding-top: 0; - } - @media (min-width: 768px) { - .docs { - margin-left: 2rem; - padding-top: 8rem; - } - } - .separator { - --tw-border-opacity: 1; - border-color: rgba(226, 224, 213, var(--tw-border-opacity)); - border-style: solid; - border-top-width: 2px; - margin-top: 2.5rem; - margin-bottom: 2.5rem; - } - .code-block { - --tw-border-opacity: 1; - border-color: rgba(226, 224, 213, var(--tw-border-opacity)); - border-style: solid; - border-width: 2px; - margin-top: 2.5rem; - margin-bottom: 2.5rem; - width: 100%; - } - .code-block__content { - margin: 1.5rem; - } - .bullet-list { - margin-top: 0.5rem; - margin-bottom: 0.5rem; - margin-left: 1rem; - } - .bullet-list__callout > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(0.5rem * var(--tw-space-x-reverse)); - margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); - } - .bullet-list__callout { - display: flex; +} +.secondary-nav__content { + --tw-border-opacity: 1; + border-color: rgba(210, 199, 91, var(--tw-border-opacity)); + width: 10rem; +} +.secondary-nav__split { + --tw-bg-opacity: 1; + background-color: rgba(18, 18, 18, var(--tw-bg-opacity)); + border-radius: 0.75rem; + display: inline-flex; + align-items: center; + justify-content: center; + font-weight: 500; + height: 3rem; + padding-left: 0.5rem; + padding-right: 0.5rem; + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); + width: 9rem; +} +.secondary-nav__split > p { + margin-right: 6px; +} +@media (min-width: 768px) { + .md\:flex-row { + flex-direction: row; } - code.doc { - overflow-x: auto; - background-color: transparent; - --tw-border-opacity: 1; - border-color: rgba(226, 224, 213, var(--tw-border-opacity)); - border-width: 2px; - display: block; - margin-top: 1rem; - margin-bottom: 1rem; - padding: 1rem; - } - .admon { - border-style: dashed; - border-width: 2px; - margin-top: 2.5rem; - margin-bottom: 2.5rem; - width: 100%; - } - .admon-warning { - --tw-border-opacity: 1; - border-color: rgba(220, 38, 38, var(--tw-border-opacity)); - } - .admon__button { - border-radius: 9999px; - display: flex; - align-items: center; - justify-content: center; - height: 2rem; - margin-left: 1rem; - margin-top: -1rem; - --tw-text-opacity: 1; - color: rgba(255, 255, 255, var(--tw-text-opacity)); - width: 8rem; - gap: 0.5rem; - } - .admon__button-warning { - --tw-bg-opacity: 1; - background-color: rgba(220, 38, 38, var(--tw-bg-opacity)); - } - .admon__button-note { - --tw-bg-opacity: 1; - background-color: rgba(30, 58, 138, var(--tw-bg-opacity)); - } - .admon-note { - --tw-border-opacity: 1; - border-color: rgba(30, 58, 138, var(--tw-border-opacity)); + .md\:ml-8 { + margin-left: 2rem; } - .secondary-nav { - margin-bottom: 1.5rem; - } - @media (min-width: 768px) { - .secondary-nav { - display: none; - } - } - .secondary-nav__content { - --tw-border-opacity: 1; - border-color: rgba(210, 199, 91, var(--tw-border-opacity)); - width: 10rem; - } - .secondary-nav__split { - --tw-bg-opacity: 1; - background-color: rgba(18, 18, 18, var(--tw-bg-opacity)); - border-radius: 0.75rem; - display: inline-flex; - align-items: center; - justify-content: center; - font-weight: 500; - height: 3rem; - padding-left: 0.5rem; - padding-right: 0.5rem; - --tw-text-opacity: 1; - color: rgba(255, 255, 255, var(--tw-text-opacity)); - width: 9rem; - } - .secondary-nav__split > p { - margin-right: 6px; - } - @media (min-width: 768px) { - .md\:flex-row { - flex-direction: row; - } - .md\:ml-8 { - margin-left: 2rem; - } - .md\:pt-32 { - padding-top: 8rem; - } - .md\:w-2\/5 { - width: 40%; - } - .md\:gap-0 { - gap: 0; - } + .md\:pt-32 { + padding-top: 8rem; } - - .nav-mobile { - display: block !important; + .md\:w-2\/5 { + width: 40%; } - - .CodeMirror { - height: auto; + .md\:gap-0 { + gap: 0; } +} - .component__example__code { - overflow: hidden; - } +.nav-mobile { + display: block !important; +} +.CodeMirror { + height: auto; } +.component__example__code { + overflow: hidden; +} - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } diff --git a/website/default.nix b/website/default.nix index c94e58e6..25acbace 100644 --- a/website/default.nix +++ b/website/default.nix @@ -28,6 +28,11 @@ let brand = import ./brand.nix { inherit chan; }; opti = if optimize then util.doCannibalize else (x: x); file = if optimize then "all.min.js" else "all.js"; + setTarget = x: y: with pkgs.haskell.lib; dontHaddock (overrideCabal x (_: { buildTarget = y; installPhase = '' + runHook preInstall + ./Setup copy ${y} + runHook postInstall + '';} )); in pkgs.runCommand "website" { LANG = "C.UTF-8"; @@ -35,6 +40,6 @@ in mkdir $out ${brand.icons.mkIcons brand.logo.thumbnail "$out"} ln -s ${./assets} $out/assets - ln -s ${opti pkgsJS.haskell.packages.${util.compilerjs}.Shpadoinkle-website}/bin/run.jsexe/${file} $out/all.min.js - ${pkgs.haskell.packages.${compiler}.Shpadoinkle-website}/bin/disembodied -o $out + ln -s ${opti (setTarget pkgsJS.haskell.packages.${util.compilerjs}.Shpadoinkle-website "run")}/bin/run.jsexe/${file} $out/all.min.js + ${setTarget pkgs.haskell.packages.${compiler}.Shpadoinkle-website "disembodied"}/bin/disembodied -o $out '' diff --git a/website/docs/concepts.adoc b/website/docs/concepts.adoc index 08fcbfea..01a8a8d0 100644 --- a/website/docs/concepts.adoc +++ b/website/docs/concepts.adoc @@ -1,5 +1,3 @@ -= Concepts - Shpadoinkle is a new way to program user interfaces with Haskell. The core idea here is to model user interfaces as functions with this type: diff --git a/website/docs/index.adoc b/website/docs/index.adoc deleted file mode 100644 index df07e09e..00000000 --- a/website/docs/index.adoc +++ /dev/null @@ -1,35 +0,0 @@ -:page-role: home - -= Shpadoinkle - -image::logo.png[Eddie is loved forever,150,150,id="logo"] - - -https://www.youtube.com/watch?v=0CizU8aB3c8[Shpadoinkle] is a new way to -program user interfaces with Haskell. - -To begin using it, go to -xref:getting-started/index.adoc[Getting Started]. - - -Fast:: - -Shpadoinkle is fast because it does little work. The renderer is modular, so -you can always benefit from the latest advances in virtual DOM rendering. - -Declarative:: - -Your Shpadoinkle code is high-level. You need not worry about low-level -details, causality, or when DOM nodes get replaced. - -Composable:: - -With Shpadoinkle, there is no need to associate model update code with DOM -locations. This avoids elaborate passing of messages and payloads. Components -are highly composable. - -Reliable:: - -Shpadoinkle UIs are composed of components with no side-effects. So, runtime -errors are exceedingly rare. Code is easy to test because model updates are -pure functions. diff --git a/website/docs/packages/core.adoc b/website/docs/packages/core.adoc index 4f39fc07..fb1d94d0 100644 --- a/website/docs/packages/core.adoc +++ b/website/docs/packages/core.adoc @@ -1,5 +1,3 @@ -= Core - https://shpadoinkle.org/core[Haddock] This module is for core types and logic. diff --git a/website/docs/packages/index.adoc b/website/docs/packages/index.adoc index 7de1baab..e7dae986 100644 --- a/website/docs/packages/index.adoc +++ b/website/docs/packages/index.adoc @@ -1,5 +1,3 @@ -= Packages - Shpadoinkle is segregated into several packages that compose together or can be used separately. [TIP] -- GitLab From 6e6e15835c95e15f67354f5ba8db0a45422e63cf Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 22 Apr 2021 15:36:34 -0600 Subject: [PATCH 070/116] route is part of the model --- .../Shpadoinkle-developer-tools.cabal | 4 +- examples/Shpadoinkle-examples.cabal | 24 ++--- template/Shpadoinkle-template.cabal | 4 +- template/Shpadoinkle/Template/TH.hs | 6 +- website/Shpadoinkle-website.cabal | 24 +++-- website/Shpadoinkle/Website/Disembodied.hs | 4 +- .../Shpadoinkle/Website/Page/Documentation.hs | 94 +++++++++++-------- website/Shpadoinkle/Website/Page/Home.hs | 49 +++++----- .../Shpadoinkle/Website/Partials/Footer.hs | 2 +- website/Shpadoinkle/Website/Partials/Nav.hs | 18 ++-- .../Shpadoinkle/Website/Partials/Social.hs | 21 ++--- .../Shpadoinkle/Website/Partials/Template.hs | 34 +++---- website/Shpadoinkle/Website/Run.hs | 8 +- website/Shpadoinkle/Website/Types/Frontend.hs | 26 ----- website/Shpadoinkle/Website/Types/Nav.hs | 8 +- .../Shpadoinkle/Website/Types/PageModel.hs | 17 ++++ website/Shpadoinkle/Website/Types/Route.hs | 25 +++++ .../Types/{Routes => Route}/GettingStarted.hs | 2 +- .../Types/{Routes => Route}/Packages.hs | 2 +- .../Types/{Routes => Route}/Tutorial.hs | 2 +- .../Website/Types/{Routes.hs => SPA.hs} | 81 +++++++--------- website/Shpadoinkle/Website/Types/Social.hs | 18 ++-- .../Shpadoinkle/Website/Types/ViewModel.hs | 21 +++++ website/Shpadoinkle/Website/View.hs | 60 ++++++------ website/assets/expando.svg | 3 + website/assets/style.css | 9 ++ widgets/Shpadoinkle-widgets.cabal | 4 +- widgets/Shpadoinkle/Widgets/Types/Core.hs | 4 + 28 files changed, 312 insertions(+), 262 deletions(-) delete mode 100644 website/Shpadoinkle/Website/Types/Frontend.hs create mode 100644 website/Shpadoinkle/Website/Types/PageModel.hs create mode 100644 website/Shpadoinkle/Website/Types/Route.hs rename website/Shpadoinkle/Website/Types/{Routes => Route}/GettingStarted.hs (95%) rename website/Shpadoinkle/Website/Types/{Routes => Route}/Packages.hs (96%) rename website/Shpadoinkle/Website/Types/{Routes => Route}/Tutorial.hs (96%) rename website/Shpadoinkle/Website/Types/{Routes.hs => SPA.hs} (76%) create mode 100644 website/Shpadoinkle/Website/Types/ViewModel.hs create mode 100644 website/assets/expando.svg diff --git a/developer-tools/Shpadoinkle-developer-tools.cabal b/developer-tools/Shpadoinkle-developer-tools.cabal index f341496d..ecf0e962 100644 --- a/developer-tools/Shpadoinkle-developer-tools.cabal +++ b/developer-tools/Shpadoinkle-developer-tools.cabal @@ -38,7 +38,7 @@ common ghc-options library - imports: ghc-options + import: ghc-options exposed-modules: Shpadoinkle.DeveloperTools @@ -69,7 +69,7 @@ library executable devtools - imports: ghc-options + import: ghc-options main-is: Main.hs other-modules: Shpadoinkle.DeveloperTools diff --git a/examples/Shpadoinkle-examples.cabal b/examples/Shpadoinkle-examples.cabal index d3140930..4a7dfbfe 100644 --- a/examples/Shpadoinkle-examples.cabal +++ b/examples/Shpadoinkle-examples.cabal @@ -46,7 +46,7 @@ common ghcjs-options executable animation - imports: ghc-options ghcjs-options + import: ghc-options, ghcjs-options main-is: Animation.hs @@ -67,7 +67,7 @@ executable animation executable calculator - imports: ghc-options ghcjs-options + import: ghc-options, ghcjs-options main-is: Calculator.hs @@ -87,7 +87,7 @@ executable calculator executable calculator-ie - imports: ghc-options ghcjs-options + import: ghc-options, ghcjs-options main-is: CalculatorIE.hs @@ -111,7 +111,7 @@ executable calculator-ie executable counter - imports: ghc-options ghcjs-options + import: ghc-options, ghcjs-options main-is: Counter.hs @@ -129,7 +129,7 @@ executable counter executable lens - imports: ghc-options ghcjs-options + import: ghc-options, ghcjs-options main-is: Lens.hs @@ -150,7 +150,7 @@ executable lens executable servant-crud-client - imports: ghc-options ghcjs-options + import: ghc-options, ghcjs-options main-is: Run/Client.hs @@ -198,7 +198,7 @@ executable servant-crud-client executable servant-crud-dev - imports: ghc-options + import: ghc-options main-is: Run/Dev.hs @@ -253,7 +253,7 @@ executable servant-crud-dev executable servant-crud-server - imports: ghc-options + import: ghc-options main-is: Run/Server.hs @@ -301,7 +301,7 @@ executable servant-crud-server executable streaming - imports: ghc-options ghcjs-options + import: ghc-options, ghcjs-options main-is: Streaming.hs @@ -320,7 +320,7 @@ executable streaming executable throttle-and-debounce - imports: ghc-options ghcjs-options + import: ghc-options, ghcjs-options main-is: ThrottleAndDebounce.hs @@ -340,7 +340,7 @@ executable throttle-and-debounce executable todomvc - imports: ghc-options ghcjs-options + import: ghc-options, ghcjs-options main-is: TODOMVC.hs @@ -362,7 +362,7 @@ executable todomvc executable widgets - imports: ghc-options ghcjs-options + import: ghc-options, ghcjs-options main-is: Widgets.hs diff --git a/template/Shpadoinkle-template.cabal b/template/Shpadoinkle-template.cabal index de7f1ada..f910197a 100644 --- a/template/Shpadoinkle-template.cabal +++ b/template/Shpadoinkle-template.cabal @@ -33,7 +33,7 @@ common ghc-options library - imports: ghc-options + import: ghc-options exposed-modules: Shpadoinkle.Template.TH @@ -56,7 +56,7 @@ library test-suite sample - imports: ghc-options + import: ghc-options type: exitcode-stdio-1.0 diff --git a/template/Shpadoinkle/Template/TH.hs b/template/Shpadoinkle/Template/TH.hs index 3c4c7fea..6095efb1 100644 --- a/template/Shpadoinkle/Template/TH.hs +++ b/template/Shpadoinkle/Template/TH.hs @@ -7,11 +7,9 @@ module Shpadoinkle.Template.TH where import Control.Monad (unless, when) import Data.Maybe (fromMaybe) -import Data.Text (Text, breakOn, cons, head, null, - pack, replace, tail, uncons, - unpack) +import Data.Text (Text, cons, head, null, pack, + replace, tail, uncons, unpack) import Data.Text.IO -import Debug.Trace (trace) import HTMLEntities.Decoder (htmlEntityBody) import Language.Haskell.TH.Syntax import Prelude hiding (head, null, readFile, tail) diff --git a/website/Shpadoinkle-website.cabal b/website/Shpadoinkle-website.cabal index e18f6ef9..00076f7b 100644 --- a/website/Shpadoinkle-website.cabal +++ b/website/Shpadoinkle-website.cabal @@ -52,10 +52,10 @@ executable disembodied Shpadoinkle.Website.Types.Effects.Example Shpadoinkle.Website.Types.Effects.Hooglable Shpadoinkle.Website.Types.Effects.Swan - Shpadoinkle.Website.Types.Frontend + Shpadoinkle.Website.Types.ViewModel + Shpadoinkle.Website.Types.PageModel Shpadoinkle.Website.Types.CurrentYear Shpadoinkle.Website.Types.Home - Shpadoinkle.Website.Types.Routes Shpadoinkle.Website.View Shpadoinkle.Website.Component.LiveExample Shpadoinkle.Website.Partials.Footer @@ -66,9 +66,11 @@ executable disembodied Shpadoinkle.Website.Types.Hoogle Shpadoinkle.Website.Types.Hoogle.Target Shpadoinkle.Website.Types.Nav - Shpadoinkle.Website.Types.Routes.GettingStarted - Shpadoinkle.Website.Types.Routes.Packages - Shpadoinkle.Website.Types.Routes.Tutorial + Shpadoinkle.Website.Types.Route.GettingStarted + Shpadoinkle.Website.Types.Route.Packages + Shpadoinkle.Website.Types.Route.Tutorial + Shpadoinkle.Website.Types.Route + Shpadoinkle.Website.Types.SPA Shpadoinkle.Website.Types.Social hs-source-dirs: ./. @@ -130,15 +132,17 @@ executable run Shpadoinkle.Website.Types.Hoogle.API Shpadoinkle.Website.Types.Hoogle.Target Shpadoinkle.Website.Types.Example - Shpadoinkle.Website.Types.Frontend + Shpadoinkle.Website.Types.ViewModel + Shpadoinkle.Website.Types.PageModel Shpadoinkle.Website.Types.ExampleState Shpadoinkle.Website.Types.Effects.Example Shpadoinkle.Website.Types.Effects.Hooglable Shpadoinkle.Website.Types.Effects.Swan - Shpadoinkle.Website.Types.Routes.GettingStarted - Shpadoinkle.Website.Types.Routes.Tutorial - Shpadoinkle.Website.Types.Routes.Packages - Shpadoinkle.Website.Types.Routes + Shpadoinkle.Website.Types.Route.GettingStarted + Shpadoinkle.Website.Types.Route.Tutorial + Shpadoinkle.Website.Types.Route.Packages + Shpadoinkle.Website.Types.Route + Shpadoinkle.Website.Types.SPA Shpadoinkle.Website.Types.Social Shpadoinkle.Website.Style Shpadoinkle.Website.View diff --git a/website/Shpadoinkle/Website/Disembodied.hs b/website/Shpadoinkle/Website/Disembodied.hs index 3049fd85..e5920966 100644 --- a/website/Shpadoinkle/Website/Disembodied.hs +++ b/website/Shpadoinkle/Website/Disembodied.hs @@ -29,7 +29,7 @@ import Shpadoinkle.Website.Types.Effects.Example (ExampleEffects) import Shpadoinkle.Website.Types.Effects.Hooglable (Hooglable) import Shpadoinkle.Website.Types.Effects.Swan (Swan) import Shpadoinkle.Website.Types.Home (Examples) -import Shpadoinkle.Website.Types.Routes (SPA, routes) +import Shpadoinkle.Website.Types.SPA (SPA, routes) import Shpadoinkle.Website.View as View import System.Environment (getArgs) @@ -42,7 +42,7 @@ newtype Noop a = Noop (JSM a) getStarts :: (MonadJSM m, ExampleEffects m) => IO (SiteSpec (SPA m)) getStarts = do cy <- getCurrentYear - traverseUnions (\r -> (\fe -> template Prod r (snd fe) $ view cy fe) <$> start r) routes + traverseUnions (fmap (template Prod <*> view cy) . start) routes main :: IO () diff --git a/website/Shpadoinkle/Website/Page/Documentation.hs b/website/Shpadoinkle/Website/Page/Documentation.hs index 4fc2d6df..bfc3e036 100644 --- a/website/Shpadoinkle/Website/Page/Documentation.hs +++ b/website/Shpadoinkle/Website/Page/Documentation.hs @@ -10,25 +10,27 @@ module Shpadoinkle.Website.Page.Documentation where -import Data.Text (Text, pack) -import Prelude hiding (div) -import Shpadoinkle (MonadJSM) -import Shpadoinkle.Html as H -import Shpadoinkle.Template.TH (embedAsciidoc) -import Shpadoinkle.Website.Style hiding (nav) -import Shpadoinkle.Website.Types.Routes (Route (..), - goTo) -import qualified Shpadoinkle.Website.Types.Routes.GettingStarted as GettingStarted -import qualified Shpadoinkle.Website.Types.Routes.Packages as Packages -import qualified Shpadoinkle.Website.Types.Routes.Tutorial as Tutorial -import Shpadoinkle.Widgets.Types (Humanize (..)) - - -concepts :: MonadJSM m => Html m a +import Data.Maybe (maybeToList) +import Data.Text (Text, pack) +import Prelude hiding (div) +import Shpadoinkle (MonadJSM) +import Shpadoinkle.Html as H +import Shpadoinkle.Html.TH.AssetLink (assetLink) +import Shpadoinkle.Template.TH (embedAsciidoc) +import Shpadoinkle.Website.Style hiding (nav) +import Shpadoinkle.Website.Types.Route (Route (..)) +import qualified Shpadoinkle.Website.Types.Route.GettingStarted as GettingStarted +import qualified Shpadoinkle.Website.Types.Route.Packages as Packages +import qualified Shpadoinkle.Website.Types.Route.Tutorial as Tutorial +import Shpadoinkle.Website.Types.SPA (goTo) +import Shpadoinkle.Widgets.Types (Humanize (..)) + + +concepts :: MonadJSM m => Route -> Html m a concepts = docsWrap ("Concepts" :: Text) $(embedAsciidoc "./docs/concepts.adoc") -gettingStarted :: MonadJSM m => GettingStarted.Route -> Html m a +gettingStarted :: MonadJSM m => GettingStarted.Route -> Route -> Html m a gettingStarted r = docsWrap r $ case r of GettingStarted.RGSIndex -> $(embedAsciidoc "./docs/getting-started/index.adoc") @@ -38,7 +40,7 @@ gettingStarted r = docsWrap r $ case r of $(embedAsciidoc "./docs/getting-started/extend-an-example.adoc") -packages :: MonadJSM m => Packages.Route -> Html m a +packages :: MonadJSM m => Packages.Route -> Route -> Html m a packages r = docsWrap r $ case r of Packages.RPIndex -> $(embedAsciidoc "./docs/packages/index.adoc") Packages.RPCore -> $(embedAsciidoc "./docs/packages/core.adoc") @@ -50,7 +52,7 @@ packages r = docsWrap r $ case r of Packages.RPWidgets -> $(embedAsciidoc "./docs/packages/widgets.adoc") -tutorial :: MonadJSM m => Tutorial.Route -> Html m a +tutorial :: MonadJSM m => Tutorial.Route -> Route -> Html m a tutorial r = docsWrap r $ case r of Tutorial.RTIndex -> $(embedAsciidoc "./docs/tutorial/index.adoc") Tutorial.RTCalculator -> $(embedAsciidoc "./docs/tutorial/calculator.adoc") @@ -58,37 +60,51 @@ tutorial r = docsWrap r $ case r of Tutorial.RTComposing -> $(embedAsciidoc "./docs/tutorial/composing.adoc") +top + :: MonadJSM m + => Route -> Route -> Text -> Maybe (Html m a, Html m a) -> Html m a +top cur r t mext = div [ class' side_nav__expand ] $ + [ a [ class' $ side_nav__expand__button <> asClass (side_nav__bold, r == cur) + , goTo r + ] $ [ text t ] <> maybeToList (fst <$> mext) + ] <> maybeToList (snd <$> mext) + + +expandEq :: Route -> Route -> Bool +expandEq (RGettingStarted _) (RGettingStarted _) = True +expandEq (RPackages _) (RPackages _) = True +expandEq (RTutorial _) (RTutorial _) = True +expandEq x y = x == y + + expandable :: forall r m a . (MonadJSM m, Bounded r, Enum r, Humanize r) - => (r -> Route) -> Text -> Html m a -expandable toR t = div [ class' side_nav__expand ] - [ a [ class' side_nav__expand__button - , goTo (toR $ minBound @r) - ] - [ text t - , H.span [ class' side_nav__expand__icon ] - [ - ] - ] + => Route -> (r -> Route) -> Text -> Html m a +expandable cur toR t = let topr = toR $ minBound @r in top cur topr t $ Just + (H.span [ class' $ side_nav__expand__icon <> asClass (expanded, expandEq cur topr) ] + [ img' [ src $(assetLink "/assets/expando.svg") ] + ] , div [ class' side_nav__expand ] - [ ul [ class' side_nav__expand__list ] $ sideNavLink toR <$> [ succ minBound .. maxBound @r ] - ] - ] + [ ul [ class' side_nav__expand__list ] $ sideNavLink cur toR <$> [ succ minBound .. maxBound @r ] + | expandEq cur topr ] + ) -sideNavLink :: MonadJSM m => Humanize r => (r -> Route) -> r -> Html m a -sideNavLink toR r = li_ - [ a [ class' side_nav__link, goTo (toR r) ] [ text $ humanize r ] +sideNavLink :: MonadJSM m => Humanize r => Route -> (r -> Route) -> r -> Html m a +sideNavLink cur toR r = li_ + [ a [ class' $ side_nav__link <> asClass (side_nav__bold, cur == toR r) + , goTo (toR r) ] [ text $ humanize r ] ] -docsWrap :: (MonadJSM m, Humanize r) => r -> [Html m a] -> Html m a -docsWrap r content' = section [class' flex ] +docsWrap :: (MonadJSM m, Humanize r) => r -> [Html m a] -> Route -> Html m a +docsWrap r content' cur = section [ class' flex ] [ nav [ class' side_nav ] - [ expandable RGettingStarted "Getting Started" - , expandable RTutorial "Tutorial" - , expandable RPackages "Reference" + [ top cur RConcepts "Basic Concept" Nothing + , expandable cur RGettingStarted "Getting Started" + , expandable cur RTutorial "Tutorial" + , expandable cur RPackages "Reference" ] , h "main" [ class' docs ] $ h1_ [ text $ humanize r ] : content' ] diff --git a/website/Shpadoinkle/Website/Page/Home.hs b/website/Shpadoinkle/Website/Page/Home.hs index 51c6c878..0e74747a 100644 --- a/website/Shpadoinkle/Website/Page/Home.hs +++ b/website/Shpadoinkle/Website/Page/Home.hs @@ -11,31 +11,30 @@ module Shpadoinkle.Website.Page.Home where -import Control.Lens ((^.)) -import Data.Generics.Labels () -import Data.Text (Text) -import GHC.Generics (Generic) -import Prelude hiding (div, - span) -import Shpadoinkle.Html as H hiding - (title) -import Shpadoinkle.Html.TH.AssetLink (assetLink) -import Shpadoinkle.Lens (onRecord, - onRecordEndo) -import Shpadoinkle.Router (MonadJSM, - navigate) -import Shpadoinkle.Website.Component.LiveExample as Live (example) -import Shpadoinkle.Website.Style as Style -import Shpadoinkle.Website.Types.Effects.Example (ExampleEffects) -import Shpadoinkle.Website.Types.Example (Example) -import Shpadoinkle.Website.Types.Home (ExampleLens, - Examples, - Home) -import Shpadoinkle.Website.Types.Nav (Nav (NGettingStarted), - toRoute) -import Shpadoinkle.Website.Types.Routes (Route (RGettingStarted), - SPA, goTo) -import Shpadoinkle.Website.Types.Routes.GettingStarted (Route (RGSIndex)) +import Control.Lens ((^.)) +import Data.Generics.Labels () +import Data.Text (Text) +import GHC.Generics (Generic) +import Prelude hiding (div, + span) +import Shpadoinkle.Html as H hiding + (title) +import Shpadoinkle.Html.TH.AssetLink (assetLink) +import Shpadoinkle.Lens (onRecord, + onRecordEndo) +import Shpadoinkle.Router (MonadJSM, + navigate) +import Shpadoinkle.Website.Component.LiveExample as Live (example) +import Shpadoinkle.Website.Style as Style +import Shpadoinkle.Website.Types.Effects.Example (ExampleEffects) +import Shpadoinkle.Website.Types.Example (Example) +import Shpadoinkle.Website.Types.Home (ExampleLens, + Examples, Home) +import Shpadoinkle.Website.Types.Nav (Nav (NGettingStarted), + toRoute) +import Shpadoinkle.Website.Types.Route (Route (RGettingStarted)) +import Shpadoinkle.Website.Types.Route.GettingStarted (Route (RGSIndex)) +import Shpadoinkle.Website.Types.SPA (SPA, goTo) view :: (MonadJSM m, ExampleEffects m) => Home -> Html m Home diff --git a/website/Shpadoinkle/Website/Partials/Footer.hs b/website/Shpadoinkle/Website/Partials/Footer.hs index ac4cb570..7cb1c1f8 100644 --- a/website/Shpadoinkle/Website/Partials/Footer.hs +++ b/website/Shpadoinkle/Website/Partials/Footer.hs @@ -16,7 +16,7 @@ import Shpadoinkle.Html.TH.AssetLink (assetLink) import Shpadoinkle.Website.Style as Style import Shpadoinkle.Website.Types.CurrentYear (CurrentYear) import Shpadoinkle.Website.Types.Nav (Nav (NHome), toRoute) -import Shpadoinkle.Website.Types.Routes (goTo) +import Shpadoinkle.Website.Types.SPA (goTo) import Shpadoinkle.Widgets.Types (humanize) diff --git a/website/Shpadoinkle/Website/Partials/Nav.hs b/website/Shpadoinkle/Website/Partials/Nav.hs index 830dc1c4..09f99245 100644 --- a/website/Shpadoinkle/Website/Partials/Nav.hs +++ b/website/Shpadoinkle/Website/Partials/Nav.hs @@ -11,23 +11,23 @@ import Shpadoinkle (MonadJSM) import Shpadoinkle.Html as H import Shpadoinkle.Html.TH.AssetLink import Shpadoinkle.Website.Style as Style -import Shpadoinkle.Website.Types.Frontend import Shpadoinkle.Website.Types.Nav -import Shpadoinkle.Website.Types.Routes (goTo) +import Shpadoinkle.Website.Types.Route +import Shpadoinkle.Website.Types.SPA (goTo) import Shpadoinkle.Widgets.Types import Shpadoinkle.Widgets.Types.Physical (Toggle (..)) -toActive :: Frontend -> Maybe Nav +toActive :: Route -> Maybe Nav toActive = \case - MHome _ -> Just NHome - MGettingStarted _ -> Just NGettingStarted - MTutorial _ -> Just NTutorial - MSandbox -> Just NSandbox + RHome -> Just NHome + RGettingStarted _ -> Just NGettingStarted + RTutorial _ -> Just NTutorial + RSandbox -> Just NSandbox _ -> Nothing -navLinkDesktop :: MonadJSM m => Frontend -> Nav -> Html m a +navLinkDesktop :: MonadJSM m => Route -> Nav -> Html m a navLinkDesktop fe n = li [ class' nav__link ] $ a [ goTo $ toRoute n ] [ text $ humanize n ] : [ img' [ class' nav__link_underline, src $(assetLink "/assets/nav_line.svg") ] | toActive fe == Just n ] @@ -37,7 +37,7 @@ navLinkMobile :: Nav -> Html m a navLinkMobile n = a [ class' nav_mobile__link, href "#" ] [ text $ humanize n ] -view :: MonadJSM m => Toggle -> Frontend -> [Html m Toggle] +view :: MonadJSM m => Toggle -> Route -> [Html m Toggle] view t r = pure $ div [ class' started_header ] $ [ H.nav [ class' Style.nav ] [ a [ goTo $ toRoute NHome ] diff --git a/website/Shpadoinkle/Website/Partials/Social.hs b/website/Shpadoinkle/Website/Partials/Social.hs index 4517150c..5158fa8c 100644 --- a/website/Shpadoinkle/Website/Partials/Social.hs +++ b/website/Shpadoinkle/Website/Partials/Social.hs @@ -7,23 +7,22 @@ module Shpadoinkle.Website.Partials.Social where -import Control.Lens ((^.)) -import Data.Generics.Labels () -import Data.Text (Text) -import Shpadoinkle.Html (Html, content, h, meta', - property, text, - textProperty) -import Shpadoinkle.Website.Types.Frontend (Frontend) -import Shpadoinkle.Website.Types.Social (toSocial) +import Control.Lens ((^.)) +import Data.Generics.Labels () +import Data.Text (Text) +import Shpadoinkle.Html (Html, content, h, meta', + property, text, textProperty) +import Shpadoinkle.Website.Types.Route (Route) +import Shpadoinkle.Website.Types.Social (toSocial) default (Text) -view :: Frontend -> [Html m a] +view :: Route -> [Html m a] view s' = let s = toSocial s' in - [ meta' [ property "og:type", content "website" ] - , meta' [ property "og:title", content $ s ^. #title ] + [ meta' [ property "og:type", content "website" ] + , meta' [ property "og:title", content $ s ^. #title ] , meta' [ property "og:description", content $ s ^. #description ] , meta' [ textProperty "name" "description", content $ s ^. #description ] , h "title" [] [ text $ s ^. #title ] diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index 8a4f497c..b9ad7bbe 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -11,7 +11,7 @@ module Shpadoinkle.Website.Partials.Template ) where -import Control.Lens (_1, _2, (&), (.~), (?~)) +import Control.Lens ((&), (.~), (?~), (^.)) import Data.Generics.Labels () import Data.Text (Text, pack) import Network.URI (nullURIAuth) @@ -19,7 +19,7 @@ import Prelude hiding (div) import Shpadoinkle (MonadJSM) import Shpadoinkle.Html import Shpadoinkle.Html.TH.AssetLink (assetLink) -import Shpadoinkle.Lens (generalize) +import Shpadoinkle.Lens (onRecord) import Shpadoinkle.Router (getURI, toHydration) import Shpadoinkle.Run (Env (..), entrypoint) import Shpadoinkle.Template.TH (embedHtml) @@ -27,8 +27,10 @@ import qualified Shpadoinkle.Website.Partials.Footer as Footer import qualified Shpadoinkle.Website.Partials.Nav as Nav import qualified Shpadoinkle.Website.Partials.Social as Social import Shpadoinkle.Website.Types.CurrentYear (CurrentYear) -import Shpadoinkle.Website.Types.Frontend (Frontend) -import Shpadoinkle.Website.Types.Routes (Route, SPA) +import Shpadoinkle.Website.Types.PageModel (PageModel) +import Shpadoinkle.Website.Types.Route (Route) +import Shpadoinkle.Website.Types.SPA (SPA) +import Shpadoinkle.Website.Types.ViewModel (ViewModel) import Shpadoinkle.Widgets.Types.Physical (Toggle (..)) @@ -40,8 +42,8 @@ favicons :: [Html m a] favicons = $(embedHtml "./favicon.html") -headView :: forall m a. Env -> Route -> Frontend -> Html m a -headView ev r fe = head_ $ +headView :: forall m a. Env -> ViewModel -> Html m a +headView ev vm = head_ $ [ meta' [ charset "UTF-8" ] , meta' [ content "width=device-width, initial-scale=1.0", name' "viewport" ] , link' @@ -56,7 +58,7 @@ headView ev r fe = head_ $ [ rel "stylesheet" , href $(assetLink "/assets/style.css") ] - , toHydration fe + , toHydration vm , script [ src $ entrypoint ev ] [] , script [ src $ codemirrorCDN "codemirror.min.js" ] [] , script [ src $ codemirrorCDN "mode/haskell/haskell.min.js" ] [] @@ -66,21 +68,21 @@ headView ev r fe = head_ $ , href . pack . show . (#uriAuthority ?~ (nullURIAuth & #uriRegName .~ "shpadoinkle.org/")) . (#uriScheme .~ "https:") - $ getURI @(SPA m) r + $ getURI @(SPA m) (vm ^. #currentRoute) ] - ] <> Social.view fe <> favicons + ] <> Social.view (vm ^. #currentRoute) <> favicons -template :: Env -> Route -> Frontend -> Html m a -> Html m a -template ev r fe content' = html [ lang "en" ] - [ headView ev r fe +template :: Env -> ViewModel -> Html m a -> Html m a +template ev vm content' = html [ lang "en" ] + [ headView ev vm , body "top-border" [ content' ] ] -wrapper :: MonadJSM m => CurrentYear -> Toggle -> Frontend -> Html m a -> [Html m (Toggle, a)] -wrapper yc t fe content' - = (generalize _1 <$> Nav.view t fe) - <> (generalize _2 +wrapper :: MonadJSM m => CurrentYear -> Toggle -> Route -> Html m PageModel -> [Html m ViewModel] +wrapper yc t cur content' + = (onRecord #mobileMenu <$> Nav.view t cur) + <> (onRecord #pageModel <$> case t of Open -> [] Closed _ ->[ content', Footer.view yc ]) diff --git a/website/Shpadoinkle/Website/Run.hs b/website/Shpadoinkle/Website/Run.hs index 9a366f8b..8c6a347f 100644 --- a/website/Shpadoinkle/Website/Run.hs +++ b/website/Shpadoinkle/Website/Run.hs @@ -64,8 +64,8 @@ import Shpadoinkle.Website.Types.Effects.Swan (Swan (..)) import Shpadoinkle.Website.Types.Home (Examples (..)) import Shpadoinkle.Website.Types.Hoogle.API as Hoogle import Shpadoinkle.Website.Types.Hoogle.Target (Target) -import Shpadoinkle.Website.Types.Routes (Route (RFourOhFour), - SPA, routes) +import Shpadoinkle.Website.Types.Route (Route (RFourOhFour)) +import Shpadoinkle.Website.Types.SPA (SPA, routes) #ifndef ghcjs_HOST_OS import Shpadoinkle.Website.Partials.Template (template) import Shpadoinkle.Website.View (start, startJS, @@ -136,8 +136,8 @@ main = runJSorWarp 8080 app dev :: IO () dev = liveWithBackend 8080 app . pure $ Servant.serve (Proxy @ (SPA IO)) $ serveUI @(SPA IO) "." (\r -> do - x <- start r + vm <- start r cy <- getCurrentYear - return $ template @App Dev r (snd x) (view cy x)) routes + return $ template @App Dev vm (view cy vm)) routes #endif diff --git a/website/Shpadoinkle/Website/Types/Frontend.hs b/website/Shpadoinkle/Website/Types/Frontend.hs deleted file mode 100644 index b9c487f7..00000000 --- a/website/Shpadoinkle/Website/Types/Frontend.hs +++ /dev/null @@ -1,26 +0,0 @@ -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DeriveGeneric #-} - - -module Shpadoinkle.Website.Types.Frontend where - - -import Data.Aeson (FromJSON, - ToJSON) -import GHC.Generics (Generic) -import Shpadoinkle (NFData) -import Shpadoinkle.Website.Types.Home (Home) -import qualified Shpadoinkle.Website.Types.Routes.GettingStarted as GettingStarted -import qualified Shpadoinkle.Website.Types.Routes.Packages as Packages -import qualified Shpadoinkle.Website.Types.Routes.Tutorial as Tutorial - - -data Frontend - = MHome Home - | MConcepts - | MGettingStarted GettingStarted.Route - | MPackages Packages.Route - | MTutorial Tutorial.Route - | MSandbox - | MFourOhFour - deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) diff --git a/website/Shpadoinkle/Website/Types/Nav.hs b/website/Shpadoinkle/Website/Types/Nav.hs index 84056776..fa13e042 100644 --- a/website/Shpadoinkle/Website/Types/Nav.hs +++ b/website/Shpadoinkle/Website/Types/Nav.hs @@ -7,10 +7,10 @@ module Shpadoinkle.Website.Types.Nav where -import Shpadoinkle.Website.Types.Routes as R (Route (RGettingStarted, RHome, RSandbox, RTutorial)) -import Shpadoinkle.Website.Types.Routes.GettingStarted (Route (RGSIndex)) -import Shpadoinkle.Website.Types.Routes.Tutorial (Route (RTIndex)) -import Shpadoinkle.Widgets.Types (Humanize (..)) +import Shpadoinkle.Website.Types.Route as R (Route (RGettingStarted, RHome, RSandbox, RTutorial)) +import Shpadoinkle.Website.Types.Route.GettingStarted (Route (RGSIndex)) +import Shpadoinkle.Website.Types.Route.Tutorial (Route (RTIndex)) +import Shpadoinkle.Widgets.Types (Humanize (..)) data Nav diff --git a/website/Shpadoinkle/Website/Types/PageModel.hs b/website/Shpadoinkle/Website/Types/PageModel.hs new file mode 100644 index 00000000..9db1317c --- /dev/null +++ b/website/Shpadoinkle/Website/Types/PageModel.hs @@ -0,0 +1,17 @@ +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} + + +module Shpadoinkle.Website.Types.PageModel where + + +import Data.Aeson (FromJSON, ToJSON) +import GHC.Generics (Generic) +import Shpadoinkle (NFData) +import Shpadoinkle.Website.Types.Home (Home) + + +data PageModel + = MHome Home + | MStatic + deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) diff --git a/website/Shpadoinkle/Website/Types/Route.hs b/website/Shpadoinkle/Website/Types/Route.hs new file mode 100644 index 00000000..3ebcfe31 --- /dev/null +++ b/website/Shpadoinkle/Website/Types/Route.hs @@ -0,0 +1,25 @@ +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} + + +module Shpadoinkle.Website.Types.Route where + + +import Data.Aeson (FromJSON, + ToJSON) +import GHC.Generics (Generic) +import Shpadoinkle (NFData) +import qualified Shpadoinkle.Website.Types.Route.GettingStarted as GettingStarted +import qualified Shpadoinkle.Website.Types.Route.Packages as Packages +import qualified Shpadoinkle.Website.Types.Route.Tutorial as Tutorial + + +data Route + = RHome + | RConcepts + | RGettingStarted GettingStarted.Route + | RPackages Packages.Route + | RTutorial Tutorial.Route + | RSandbox + | RFourOhFour + deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) diff --git a/website/Shpadoinkle/Website/Types/Routes/GettingStarted.hs b/website/Shpadoinkle/Website/Types/Route/GettingStarted.hs similarity index 95% rename from website/Shpadoinkle/Website/Types/Routes/GettingStarted.hs rename to website/Shpadoinkle/Website/Types/Route/GettingStarted.hs index 144cf025..9ebbc35b 100644 --- a/website/Shpadoinkle/Website/Types/Routes/GettingStarted.hs +++ b/website/Shpadoinkle/Website/Types/Route/GettingStarted.hs @@ -6,7 +6,7 @@ {-# LANGUAGE TypeOperators #-} -module Shpadoinkle.Website.Types.Routes.GettingStarted where +module Shpadoinkle.Website.Types.Route.GettingStarted where import Data.Aeson (FromJSON, ToJSON) diff --git a/website/Shpadoinkle/Website/Types/Routes/Packages.hs b/website/Shpadoinkle/Website/Types/Route/Packages.hs similarity index 96% rename from website/Shpadoinkle/Website/Types/Routes/Packages.hs rename to website/Shpadoinkle/Website/Types/Route/Packages.hs index 5382e8f3..9abc5d7d 100644 --- a/website/Shpadoinkle/Website/Types/Routes/Packages.hs +++ b/website/Shpadoinkle/Website/Types/Route/Packages.hs @@ -6,7 +6,7 @@ {-# LANGUAGE TypeOperators #-} -module Shpadoinkle.Website.Types.Routes.Packages where +module Shpadoinkle.Website.Types.Route.Packages where import Data.Aeson (FromJSON, ToJSON) diff --git a/website/Shpadoinkle/Website/Types/Routes/Tutorial.hs b/website/Shpadoinkle/Website/Types/Route/Tutorial.hs similarity index 96% rename from website/Shpadoinkle/Website/Types/Routes/Tutorial.hs rename to website/Shpadoinkle/Website/Types/Route/Tutorial.hs index 5d30805b..4309ebf1 100644 --- a/website/Shpadoinkle/Website/Types/Routes/Tutorial.hs +++ b/website/Shpadoinkle/Website/Types/Route/Tutorial.hs @@ -6,7 +6,7 @@ {-# LANGUAGE TypeOperators #-} -module Shpadoinkle.Website.Types.Routes.Tutorial where +module Shpadoinkle.Website.Types.Route.Tutorial where import Data.Aeson (FromJSON, ToJSON) diff --git a/website/Shpadoinkle/Website/Types/Routes.hs b/website/Shpadoinkle/Website/Types/SPA.hs similarity index 76% rename from website/Shpadoinkle/Website/Types/Routes.hs rename to website/Shpadoinkle/Website/Types/SPA.hs index cdfc21e4..003ae66b 100644 --- a/website/Shpadoinkle/Website/Types/Routes.hs +++ b/website/Shpadoinkle/Website/Types/SPA.hs @@ -1,8 +1,6 @@ {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} @@ -10,50 +8,35 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeOperators #-} -{-# OPTIONS_GHC -fno-warn-unused-imports #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} {-# OPTIONS_GHC -fno-warn-redundant-constraints #-} +{-# OPTIONS_GHC -fno-warn-unused-imports #-} + + +module Shpadoinkle.Website.Types.SPA where + + +import Data.Proxy (Proxy (Proxy)) +import Data.Text (Text, pack) +import Servant.API (type (:<|>) (..), + type (:>)) +import Shpadoinkle (MonadJSM, Prop) +import Shpadoinkle.Html (href, + onClickM_) +import Shpadoinkle.Router (Redirect (..), + Routed (..), + View, getURI, + mapUnions, + navigate, + type (:>>)) +import Shpadoinkle.Website.Types.Route (Route (..)) +import qualified Shpadoinkle.Website.Types.Route.GettingStarted as GettingStarted +import qualified Shpadoinkle.Website.Types.Route.Packages as Packages +import qualified Shpadoinkle.Website.Types.Route.Tutorial as Tutorial +import Shpadoinkle.Website.Types.ViewModel (ViewModel) -module Shpadoinkle.Website.Types.Routes where - - -import Data.Aeson (FromJSON, - ToJSON) -import Data.Proxy (Proxy (Proxy)) -import Data.Text (Text, pack) -import GHC.Generics (Generic) -import Servant.API (type (:<|>) (..), - type (:>)) -import Shpadoinkle (MonadJSM, - NFData, Prop) -import Shpadoinkle.Html (href, - onClickM_) -import Shpadoinkle.Router (Redirect (..), - Routed (..), - View, getURI, - mapUnions, - navigate, - type (:>>)) -import Shpadoinkle.Website.Types.Frontend (Frontend) -import qualified Shpadoinkle.Website.Types.Routes.GettingStarted as GettingStarted -import qualified Shpadoinkle.Website.Types.Routes.Packages as Packages -import qualified Shpadoinkle.Website.Types.Routes.Tutorial as Tutorial -import Shpadoinkle.Widgets.Types.Physical (Toggle) - - -type TopModel = (Toggle, Frontend) -type View' m = View m TopModel - - -data Route - = RHome - | RConcepts - | RGettingStarted GettingStarted.Route - | RPackages Packages.Route - | RTutorial Tutorial.Route - | RSandbox - | RFourOhFour - deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) +type View' m = View m ViewModel type SPA m @@ -63,14 +46,14 @@ type SPA m :<|> "concepts" :> View' m :<|> "docs" :> "concepts.html" :> View' m - :<|> "getting-started" :> GettingStarted.SPA m TopModel - :<|> "docs" :> "getting-started" :> GettingStarted.LegacySPA m TopModel + :<|> "getting-started" :> GettingStarted.SPA m ViewModel + :<|> "docs" :> "getting-started" :> GettingStarted.LegacySPA m ViewModel - :<|> "packages" :> Packages.SPA m TopModel - :<|> "docs" :> "packages" :> Packages.LegacySPA m TopModel + :<|> "packages" :> Packages.SPA m ViewModel + :<|> "docs" :> "packages" :> Packages.LegacySPA m ViewModel - :<|> "tutorial" :> Tutorial.SPA m TopModel - :<|> "docs" :> "tutorial" :> Tutorial.LegacySPA m TopModel + :<|> "tutorial" :> Tutorial.SPA m ViewModel + :<|> "docs" :> "tutorial" :> Tutorial.LegacySPA m ViewModel :<|> "sandbox" :> View' m :<|> "404" :> View' m diff --git a/website/Shpadoinkle/Website/Types/Social.hs b/website/Shpadoinkle/Website/Types/Social.hs index 5958e83a..e55f642e 100644 --- a/website/Shpadoinkle/Website/Types/Social.hs +++ b/website/Shpadoinkle/Website/Types/Social.hs @@ -6,9 +6,9 @@ module Shpadoinkle.Website.Types.Social where -import Data.Text (Text) -import GHC.Generics (Generic) -import Shpadoinkle.Website.Types.Frontend (Frontend (..)) +import Data.Text (Text) +import GHC.Generics (Generic) +import Shpadoinkle.Website.Types.Route (Route (..)) data Social = Social @@ -18,21 +18,21 @@ data Social = Social } deriving Generic -toSocial :: Frontend -> Social +toSocial :: Route -> Social toSocial = prependTitle . \case - MHome _ -> Social "" + RHome -> Social "" "A new Functional UI programming paradigm" Nothing - MConcepts -> Social "Concepts" + RConcepts -> Social "Concepts" "Learn the basic ideas need to write Shpadoinkle applications" Nothing - MGettingStarted _ -> Social "Getting Started" + RGettingStarted _ -> Social "Getting Started" "Instructions for getting stared using Shpadoinkle for application development" Nothing - MPackages _ -> Social "Reference" + RPackages _ -> Social "Reference" "Packages in the Shpadoinkle ecosystem" Nothing - MTutorial _ -> Social "Tutorial" + RTutorial _ -> Social "Tutorial" "Step by step guide, writing a simple Shpadoinkle project" Nothing _ -> Social "404" diff --git a/website/Shpadoinkle/Website/Types/ViewModel.hs b/website/Shpadoinkle/Website/Types/ViewModel.hs new file mode 100644 index 00000000..72d90054 --- /dev/null +++ b/website/Shpadoinkle/Website/Types/ViewModel.hs @@ -0,0 +1,21 @@ +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} + + +module Shpadoinkle.Website.Types.ViewModel where + + +import Data.Aeson (FromJSON, ToJSON) +import GHC.Generics (Generic) +import Shpadoinkle (NFData) +import Shpadoinkle.Website.Types.PageModel (PageModel) +import Shpadoinkle.Website.Types.Route (Route) +import Shpadoinkle.Widgets.Types.Physical (Toggle) + + +data ViewModel = ViewModel + { mobileMenu :: Toggle + , currentRoute :: Route + , pageModel :: PageModel + } + deriving (Eq, Ord, Read, Show, Generic, FromJSON, ToJSON, NFData) diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index e7346e45..163e7447 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -7,7 +7,6 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TupleSections #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-} {-# OPTIONS_GHC -fno-warn-redundant-constraints #-} @@ -15,7 +14,7 @@ module Shpadoinkle.Website.View where -import Control.Lens (_2, (%~), (&), (.~), +import Control.Lens ((%~), (&), (.~), (^.)) import Control.Monad (void) import Control.Monad.IO.Class (MonadIO, liftIO) @@ -35,11 +34,11 @@ import Shpadoinkle.Website.Style (documentation) import Shpadoinkle.Website.Types.CurrentYear import Shpadoinkle.Website.Types.Effects.Example import Shpadoinkle.Website.Types.Example (Example) -import Shpadoinkle.Website.Types.Frontend import Shpadoinkle.Website.Types.Home -import Shpadoinkle.Website.Types.Routes +import Shpadoinkle.Website.Types.PageModel +import Shpadoinkle.Website.Types.Route import Shpadoinkle.Website.Types.Social (toSocial) -import Shpadoinkle.Widgets.Types.Physical (Toggle (..)) +import Shpadoinkle.Website.Types.ViewModel import UnliftIO.Async (Concurrently (..), async) @@ -51,21 +50,23 @@ fourOhFour :: Html m a fourOhFour = h2_ [ "404" ] -topClass :: Frontend -> ClassList +topClass :: Route -> ClassList topClass = \case - MHome _ -> mempty - _ -> documentation + RHome -> mempty + _ -> documentation -view :: (MonadJSM m, ExampleEffects m) => CurrentYear -> (Toggle, Frontend) -> Html m (Toggle, Frontend) -view cy (t, fe) = div [ class' $ topClass fe ] . wrapper cy t fe $ case fe of - MHome x -> onSum #_MHome $ Home.view x - MConcepts -> Documentation.concepts - MGettingStarted gsr -> Documentation.gettingStarted gsr - MPackages pr -> Documentation.packages pr - MTutorial tutr -> Documentation.tutorial tutr - MSandbox -> "" - MFourOhFour -> fourOhFour +view :: (MonadJSM m, ExampleEffects m) => CurrentYear -> ViewModel -> Html m ViewModel +view cy vm = div [ class' $ topClass cur ] . wrapper cy (vm ^. #mobileMenu) cur $ + case (cur, vm ^. #pageModel) of + (RHome, MHome x) -> onSum #_MHome $ Home.view x + (RConcepts, MStatic) -> Documentation.concepts cur + (RGettingStarted gsr, MStatic) -> Documentation.gettingStarted gsr cur + (RPackages pr, MStatic) -> Documentation.packages pr cur + (RTutorial tutr, MStatic) -> Documentation.tutorial tutr cur + (RSandbox, MStatic) -> "" + _ -> fourOhFour + where cur = vm ^. #currentRoute compileExamples :: forall m. (MonadUnliftIO m, MonadJSM m, ExampleEffects m) => Home -> m Home @@ -86,26 +87,21 @@ genExampleTokens :: MonadIO m => m (Examples SnowToken) genExampleTokens = liftIO $ Examples <$> genSnowToken <*> genSnowToken <*> genSnowToken -start :: MonadIO m => Route -> m (Toggle, Frontend) -start = fmap (mempty, ) . \case - RHome -> MHome . emptyHome <$> genExampleTokens - RConcepts -> pure MConcepts - RGettingStarted x -> pure $ MGettingStarted x - RPackages x -> pure $ MPackages x - RTutorial x -> pure $ MTutorial x - RFourOhFour -> pure MFourOhFour - RSandbox -> pure MSandbox +start :: MonadIO m => Route -> m ViewModel +start r = ViewModel mempty r <$> case r of + RHome -> MHome . emptyHome <$> genExampleTokens + _ -> pure MStatic startJS :: (MonadUnliftIO m, MonadJSM m, ExampleEffects m) - => TVar (Toggle, Frontend) -> Route -> m (Toggle, Frontend) + => TVar ViewModel -> Route -> m ViewModel startJS model r = do - t@(_, fe) <- start r - setTitle $ toSocial fe ^. #title - case fe of + vm <- start r + setTitle $ toSocial r ^. #title + case vm ^. #pageModel of MHome home' -> void . async $ do corn <- compileExamples home' - atomically . modifyTVar model $ _2 . #_MHome .~ corn + atomically . modifyTVar model $ #pageModel . #_MHome .~ corn _ -> pure () - return t + return vm diff --git a/website/assets/expando.svg b/website/assets/expando.svg new file mode 100644 index 00000000..1f9f8200 --- /dev/null +++ b/website/assets/expando.svg @@ -0,0 +1,3 @@ + + + diff --git a/website/assets/style.css b/website/assets/style.css index 75530f11..70acc5e5 100644 --- a/website/assets/style.css +++ b/website/assets/style.css @@ -1272,6 +1272,9 @@ footer { width: 21rem; z-index: 0; } +.side-nav__bold { + font-weight: bold; +} @media (min-width: 768px) { .side-nav { display: flex; @@ -1306,6 +1309,12 @@ footer { } .side-nav__expand__icon { margin-left: auto; + transform: rotate(90deg); + transition: all ease-in-out 0.5s; + transition-property: color, transform; +} +.expanded{ + transform: rotate(0) !important; } .side-nav__expand__list { padding-left: 2rem; diff --git a/widgets/Shpadoinkle-widgets.cabal b/widgets/Shpadoinkle-widgets.cabal index 60a43529..9406542b 100644 --- a/widgets/Shpadoinkle-widgets.cabal +++ b/widgets/Shpadoinkle-widgets.cabal @@ -41,7 +41,7 @@ common ghc-options library - imports: ghc-options + import: ghc-options exposed-modules: Shpadoinkle.Widgets.Form.Input @@ -94,7 +94,7 @@ library test-suite unit - imports: ghc-options + import: ghc-options type: exitcode-stdio-1.0 diff --git a/widgets/Shpadoinkle/Widgets/Types/Core.hs b/widgets/Shpadoinkle/Widgets/Types/Core.hs index 26c52f19..9085dded 100644 --- a/widgets/Shpadoinkle/Widgets/Types/Core.hs +++ b/widgets/Shpadoinkle/Widgets/Types/Core.hs @@ -64,6 +64,10 @@ class Humanize a where {-# INLINE humanize #-} +instance Humanize () where + humanize _ = pack "∅" + + instance Humanize Text where humanize = id {-# INLINE humanize #-} -- GitLab From 2bb44bca2c271e1ffc157eab94df78b74abfc8f8 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 22 Apr 2021 15:53:31 -0600 Subject: [PATCH 071/116] expand sorta animated --- website/assets/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/assets/style.css b/website/assets/style.css index 70acc5e5..04589ee1 100644 --- a/website/assets/style.css +++ b/website/assets/style.css @@ -1310,7 +1310,7 @@ footer { .side-nav__expand__icon { margin-left: auto; transform: rotate(90deg); - transition: all ease-in-out 0.5s; + transition: all ease-in-out 0.2s; transition-property: color, transform; } .expanded{ -- GitLab From 488dd74c882b11a8a83b8ec83ff203864d496b76 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 22 Apr 2021 15:54:31 -0600 Subject: [PATCH 072/116] change guide order --- website/Shpadoinkle/Website/Page/Documentation.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/Shpadoinkle/Website/Page/Documentation.hs b/website/Shpadoinkle/Website/Page/Documentation.hs index bfc3e036..23a35383 100644 --- a/website/Shpadoinkle/Website/Page/Documentation.hs +++ b/website/Shpadoinkle/Website/Page/Documentation.hs @@ -101,8 +101,8 @@ sideNavLink cur toR r = li_ docsWrap :: (MonadJSM m, Humanize r) => r -> [Html m a] -> Route -> Html m a docsWrap r content' cur = section [ class' flex ] [ nav [ class' side_nav ] - [ top cur RConcepts "Basic Concept" Nothing - , expandable cur RGettingStarted "Getting Started" + [ expandable cur RGettingStarted "Getting Started" + , top cur RConcepts "Basic Concept" Nothing , expandable cur RTutorial "Tutorial" , expandable cur RPackages "Reference" ] -- GitLab From c033d2e5fa133e0d7e37ca86e61ff395af817628 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 22 Apr 2021 15:56:35 -0600 Subject: [PATCH 073/116] mobile nav should, nav --- website/Shpadoinkle/Website/Partials/Nav.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/Shpadoinkle/Website/Partials/Nav.hs b/website/Shpadoinkle/Website/Partials/Nav.hs index 09f99245..fb91be2c 100644 --- a/website/Shpadoinkle/Website/Partials/Nav.hs +++ b/website/Shpadoinkle/Website/Partials/Nav.hs @@ -33,8 +33,8 @@ navLinkDesktop fe n = li [ class' nav__link ] $ : [ img' [ class' nav__link_underline, src $(assetLink "/assets/nav_line.svg") ] | toActive fe == Just n ] -navLinkMobile :: Nav -> Html m a -navLinkMobile n = a [ class' nav_mobile__link, href "#" ] [ text $ humanize n ] +navLinkMobile :: MonadJSM m => Nav -> Html m a +navLinkMobile n = a [ class' nav_mobile__link, goTo $ toRoute n ] [ text $ humanize n ] view :: MonadJSM m => Toggle -> Route -> [Html m Toggle] -- GitLab From 626c687c06ac243152f81c279dee6c364f280699 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 22 Apr 2021 16:14:38 -0600 Subject: [PATCH 074/116] serve the git hash --- website/default.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/website/default.nix b/website/default.nix index 25acbace..015ad223 100644 --- a/website/default.nix +++ b/website/default.nix @@ -42,4 +42,5 @@ in ln -s ${./assets} $out/assets ln -s ${opti (setTarget pkgsJS.haskell.packages.${util.compilerjs}.Shpadoinkle-website "run")}/bin/run.jsexe/${file} $out/all.min.js ${setTarget pkgs.haskell.packages.${compiler}.Shpadoinkle-website "disembodied"}/bin/disembodied -o $out + echo ${pkgs.lib.commitIdFromGitRepo ../.git} > $out/version '' -- GitLab From 6b7762c676f47a06229747bac91edd972fb62c3e Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 22 Apr 2021 16:29:32 -0600 Subject: [PATCH 075/116] build passes --- .../Shpadoinkle-developer-tools.cabal | 61 ++++++++----------- examples/Shpadoinkle-examples.cabal | 1 - examples/servant-crud/Types.hs | 11 +--- nix/overlay-shpadoinkle.nix | 4 +- 4 files changed, 29 insertions(+), 48 deletions(-) diff --git a/developer-tools/Shpadoinkle-developer-tools.cabal b/developer-tools/Shpadoinkle-developer-tools.cabal index b443e7e0..b4a44d09 100644 --- a/developer-tools/Shpadoinkle-developer-tools.cabal +++ b/developer-tools/Shpadoinkle-developer-tools.cabal @@ -37,17 +37,20 @@ common ghc-options -fwarn-identities -library - import: ghc-options - - exposed-modules: - Shpadoinkle.DeveloperTools - - other-modules: - Main +common ghcjs-options + ghcjs-options: + -Wall + -Wcompat + -fno-warn-missing-home-modules + -fwarn-redundant-constraints + -fwarn-incomplete-uni-patterns + -fwarn-tabs + -fwarn-incomplete-record-updates + -fwarn-identities + -O2 - hs-source-dirs: ./. +common build-depends build-depends: Shpadoinkle , Shpadoinkle-backend-pardiff @@ -62,14 +65,25 @@ library , time , unliftio - if !flag(development) +library + import: ghc-options, ghcjs-options, build-depends + + exposed-modules: + Shpadoinkle.DeveloperTools + + other-modules: + Main + + hs-source-dirs: ./. + + if flag(development) cpp-options: -DDEVELOPMENT default-language: Haskell2010 executable devtools - import: ghc-options + import: ghc-options, ghcjs-options, build-depends main-is: Main.hs @@ -78,29 +92,4 @@ executable devtools hs-source-dirs: ./. - ghcjs-options: - -Wall - -Wcompat - -fno-warn-missing-home-modules - -fwarn-redundant-constraints - -fwarn-incomplete-uni-patterns - -fwarn-tabs - -fwarn-incomplete-record-updates - -fwarn-identities - -O2 - - build-depends: - Shpadoinkle - , Shpadoinkle-backend-pardiff - , Shpadoinkle-html - , base >=4.12.0 && <4.16 - , containers - , jsaddle - , lens - , pretty-show - , stm - , text - , time - , unliftio - default-language: Haskell2010 diff --git a/examples/Shpadoinkle-examples.cabal b/examples/Shpadoinkle-examples.cabal index 4a7dfbfe..fe33edda 100644 --- a/examples/Shpadoinkle-examples.cabal +++ b/examples/Shpadoinkle-examples.cabal @@ -41,7 +41,6 @@ common ghcjs-options -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities - -dedupe -O2 diff --git a/examples/servant-crud/Types.hs b/examples/servant-crud/Types.hs index 622719e6..041be82a 100644 --- a/examples/servant-crud/Types.hs +++ b/examples/servant-crud/Types.hs @@ -6,10 +6,10 @@ {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE InstanceSigs #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuantifiedConstraints #-} @@ -17,7 +17,6 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-} -{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} @@ -29,10 +28,7 @@ module Types (module Types, module Types.Prim) where -import Control.Lens as Lens (Identity, - makeFieldsNoPrefix, - makeLenses, - makePrisms, view, +import Control.Lens as Lens (Identity, view, (^.)) import Control.Monad.Except (MonadError (throwError), MonadTrans (..)) @@ -157,9 +153,6 @@ instance Validate SpaceCraftUpdate where } -makeFieldsNoPrefix ''SpaceCraftUpdate - - data Roster = Roster { sort :: SortCol [SpaceCraft] , search :: Input Search diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index ed1c02d3..96665d93 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -168,7 +168,7 @@ in { ''; }); - websiteOverride = x: x.overrideAttrs (old: { + websiteOverride = x: addDev (x.overrideAttrs (old: { buildInputs = old.buildInputs ++ [ super.asciidoctor ]; prePatch = let brand = import ../website/brand.nix {}; in '' ${old.prePatch} @@ -176,7 +176,7 @@ in { cp -r ${../website/docs} docs chmod -R +rw docs ''; - }); + })); hpkgs = { -- GitLab From 133a002fe11a10fea6dbdb3ed45a407fb0b145fc Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 22 Apr 2021 16:31:35 -0600 Subject: [PATCH 076/116] include keybase --- website/default.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/website/default.nix b/website/default.nix index 015ad223..eee31a2c 100644 --- a/website/default.nix +++ b/website/default.nix @@ -42,5 +42,6 @@ in ln -s ${./assets} $out/assets ln -s ${opti (setTarget pkgsJS.haskell.packages.${util.compilerjs}.Shpadoinkle-website "run")}/bin/run.jsexe/${file} $out/all.min.js ${setTarget pkgs.haskell.packages.${compiler}.Shpadoinkle-website "disembodied"}/bin/disembodied -o $out + cp ${./keybase.txt} $out/keybase.txt echo ${pkgs.lib.commitIdFromGitRepo ../.git} > $out/version '' -- GitLab From c68689e92653017e52a7f3cd118d93fd3a035637 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 22 Apr 2021 16:34:00 -0600 Subject: [PATCH 077/116] add landing logo to build --- nix/overlay-shpadoinkle.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index 96665d93..e5e03774 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -174,6 +174,7 @@ in { ${old.prePatch} cp ${brand.icons.faviconHTML} favicon.html cp -r ${../website/docs} docs + cp ${brand.logo.full.svg { color = "bright_yellow"; }} ./website/assets/landing_logo.svg chmod -R +rw docs ''; })); -- GitLab From 2a6788652a76f918804790415d7d83e09e42e10b Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 22 Apr 2021 16:37:33 -0600 Subject: [PATCH 078/116] path fix --- nix/overlay-shpadoinkle.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index e5e03774..2c8d03d6 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -174,7 +174,7 @@ in { ${old.prePatch} cp ${brand.icons.faviconHTML} favicon.html cp -r ${../website/docs} docs - cp ${brand.logo.full.svg { color = "bright_yellow"; }} ./website/assets/landing_logo.svg + cp ${brand.logo.full.svg { color = "bright_yellow"; }} assets/landing_logo.svg chmod -R +rw docs ''; })); -- GitLab From 7c2c2c5075c529a699367227bfe4046902bc2ecb Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 22 Apr 2021 16:38:05 -0600 Subject: [PATCH 079/116] symlinks --- nix/overlay-shpadoinkle.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index 2c8d03d6..fc04eea1 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -172,9 +172,9 @@ in { buildInputs = old.buildInputs ++ [ super.asciidoctor ]; prePatch = let brand = import ../website/brand.nix {}; in '' ${old.prePatch} - cp ${brand.icons.faviconHTML} favicon.html - cp -r ${../website/docs} docs - cp ${brand.logo.full.svg { color = "bright_yellow"; }} assets/landing_logo.svg + ln -s ${brand.icons.faviconHTML} favicon.html + ln -s ${../website/docs} docs + ln -s ${brand.logo.full.svg { color = "bright_yellow"; }} assets/landing_logo.svg chmod -R +rw docs ''; })); -- GitLab From 4b4f8944c079cc325b3f9c7946da4b81636ad508 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 22 Apr 2021 16:40:10 -0600 Subject: [PATCH 080/116] mofit --- nix/overlay-shpadoinkle.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index fc04eea1..a43659f7 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -175,7 +175,6 @@ in { ln -s ${brand.icons.faviconHTML} favicon.html ln -s ${../website/docs} docs ln -s ${brand.logo.full.svg { color = "bright_yellow"; }} assets/landing_logo.svg - chmod -R +rw docs ''; })); -- GitLab From 7ba2ef02786d7fdf308d3b84b6e37075355b5c02 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 22 Apr 2021 16:45:34 -0600 Subject: [PATCH 081/116] mixed bag --- nix/overlay-shpadoinkle.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index a43659f7..fa39300e 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -173,7 +173,8 @@ in { prePatch = let brand = import ../website/brand.nix {}; in '' ${old.prePatch} ln -s ${brand.icons.faviconHTML} favicon.html - ln -s ${../website/docs} docs + cp -r ${../website/docs} docs + chmod -R +rw docs ln -s ${brand.logo.full.svg { color = "bright_yellow"; }} assets/landing_logo.svg ''; })); -- GitLab From f42236a6889c1401c247684fcd288727e8caa9b3 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 22 Apr 2021 17:00:07 -0600 Subject: [PATCH 082/116] faster website build on ci --- .gitlab-ci.yml | 2 +- website/default.nix | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6554faa1..d4fc2380 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -116,7 +116,7 @@ Website: - Packages GHCJS 8.6 on Linux - Packages GHC 8.6.5 on Linux script: - - nix-build website --fallback + - nix-build website --arg ci true --fallback Isreal: stage: Push Artifacts diff --git a/website/default.nix b/website/default.nix index eee31a2c..53904f20 100644 --- a/website/default.nix +++ b/website/default.nix @@ -1,4 +1,5 @@ { compiler ? "ghc865" +, ci ? false , system ? builtins.currentSystem , chan ? (import ../nix/chan.nix) , optimize ? true @@ -33,6 +34,7 @@ let ./Setup copy ${y} runHook postInstall '';} )); + setMay = if ci then (x: _: x) else setTarget; in pkgs.runCommand "website" { LANG = "C.UTF-8"; @@ -40,8 +42,8 @@ in mkdir $out ${brand.icons.mkIcons brand.logo.thumbnail "$out"} ln -s ${./assets} $out/assets - ln -s ${opti (setTarget pkgsJS.haskell.packages.${util.compilerjs}.Shpadoinkle-website "run")}/bin/run.jsexe/${file} $out/all.min.js - ${setTarget pkgs.haskell.packages.${compiler}.Shpadoinkle-website "disembodied"}/bin/disembodied -o $out + ln -s ${opti (setMay pkgsJS.haskell.packages.${util.compilerjs}.Shpadoinkle-website "run")}/bin/run.jsexe/${file} $out/all.min.js + ${setMay pkgs.haskell.packages.${compiler}.Shpadoinkle-website "disembodied"}/bin/disembodied -o $out cp ${./keybase.txt} $out/keybase.txt echo ${pkgs.lib.commitIdFromGitRepo ../.git} > $out/version '' -- GitLab From e0ed8127e99a0629823014a410c2b556b9f3acde Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 22 Apr 2021 17:02:16 -0600 Subject: [PATCH 083/116] test --- website/Shpadoinkle/Website/Partials/Nav.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/website/Shpadoinkle/Website/Partials/Nav.hs b/website/Shpadoinkle/Website/Partials/Nav.hs index fb91be2c..af5af2da 100644 --- a/website/Shpadoinkle/Website/Partials/Nav.hs +++ b/website/Shpadoinkle/Website/Partials/Nav.hs @@ -51,5 +51,6 @@ view t r = pure $ div [ class' started_header ] $ ] ] <> case t of Closed _ -> [] - Open -> [ H.nav [ class' nav_mobile ] [ div [ class' nav_mobile__wrapper ] $ navLinkMobile <$> [minBound..maxBound] ] - ] + Open -> [ H.nav [ class' nav_mobile ] + [ div [ class' nav_mobile__wrapper ] $ navLinkMobile <$> [minBound..maxBound] ] + ] -- GitLab From 9a2fc6e02bb91fe5b626845c31b817b6bcb5e64b Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Fri, 23 Apr 2021 16:48:52 -0600 Subject: [PATCH 084/116] fixed links, icons, and added highlight.js --- nix/overlay-shpadoinkle.nix | 2 + .../Shpadoinkle/Website/Page/Documentation.hs | 6 +-- .../Shpadoinkle/Website/Partials/Template.hs | 45 +++++++++++-------- website/assets/animate.js | 6 ++- website/assets/hljs.js | 21 +++++++++ website/assets/style.css | 3 +- website/docs/concepts.adoc | 2 + .../index.adoc => getting-started.adoc} | 5 ++- .../adding-to-your-project.adoc | 4 +- .../getting-started/extend-an-example.adoc | 6 ++- .../{packages/index.adoc => packages.adoc} | 6 +++ website/docs/packages/backends.adoc | 3 +- website/docs/packages/console.adoc | 2 +- website/docs/packages/core.adoc | 2 + website/docs/packages/html.adoc | 2 +- website/docs/packages/lens.adoc | 4 +- website/docs/packages/router.adoc | 2 +- website/docs/packages/widgets.adoc | 2 +- .../{tutorial/index.adoc => tutorial.adoc} | 6 ++- website/docs/tutorial/calculator.adoc | 6 ++- website/docs/tutorial/composing.adoc | 4 +- .../docs/tutorial/immediate-execution.adoc | 6 ++- 22 files changed, 104 insertions(+), 41 deletions(-) create mode 100644 website/assets/hljs.js rename website/docs/{getting-started/index.adoc => getting-started.adoc} (96%) rename website/docs/{packages/index.adoc => packages.adoc} (95%) rename website/docs/{tutorial/index.adoc => tutorial.adoc} (96%) diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index fa39300e..e515df16 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -105,6 +105,8 @@ "*.md" ".*.yml" "*docs*" + "*landing_logo.svg" + "*favicon.html" ]; diff --git a/website/Shpadoinkle/Website/Page/Documentation.hs b/website/Shpadoinkle/Website/Page/Documentation.hs index 23a35383..9d56f7c2 100644 --- a/website/Shpadoinkle/Website/Page/Documentation.hs +++ b/website/Shpadoinkle/Website/Page/Documentation.hs @@ -33,7 +33,7 @@ concepts = docsWrap ("Concepts" :: Text) $(embedAsciidoc "./docs/concepts.adoc") gettingStarted :: MonadJSM m => GettingStarted.Route -> Route -> Html m a gettingStarted r = docsWrap r $ case r of GettingStarted.RGSIndex -> - $(embedAsciidoc "./docs/getting-started/index.adoc") + $(embedAsciidoc "./docs/getting-started.adoc") GettingStarted.RGSAddingToYourProject -> $(embedAsciidoc "./docs/getting-started/adding-to-your-project.adoc") GettingStarted.RGSExtendAnExample -> @@ -42,7 +42,7 @@ gettingStarted r = docsWrap r $ case r of packages :: MonadJSM m => Packages.Route -> Route -> Html m a packages r = docsWrap r $ case r of - Packages.RPIndex -> $(embedAsciidoc "./docs/packages/index.adoc") + Packages.RPIndex -> $(embedAsciidoc "./docs/packages.adoc") Packages.RPCore -> $(embedAsciidoc "./docs/packages/core.adoc") Packages.RPConsole -> $(embedAsciidoc "./docs/packages/console.adoc") Packages.RPBackends -> $(embedAsciidoc "./docs/packages/backends.adoc") @@ -54,7 +54,7 @@ packages r = docsWrap r $ case r of tutorial :: MonadJSM m => Tutorial.Route -> Route -> Html m a tutorial r = docsWrap r $ case r of - Tutorial.RTIndex -> $(embedAsciidoc "./docs/tutorial/index.adoc") + Tutorial.RTIndex -> $(embedAsciidoc "./docs/tutorial.adoc") Tutorial.RTCalculator -> $(embedAsciidoc "./docs/tutorial/calculator.adoc") Tutorial.RTImmediateExecution -> $(embedAsciidoc "./docs/tutorial/immediate-execution.adoc") Tutorial.RTComposing -> $(embedAsciidoc "./docs/tutorial/composing.adoc") diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index b9ad7bbe..14c05679 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -34,35 +34,44 @@ import Shpadoinkle.Website.Types.ViewModel (ViewModel) import Shpadoinkle.Widgets.Types.Physical (Toggle (..)) -codemirrorCDN :: Text -> Text -codemirrorCDN = mappend "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.59.4/" +cdnjs :: Text +cdnjs = "https://cdnjs.cloudflare.com/ajax/libs/" +codemirrorCDN, highlightCDN :: Text -> Text +codemirrorCDN = mappend $ cdnjs <> "codemirror/5.59.4/" +highlightCDN = mappend $ cdnjs <> "highlight.js/10.7.2/" favicons :: [Html m a] favicons = $(embedHtml "./favicon.html") +stylesheet :: Text -> Html m a +stylesheet x = link' [ rel "stylesheet", href x ] + + +javascript :: Text -> Html m a +javascript x = script [ src x ] [] + + headView :: forall m a. Env -> ViewModel -> Html m a headView ev vm = head_ $ [ meta' [ charset "UTF-8" ] , meta' [ content "width=device-width, initial-scale=1.0", name' "viewport" ] - , link' - [ rel "stylesheet" - , href $ codemirrorCDN "codemirror.min.css" - ] - , link' - [ rel "stylesheet" - , href $ codemirrorCDN "theme/darcula.min.css" - ] - , link' - [ rel "stylesheet" - , href $(assetLink "/assets/style.css") - ] + , stylesheet $ codemirrorCDN "codemirror.min.css" + , stylesheet $ codemirrorCDN "theme/darcula.min.css" + , stylesheet $ highlightCDN "styles/a11y-dark.min.css" + , stylesheet $(assetLink "/assets/style.css") + , stylesheet "https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" + , javascript $ entrypoint ev + , javascript $ codemirrorCDN "codemirror.min.js" + , javascript $ codemirrorCDN "mode/haskell/haskell.min.js" + , javascript $ highlightCDN "highlight.min.js" + , javascript $ highlightCDN "languages/haskell.min.js" + , javascript $ highlightCDN "languages/bash.min.js" + , javascript $ highlightCDN "languages/nix.min.js" + , javascript $(assetLink "/assets/animate.js") + , javascript $(assetLink "/assets/hljs.js") , toHydration vm - , script [ src $ entrypoint ev ] [] - , script [ src $ codemirrorCDN "codemirror.min.js" ] [] - , script [ src $ codemirrorCDN "mode/haskell/haskell.min.js" ] [] - , script [ src $(assetLink "/assets/animate.js") ] [] , link' [ rel "canonical" , href . pack . show diff --git a/website/assets/animate.js b/website/assets/animate.js index a287b8eb..5ac88ddd 100644 --- a/website/assets/animate.js +++ b/website/assets/animate.js @@ -1,3 +1,5 @@ +(() => { + const animClasses = [ ".feature__card" , ".feature-button" @@ -18,7 +20,7 @@ const observer = new IntersectionObserver((entries, _) => const init = () => animClasses.forEach(x => { - for (elm of document.querySelectorAll(x)){ + for (const elm of document.querySelectorAll(x)){ elm.classList.add("animate") observer.observe(elm) } @@ -34,3 +36,5 @@ window.history.pushState = new Proxy(window.history.pushState, { setTimeout(init, 0) + +})() diff --git a/website/assets/hljs.js b/website/assets/hljs.js new file mode 100644 index 00000000..e2d65a91 --- /dev/null +++ b/website/assets/hljs.js @@ -0,0 +1,21 @@ +(() => { + +const highlight = () => { + for (const code of document.getElementsByTagName("code")){ + if(!code.light && code.classList.length){ + hljs.highlightElement(code) + code.light = true + } + } +} + +const observer = new MutationObserver(highlight) + +const init = () => { + highlight() + observer.observe(document, { childList: true, subtree: true }) +} + +setTimeout(init, 0) + +})() diff --git a/website/assets/style.css b/website/assets/style.css index 04589ee1..1625ee19 100644 --- a/website/assets/style.css +++ b/website/assets/style.css @@ -513,12 +513,11 @@ video { padding: 1rem 0; } .listingblock { - padding: 2rem 0; + margin: 2rem 0; overflow-x: scroll; } .listingblock > .content > .highlight > code { border: 2px solid #e2e0d5; - background-color: transparent; padding: 15px 10px; } div.admonitionblock { diff --git a/website/docs/concepts.adoc b/website/docs/concepts.adoc index 01a8a8d0..ca02fddf 100644 --- a/website/docs/concepts.adoc +++ b/website/docs/concepts.adoc @@ -1,3 +1,5 @@ +:icons: font + Shpadoinkle is a new way to program user interfaces with Haskell. The core idea here is to model user interfaces as functions with this type: diff --git a/website/docs/getting-started/index.adoc b/website/docs/getting-started.adoc similarity index 96% rename from website/docs/getting-started/index.adoc rename to website/docs/getting-started.adoc index 9ebf60a5..784d5d09 100644 --- a/website/docs/getting-started/index.adoc +++ b/website/docs/getting-started.adoc @@ -1,4 +1,7 @@ -= Getting Started +:relfilesuffix: / +:relfileprefix: / +:gitlab: https://gitlab.com/platonic/shpadoinkle +:icons: font Shpadoinkle is built with Nix, but you can build however you like. `stack` and `cabal` are supported with or without Nix. diff --git a/website/docs/getting-started/adding-to-your-project.adoc b/website/docs/getting-started/adding-to-your-project.adoc index 9983acb1..a22ec92e 100644 --- a/website/docs/getting-started/adding-to-your-project.adoc +++ b/website/docs/getting-started/adding-to-your-project.adoc @@ -1,4 +1,6 @@ -= Adding to a Project +:relfilesuffix: / +:relfileprefix: / +:icons: font Adding Shpadoinkle to a project should be easy. diff --git a/website/docs/getting-started/extend-an-example.adoc b/website/docs/getting-started/extend-an-example.adoc index e81f3ef9..651381da 100644 --- a/website/docs/getting-started/extend-an-example.adoc +++ b/website/docs/getting-started/extend-an-example.adoc @@ -1,4 +1,6 @@ -= Extend an example +:relfilesuffix: / +:relfileprefix: / +:icons: font [source,bash] ---- @@ -11,7 +13,7 @@ cd Shpadoinkle Shpadoinkle works with both https://gitlab.haskell.org/ghc/ghc[GHC] and https://github.com/ghcjs/ghcjs[GHCjs]. [CAUTION] -Building from source can take a long time. If you run with `nix-build` it's highly recommended you use xref:getting-started/index.adoc#_cachix[Cachix]. +Building from source can take a long time. If you run with `nix-build` it's highly recommended you use xref:getting-started.adoc#_cachix[Cachix]. === GHCjs diff --git a/website/docs/packages/index.adoc b/website/docs/packages.adoc similarity index 95% rename from website/docs/packages/index.adoc rename to website/docs/packages.adoc index e7dae986..fb80ecbd 100644 --- a/website/docs/packages/index.adoc +++ b/website/docs/packages.adoc @@ -1,3 +1,9 @@ +:relfilesuffix: / +:relfileprefix: / +:gitlab: https://gitlab.com/platonic/shpadoinkle +:hackage: https://hackage.haskell.org/package/Shpadoinkle +:icons: font + Shpadoinkle is segregated into several packages that compose together or can be used separately. [TIP] diff --git a/website/docs/packages/backends.adoc b/website/docs/packages/backends.adoc index d46e09f1..adda82a7 100644 --- a/website/docs/packages/backends.adoc +++ b/website/docs/packages/backends.adoc @@ -1,4 +1,5 @@ -= Backends +:gitlab: https://gitlab.com/platonic/shpadoinkle +:icons: font Render backends are pluggable in Shpadoinkle. This project currently comes with three backends: diff --git a/website/docs/packages/console.adoc b/website/docs/packages/console.adoc index 8d5e0c5c..d3f380bf 100644 --- a/website/docs/packages/console.adoc +++ b/website/docs/packages/console.adoc @@ -1,4 +1,4 @@ -= Console +:icons: font https://shpadoinkle.org/console/index.html[Haddock] diff --git a/website/docs/packages/core.adoc b/website/docs/packages/core.adoc index fb1d94d0..e9161b2b 100644 --- a/website/docs/packages/core.adoc +++ b/website/docs/packages/core.adoc @@ -1,3 +1,5 @@ +:icons: font + https://shpadoinkle.org/core[Haddock] This module is for core types and logic. diff --git a/website/docs/packages/html.adoc b/website/docs/packages/html.adoc index 9a573f87..e6a78b5e 100644 --- a/website/docs/packages/html.adoc +++ b/website/docs/packages/html.adoc @@ -1,4 +1,4 @@ -= Html +:icons: font https://shpadoinkle.org/html[Haddock] diff --git a/website/docs/packages/lens.adoc b/website/docs/packages/lens.adoc index 489c7666..bd47954d 100644 --- a/website/docs/packages/lens.adoc +++ b/website/docs/packages/lens.adoc @@ -1,4 +1,6 @@ -= Lens +:relfilesuffix: / +:relfileprefix: / +:icons: font https://shpadoinkle.org/lens[Haddock] diff --git a/website/docs/packages/router.adoc b/website/docs/packages/router.adoc index 5785efbd..6a7c5f8a 100644 --- a/website/docs/packages/router.adoc +++ b/website/docs/packages/router.adoc @@ -1,4 +1,4 @@ -= Router +:icons: font https://shpadoinkle.org/router[Haddock] diff --git a/website/docs/packages/widgets.adoc b/website/docs/packages/widgets.adoc index 7d39072e..cebd639d 100644 --- a/website/docs/packages/widgets.adoc +++ b/website/docs/packages/widgets.adoc @@ -1,4 +1,4 @@ -= Widgets +:icons: font https://shpadoinkle.org/widgets[Haddock] diff --git a/website/docs/tutorial/index.adoc b/website/docs/tutorial.adoc similarity index 96% rename from website/docs/tutorial/index.adoc rename to website/docs/tutorial.adoc index 9fcad98e..0426d223 100644 --- a/website/docs/tutorial/index.adoc +++ b/website/docs/tutorial.adoc @@ -1,4 +1,6 @@ -= Tutorial +:relfilesuffix: / +:relfileprefix: / +:icons: font _This is for those who would like to think they know precisely what they mean._ @@ -9,7 +11,7 @@ _This is for those who would like to think they know precisely what they mean._ == Setup To begin, complete the setup described in the -xref:getting-started/index.adoc[Getting Started] section. +xref:getting-started.adoc[Getting Started] section. == HTML diff --git a/website/docs/tutorial/calculator.adoc b/website/docs/tutorial/calculator.adoc index 6f03fa9d..b6bd6466 100644 --- a/website/docs/tutorial/calculator.adoc +++ b/website/docs/tutorial/calculator.adoc @@ -1,11 +1,13 @@ -= Calculator +:relfilesuffix: / +:relfileprefix: / Here we will build a simple calculator, following pioneers of the space + http://weblog.luite.com/wordpress/?p=127[Luite Stegemann], https://reflex-frp.org/tutorial[Ryan Trinkle], and https://keera.co.uk/2020/05/28/building-a-reactive-calculator-in-haskell-1-5/[Ivan Perez]. -* xref:tutorial/index.adoc[<- Part 1, Basic concepts] +* xref:tutorial.adoc[<- Part 1, Basic concepts] * xref:tutorial/immediate-execution.adoc[Part 3: Immediate execution ->] * xref:tutorial/composing.adoc[Part 4, Composing HTML Elements ->] diff --git a/website/docs/tutorial/composing.adoc b/website/docs/tutorial/composing.adoc index fd164d91..748c544a 100644 --- a/website/docs/tutorial/composing.adoc +++ b/website/docs/tutorial/composing.adoc @@ -1,4 +1,6 @@ -= Composing Heterogeneous Elements +:relfilesuffix: / +:relfileprefix: / +:icons: font xref:tutorial/immediate-execution.adoc[<- Back to part 3, Emulating Immediate Execution] diff --git a/website/docs/tutorial/immediate-execution.adoc b/website/docs/tutorial/immediate-execution.adoc index 5958bdbb..b2c94a14 100644 --- a/website/docs/tutorial/immediate-execution.adoc +++ b/website/docs/tutorial/immediate-execution.adoc @@ -1,6 +1,8 @@ -= Emulating Immediate Execution +:relfilesuffix: / +:relfileprefix: / +:icons: font -* xref:tutorial/index.adoc[<- See part 1, Basic concepts] +* xref:tutorial.adoc[<- See part 1, Basic concepts] * xref:tutorial/calculator.adoc[<- Back to part 2, Simple calculator] All the prior articles on writing a calculator in GHCjs feature old school functionality, namely https://en.wikipedia.org/wiki/Calculator_input_methods#Immediate_execution[immediate execution], which is a terrible UX. If you ever make an actual calculator app, at least let your users type into an input the expression they want evaluated. However, this choice makes good sense, as it requires a state machine and some other properties that make it good for learning. -- GitLab From c7678d726a0eeb21db38a96ee3686709b30f034d Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Fri, 23 Apr 2021 20:37:55 -0600 Subject: [PATCH 085/116] add copy badge --- website/Shpadoinkle/Website/Partials/Template.hs | 1 + website/assets/hljs.js | 1 + website/assets/style.css | 6 ++++++ 3 files changed, 8 insertions(+) diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index 14c05679..2bded17a 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -69,6 +69,7 @@ headView ev vm = head_ $ , javascript $ highlightCDN "languages/haskell.min.js" , javascript $ highlightCDN "languages/bash.min.js" , javascript $ highlightCDN "languages/nix.min.js" + , javascript "https://unpkg.com/highlightjs-badge/highlightjs-badge.min.js" , javascript $(assetLink "/assets/animate.js") , javascript $(assetLink "/assets/hljs.js") , toHydration vm diff --git a/website/assets/hljs.js b/website/assets/hljs.js index e2d65a91..30af152a 100644 --- a/website/assets/hljs.js +++ b/website/assets/hljs.js @@ -7,6 +7,7 @@ const highlight = () => { code.light = true } } + highlightJsBadge({}) } const observer = new MutationObserver(highlight) diff --git a/website/assets/style.css b/website/assets/style.css index 1625ee19..9238cffd 100644 --- a/website/assets/style.css +++ b/website/assets/style.css @@ -520,6 +520,12 @@ video { border: 2px solid #e2e0d5; padding: 15px 10px; } +.hljs-comment{ + display: none; +} +.conum .hljs-comment{ + display: inline; +} div.admonitionblock { position: relative; margin: 3rem 0; -- GitLab From 94f229a9a71a6c025172acd801ea095c4c06be44 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Fri, 23 Apr 2021 20:57:49 -0600 Subject: [PATCH 086/116] use existing click to copy code instead --- website/Shpadoinkle/Website/Partials/Template.hs | 1 - website/assets/hljs.js | 11 ++++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index 2bded17a..14c05679 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -69,7 +69,6 @@ headView ev vm = head_ $ , javascript $ highlightCDN "languages/haskell.min.js" , javascript $ highlightCDN "languages/bash.min.js" , javascript $ highlightCDN "languages/nix.min.js" - , javascript "https://unpkg.com/highlightjs-badge/highlightjs-badge.min.js" , javascript $(assetLink "/assets/animate.js") , javascript $(assetLink "/assets/hljs.js") , toHydration vm diff --git a/website/assets/hljs.js b/website/assets/hljs.js index 30af152a..f61ac8e4 100644 --- a/website/assets/hljs.js +++ b/website/assets/hljs.js @@ -1,13 +1,22 @@ (() => { +const copyCode = code => { + code.title = "Click To Copy" + code.addEventListener("click", () => { + navigator.clipboard.writeText(code.textContent) + }) +} + const highlight = () => { for (const code of document.getElementsByTagName("code")){ if(!code.light && code.classList.length){ hljs.highlightElement(code) + if(code.classList.contains("language-bash")){ + copyCode(code) + } code.light = true } } - highlightJsBadge({}) } const observer = new MutationObserver(highlight) -- GitLab From c874dff08057f4616da19ce8a93ddb84d2e2f062 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Fri, 23 Apr 2021 20:58:27 -0600 Subject: [PATCH 087/116] parens --- website/assets/hljs.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/website/assets/hljs.js b/website/assets/hljs.js index f61ac8e4..9c83d670 100644 --- a/website/assets/hljs.js +++ b/website/assets/hljs.js @@ -2,9 +2,8 @@ const copyCode = code => { code.title = "Click To Copy" - code.addEventListener("click", () => { - navigator.clipboard.writeText(code.textContent) - }) + code.addEventListener("click", () => + navigator.clipboard.writeText(code.textContent)) } const highlight = () => { -- GitLab From ff33a72642b77f7ffe3e78683f3f1bf921462508 Mon Sep 17 00:00:00 2001 From: Jose Soto Date: Sat, 24 Apr 2021 01:01:47 -0700 Subject: [PATCH 088/116] Gitlab Nits fixes --- website/assets/style.css | 1723 +++++++++++++++++++------------------- 1 file changed, 865 insertions(+), 858 deletions(-) diff --git a/website/assets/style.css b/website/assets/style.css index 9238cffd..c0f841ac 100644 --- a/website/assets/style.css +++ b/website/assets/style.css @@ -1,112 +1,112 @@ -@font-face { - font-family: Adelle; - src: url(/assets/Adelle-Regular.woff2) format("woff2"); - font-weight: 400; - font-style: normal; + { + font-family: Adelle; + src: url(/assets/Adelle-Regular.woff2) format("woff2"); + font-weight: 400; + font-style: normal; } @font-face { - font-family: AdelleBold; - src: url(/assets/Adelle-Bold.woff2) format("woff2"); - font-weight: 400; - font-style: normal; + font-family: AdelleBold; + src: url(/assets/Adelle-Bold.woff2) format("woff2"); + font-weight: 400; + font-style: normal; } /*! modern-normalize v1.0.0 | MIT License | https://github.com/sindresorhus/modern-normalize */ *, ::after, ::before { - box-sizing: border-box; + box-sizing: border-box; } :root { - -moz-tab-size: 4; - -o-tab-size: 4; - tab-size: 4; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; } -.animate{ +.animate { opacity: 0; transform: translate3d(0, 20px, 0); transition: all ease-out 0.5s; transition-property: opacity, transform; } -.animate-show{ +.animate-show { opacity: 1; transform: translate3d(0, 0, 0); } html { - line-height: 1.15; - -webkit-text-size-adjust: 100%; + line-height: 1.15; + -webkit-text-size-adjust: 100%; } body { - margin: 0; + margin: 0; } body { - font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, + font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; } hr { - height: 0; - color: inherit; + height: 0; + color: inherit; } abbr[title] { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; } b, strong { - font-weight: bolder; + font-weight: bolder; } code, kbd, pre, samp { - font-family: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", Menlo, + font-family: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace; - font-size: 1em; + font-size: 1em; } small { - font-size: 80%; + font-size: 80%; } sub, sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; } sub { - bottom: -0.25em; + bottom: -0.25em; } sup { - top: -0.5em; + top: -0.5em; } table { - text-indent: 0; - border-color: inherit; + text-indent: 0; + border-color: inherit; } button, input, optgroup, select, textarea { - font-family: inherit; - font-size: 100%; - line-height: 1.15; - margin: 0; + font-family: inherit; + font-size: 100%; + line-height: 1.15; + margin: 0; } button, select { - text-transform: none; + text-transform: none; } [type="button"], button { - -webkit-appearance: button; + -webkit-appearance: button; } legend { - padding: 0; + padding: 0; } progress { - vertical-align: baseline; + vertical-align: baseline; } summary { - display: list-item; + display: list-item; } blockquote, dd, @@ -121,73 +121,69 @@ h6, hr, p, pre { - margin: 0; + margin: 0; } button { - background-color: transparent; - background-image: none; -} -button:focus { - outline: 1px dotted; - outline: 5px auto -webkit-focus-ring-color; + background-color: transparent; + background-image: none; } fieldset { - margin: 0; - padding: 0; + margin: 0; + padding: 0; } ol, ul { - list-style: none; - margin: 0; - padding: 0; + list-style: none; + margin: 0; + padding: 0; } html { - font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - line-height: 1.5; + line-height: 1.5; } body { - font-family: inherit; - line-height: inherit; + font-family: inherit; + line-height: inherit; } *, ::after, ::before { - box-sizing: border-box; - border-width: 0; - border-style: solid; - border-color: currentColor; + box-sizing: border-box; + border-width: 0; + border-style: solid; + border-color: currentColor; } hr { - border-top-width: 1px; + border-top-width: 1px; } img { - border-style: solid; + border-style: solid; } textarea { - resize: vertical; + resize: vertical; } input::-moz-placeholder, textarea::-moz-placeholder { - opacity: 1; - color: #a1a1aa; + opacity: 1; + color: #a1a1aa; } input:-ms-input-placeholder, textarea:-ms-input-placeholder { - opacity: 1; - color: #a1a1aa; + opacity: 1; + color: #a1a1aa; } input::placeholder, textarea::placeholder { - opacity: 1; - color: #a1a1aa; + opacity: 1; + color: #a1a1aa; } button { - cursor: pointer; + cursor: pointer; } table { - border-collapse: collapse; + border-collapse: collapse; } h1, h2, @@ -195,27 +191,27 @@ h3, h4, h5, h6 { - font-size: inherit; - font-weight: inherit; + font-size: inherit; + font-weight: inherit; } a { - color: inherit; - text-decoration: inherit; + color: inherit; + text-decoration: inherit; } button, input, optgroup, select, textarea { - padding: 0; - line-height: inherit; - color: inherit; + padding: 0; + line-height: inherit; + color: inherit; } code, kbd, pre, samp { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; } audio, @@ -226,171 +222,171 @@ img, object, svg, video { - display: block; - vertical-align: middle; + display: block; + vertical-align: middle; } img, video { - max-width: 100%; - height: auto; + max-width: 100%; + height: auto; } .container { - width: 100%; + width: 100%; } -@media (min-width: 640px) { - .container { - max-width: 640px; - } +@media (min-width: 768px) { + .container { + max-width: 768px; + } } @media (min-width: 768px) { - .container { - max-width: 768px; - } + .container { + max-width: 768px; + } } @media (min-width: 1024px) { - .container { - max-width: 1024px; - } + .container { + max-width: 1024px; + } } @media (min-width: 1280px) { - .container { - max-width: 1280px; - } + .container { + max-width: 1280px; + } } @media (min-width: 1536px) { - .container { - max-width: 1536px; - } + .container { + max-width: 1536px; + } } .rounded { - border-radius: 0.25rem; + border-radius: 0.25rem; } .flex { - display: flex; + display: flex; } .table { - display: table; + display: table; } .table-row { - display: table-row; + display: table-row; } .grid { - display: grid; + display: grid; } .hidden { - display: none; + display: none; } .flex-col { - flex-direction: column; + flex-direction: column; } .flex-wrap { - flex-wrap: wrap; + flex-wrap: wrap; } .items-center { - align-items: center; + align-items: center; } .justify-center { - justify-content: center; + justify-content: center; } .justify-between { - justify-content: space-between; + justify-content: space-between; } .flex-1 { - flex: 1 1 0%; + flex: 1 1 0%; } .font-bold { - font-family: AdelleBold, Verdana; + font-family: AdelleBold, Verdana; } .font-bold { - font-weight: 700; + font-weight: 700; } .h-32 { - height: 8rem; + height: 8rem; } .text-xs { - font-size: 0.75rem; - line-height: 1rem; + font-size: 0.75rem; + line-height: 1rem; } .text-3xl { - font-size: 1.875rem; - line-height: 2.25rem; + font-size: 1.875rem; + line-height: 2.25rem; } .leading-8 { - line-height: 2rem; + line-height: 2rem; } .mx-auto { - margin-left: auto; - margin-right: auto; + margin-left: auto; + margin-right: auto; } .ml-0 { - margin-left: 0; + margin-left: 0; } .mr-4 { - margin-right: 1rem; + margin-right: 1rem; } .mb-4 { - margin-bottom: 1rem; + margin-bottom: 1rem; } .mb-24 { - margin-bottom: 6rem; + margin-bottom: 6rem; } .max-w-sm { - max-width: 24rem; + max-width: 24rem; } .min-h-screen { - min-height: 100vh; + min-height: 100vh; } .opacity-0 { - opacity: 0; + opacity: 0; } .opacity-100 { - opacity: 1; + opacity: 1; } .p-2 { - padding: 0.5rem; + padding: 0.5rem; } .p-6 { - padding: 1.5rem; + padding: 1.5rem; } .p-10 { - padding: 2.5rem; + padding: 2.5rem; } .px-4 { - padding-left: 1rem; - padding-right: 1rem; + padding-left: 1rem; + padding-right: 1rem; } .px-8 { - padding-left: 2rem; - padding-right: 2rem; + padding-left: 2rem; + padding-right: 2rem; } .px-10 { - padding-left: 2.5rem; - padding-right: 2.5rem; + padding-left: 2.5rem; + padding-right: 2.5rem; } .py-16 { - padding-top: 4rem; - padding-bottom: 4rem; + padding-top: 4rem; + padding-bottom: 4rem; } .pt-0 { - padding-top: 0; + padding-top: 0; } .pt-12 { - padding-top: 3rem; + padding-top: 3rem; } .fixed { - position: fixed; + position: fixed; } .sticky { - position: sticky; + position: sticky; } .top-0 { - top: 0; + top: 0; } .right-0 { - right: 0; + right: 0; } .bottom-0 { - bottom: 0; + bottom: 0; } :root { --tw-shadow: 0 0 #0000; @@ -402,1112 +398,1123 @@ video { --tw-ring-shadow: 0 0 #0000; } .text-white { - --tw-text-opacity: 1; - color: rgba(255, 255, 255, var(--tw-text-opacity)); + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); } .w-full { - width: 100%; + width: 100%; } .gap-8 { - gap: 2rem; + gap: 2rem; } .gap-10 { - gap: 2.5rem; + gap: 2.5rem; } .grid-cols-4 { - grid-template-columns: repeat(4, minmax(0, 1fr)); + grid-template-columns: repeat(4, minmax(0, 1fr)); } .transform { - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - transform: translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + transform: translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); } .scale-90 { - --tw-scale-x: 0.9; - --tw-scale-y: 0.9; + --tw-scale-x: 0.9; + --tw-scale-y: 0.9; } .scale-100 { - --tw-scale-x: 1; - --tw-scale-y: 1; + --tw-scale-x: 1; + --tw-scale-y: 1; } .transition { - transition-property: background-color, border-color, color, fill, stroke, + transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; } .ease-in { - transition-timing-function: cubic-bezier(0.4, 0, 1, 1); + transition-timing-function: cubic-bezier(0.4, 0, 1, 1); } .ease-out { - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); } @-webkit-keyframes spin { - to { - transform: rotate(360deg); - } + to { + transform: rotate(360deg); + } } @keyframes spin { - to { - transform: rotate(360deg); - } + to { + transform: rotate(360deg); + } } @-webkit-keyframes ping { - 100%, - 75% { - transform: scale(2); - opacity: 0; - } + 100%, + 75% { + transform: scale(2); + opacity: 0; + } } @keyframes ping { - 100%, - 75% { - transform: scale(2); - opacity: 0; - } + 100%, + 75% { + transform: scale(2); + opacity: 0; + } } @-webkit-keyframes pulse { - 50% { - opacity: 0.5; - } + 50% { + opacity: 0.5; + } } @keyframes pulse { - 50% { - opacity: 0.5; - } + 50% { + opacity: 0.5; + } } @-webkit-keyframes bounce { - 0%, - 100% { - transform: translateY(-25%); - -webkit-animation-timing-function: cubic-bezier(0.8, 0, 1, 1); - animation-timing-function: cubic-bezier(0.8, 0, 1, 1); - } - 50% { - transform: none; - -webkit-animation-timing-function: cubic-bezier(0, 0, 0.2, 1); - animation-timing-function: cubic-bezier(0, 0, 0.2, 1); - } + 0%, + 100% { + transform: translateY(-25%); + -webkit-animation-timing-function: cubic-bezier(0.8, 0, 1, 1); + animation-timing-function: cubic-bezier(0.8, 0, 1, 1); + } + 50% { + transform: none; + -webkit-animation-timing-function: cubic-bezier(0, 0, 0.2, 1); + animation-timing-function: cubic-bezier(0, 0, 0.2, 1); + } } @keyframes bounce { - 0%, - 100% { - transform: translateY(-25%); - -webkit-animation-timing-function: cubic-bezier(0.8, 0, 1, 1); - animation-timing-function: cubic-bezier(0.8, 0, 1, 1); - } - 50% { - transform: none; - -webkit-animation-timing-function: cubic-bezier(0, 0, 0.2, 1); - animation-timing-function: cubic-bezier(0, 0, 0.2, 1); - } + 0%, + 100% { + transform: translateY(-25%); + -webkit-animation-timing-function: cubic-bezier(0.8, 0, 1, 1); + animation-timing-function: cubic-bezier(0.8, 0, 1, 1); + } + 50% { + transform: none; + -webkit-animation-timing-function: cubic-bezier(0, 0, 0.2, 1); + animation-timing-function: cubic-bezier(0, 0, 0.2, 1); + } } #preamble > .sectionbody > .paragraph > p { - padding: 1rem 0; + padding: 1rem 0; } .listingblock { - margin: 2rem 0; - overflow-x: scroll; + margin: 2rem 0; + overflow-x: scroll; } .listingblock > .content > .highlight > code { - border: 2px solid #e2e0d5; - padding: 15px 10px; + border: 2px solid #e2e0d5; + padding: 15px 10px; } -.hljs-comment{ - display: none; +.hljs-comment { + display: none; } -.conum .hljs-comment{ +.conum .hljs-comment { display: inline; } div.admonitionblock { - position: relative; - margin: 3rem 0; + position: relative; + margin: 3rem 0; } div.admonitionblock > table > tbody > tr > td.icon { - border-radius: 18px; - color: #fff; - display: flex; - height: 30px; - justify-content: flex-start; - left: 5px; - padding: 0 1.5rem; - position: absolute; - top: -15px; - width: 135px; + border-radius: 18px; + color: #fff; + display: flex; + height: 30px; + justify-content: flex-start; + left: 5px; + padding: 0 1.5rem; + position: absolute; + top: -15px; + width: 135px; } div.admonitionblock > table > tbody > tr > td.content { - padding: 20px 10px 15px 1rem; + padding: 20px 10px 15px 1rem; } div.admonitionblock.caution { - border: 2px dashed #ac2a34; + border: 2px dashed #ac2a34; } div.admonitionblock.caution > table > tbody > tr > td.icon { - background: #ac2a34; + background: #ac2a34; } div.admonitionblock.caution > table > tbody > tr > td.content { - color: #ac2a34; + color: #ac2a34; } div.admonitionblock.note { - border: 2px dashed #0f406b; + border: 2px dashed #0f406b; } div.admonitionblock.note > table > tbody > tr > td.icon { - background: #0f406b; + background: #0f406b; } div.admonitionblock.note > table > tbody > tr > td.content { - color: #0f406b; + color: #0f406b; } div.admonitionblock.warning { - border: 2px dashed #e37743; + border: 2px dashed #e37743; } div.admonitionblock.warning > table > tbody > tr > td.icon { - background: #e37743; + background: #e37743; } div.admonitionblock.warning > table > tbody > tr > td.content { - color: #e37743; + color: #e37743; } h1, h2, h3, h4, h5 { - padding: 1rem 0; + padding: 1rem 0; } h1 { - font-size: 1.875rem; + font-size: 1.875rem; } h2 { - font-size: 1.375rem; + font-size: 1.375rem; } h3 { - font-size: 1.5rem; + font-size: 1.5rem; } @media (min-width: 768px) { - h1 { - font-size: 3.75rem; - } - h2 { - font-size: 2.625rem; - } - h3 { - font-size: 1.325rem; - } + h1 { + font-size: 3.75rem; + } + h2 { + font-size: 2.625rem; + } + h3 { + font-size: 1.325rem; + } } .split-button:active { - transform: translate(2px, 2px); + transform: translate(2px, 2px); } .started-header { - background-image: url(/assets/get_started_header_bg.svg); - height: 96px; - background-position: center; - background-repeat: no-repeat; - background-size: cover; - margin-bottom: 3rem; - position: relative; - z-index: 50; + background-image: url(/assets/get_started_header_bg.svg); + height: 100px; + background-position: center; + background-repeat: no-repeat; + background-size: cover; + margin-bottom: 3rem; + position: relative; + z-index: 50; } @media (min-width: 768px) { - .started-header { - margin-bottom: -3.5rem; - } + .started-header { + margin-bottom: -3.5rem; + } } @media (min-width: 768px) { - .started-header { - background-image: url(/assets/get_started_header_bg.svg); - height: 156px; - } + .started-header { + background-image: url(/assets/get_started_header_bg.svg); + height: 156px; + } +} +@media (min-width: 1440px) { + .started-header { + background-image: url(/assets/get_started_header_bg.svg); + height: 200px; + } } body { - --tw-bg-opacity: 1; - background-color: rgba(237, 225, 113, var(--tw-bg-opacity)); - --tw-border-opacity: 1; - border-color: rgba(217, 206, 95, var(--tw-border-opacity)); - border-style: solid; - font-family: Adelle, ui-serif; + --tw-bg-opacity: 1; + background-color: rgba(237, 225, 113, var(--tw-bg-opacity)); + --tw-border-opacity: 1; + border-color: rgba(217, 206, 95, var(--tw-border-opacity)); + border-style: solid; + font-family: Adelle, ui-serif; } .nav { - width: 100%; + width: 100%; } @media (min-width: 640px) { - .nav { - max-width: 640px; - } + .nav { + max-width: 640px; + } } @media (min-width: 768px) { - .nav { - max-width: 768px; - } + .nav { + max-width: 768px; + } } @media (min-width: 1024px) { - .nav { - max-width: 1024px; - } + .nav { + max-width: 1024px; + } } @media (min-width: 1280px) { - .nav { - max-width: 1280px; - } + .nav { + max-width: 1280px; + } } @media (min-width: 1536px) { - .nav { - max-width: 1536px; - } + .nav { + max-width: 1536px; + } } .nav { - display: flex; - justify-content: space-between; - margin-left: auto; - margin-right: auto; - padding-left: 1rem; - padding-right: 1rem; - padding-top: 2rem; - position: relative; - width: 100%; + display: flex; + justify-content: space-between; + margin-left: auto; + margin-right: auto; + padding-left: 1rem; + padding-right: 1rem; + padding-top: 2rem; + position: relative; + width: 100%; } .nav__logo { - width: 75%; + width: 75%; } @media (min-width: 768px) { - .nav__logo { - width: 70%; - } + .nav__logo { + width: 70%; + } } .nav__menu-icon { - display: block; - padding-right: 0.75rem; - width: 2.5rem; + display: block; + padding-right: 0.75rem; + width: 2.5rem; } @media (min-width: 768px) { - .nav__menu-icon { - display: none; - } + .nav__menu-icon { + display: none; + } } .nav__links { - display: none; - position: absolute; - right: 0; - text-transform: uppercase; - gap: 1.25rem; + display: none; + position: absolute; + right: 0; + text-transform: uppercase; + gap: 1.25rem; } @media (min-width: 768px) { - .nav__links { - display: flex; - } + .nav__links { + display: flex; + } } .nav__link { - display: flex; - flex-direction: column; - justify-content: center; - position: relative; - text-align: center; - width: 7rem; + display: flex; + flex-direction: column; + justify-content: center; + position: relative; + text-align: center; + width: 7rem; +} +.nav__link:hover { + color: #4c4c4c; + cursor: pointer; } .nav__link-underline { - position: absolute; - top: 1.25rem; + position: absolute; + top: 1.25rem; } .nav__link__underline-docs { - padding-top: 0.5rem; - position: absolute; - top: 1.25rem; + padding-top: 0.5rem; + position: absolute; + top: 1.25rem; } .nav-mobile { - padding-top: 3rem; + padding-top: 3rem; } -@media (min-width: 640px) { - .nav-mobile { - display: none; - } +@media (min-width: 768px) { + .nav-mobile { + display: none; + } } .nav-mobile__wrapper > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); + --tw-space-y-reverse: 0; + margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); } .nav-mobile__wrapper { - padding-top: 0.5rem; - padding-bottom: 1rem; + padding-top: 0.5rem; + padding-bottom: 1rem; } .nav-mobile__link { - border-color: transparent; - display: block; - font-size: 1.875rem; - line-height: 2.25rem; - padding-top: 0.5rem; - padding-bottom: 0.5rem; - padding-right: 1rem; - padding-left: 1rem; - --tw-text-opacity: 1; - color: rgba(18, 18, 18, var(--tw-text-opacity)); + border-color: transparent; + display: block; + font-size: 1.875rem; + line-height: 2.25rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-right: 1rem; + padding-left: 1rem; + --tw-text-opacity: 1; + color: rgba(18, 18, 18, var(--tw-text-opacity)); } .nav-mobile__links > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); + --tw-space-y-reverse: 0; + margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); } .nav-mobile__links { - padding-top: 0.5rem; - padding-bottom: 1rem; + padding-top: 0.5rem; + padding-bottom: 1rem; } .nav-mobile__heading { - --tw-bg-opacity: 1; - background-color: rgba(0, 0, 0, var(--tw-bg-opacity)); - display: flex; - flex-direction: column; - justify-content: flex-end; - height: 5rem; - font-size: 1.5rem; - line-height: 2rem; - padding-bottom: 0.75rem; - padding-left: 1rem; - --tw-text-opacity: 1; - color: rgba(255, 255, 255, var(--tw-text-opacity)); - width: 100%; + --tw-bg-opacity: 1; + background-color: rgba(0, 0, 0, var(--tw-bg-opacity)); + display: flex; + flex-direction: column; + justify-content: flex-end; + height: 5rem; + font-size: 1.5rem; + line-height: 2rem; + padding-bottom: 0.75rem; + padding-left: 1rem; + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); + width: 100%; } .nav-mobile__heading__title { - display: flex; - justify-content: space-between; - padding-right: 1rem; + display: flex; + justify-content: space-between; + padding-right: 1rem; } .nav-mobile__heading__close { - display: block; - padding-right: 0.75rem; + display: block; + padding-right: 0.75rem; } @media (min-width: 768px) { - .nav-mobile__heading__close { - display: none; - } + .nav-mobile__heading__close { + display: none; + } } .nav-mobile__links__level1 { - padding-left: 3rem; + padding-left: 3rem; } .nav-mobile__links__level2 { - padding-left: 1rem; + padding-left: 1rem; } .hero-bg { - display: none; - margin-top: -28rem; - width: 100%; - z-index: 50; - position: relative; + display: none; + margin-top: -28rem; + width: 100%; + z-index: 50; + position: relative; } @media (min-width: 768px) { - .hero-bg { - display: block; - } + .hero-bg { + display: block; + } } .hero-bg-mobile { - display: block; - margin-top: -16rem; - width: 100%; + display: block; + margin-top: -16rem; + width: 100%; } @media (min-width: 768px) { - .hero-bg-mobile { - display: none; - } + .hero-bg-mobile { + display: none; + } } .hero { - width: 100%; + width: 100%; } @media (min-width: 640px) { - .hero { - max-width: 640px; - } + .hero { + max-width: 640px; + } } @media (min-width: 768px) { - .hero { - max-width: 768px; - } + .hero { + max-width: 768px; + } } @media (min-width: 1024px) { - .hero { - max-width: 1024px; - } + .hero { + max-width: 1024px; + } } @media (min-width: 1280px) { - .hero { - max-width: 1280px; - } + .hero { + max-width: 1280px; + } } @media (min-width: 1536px) { - .hero { - max-width: 1536px; - } + .hero { + max-width: 1536px; + } } .hero { - position: relative; - z-index: 60; - margin-left: auto; - margin-right: auto; - margin-top: 3rem; - padding-left: 1rem; - padding-right: 1rem; - padding-bottom: 6rem; + position: relative; + z-index: 60; + margin-left: auto; + margin-right: auto; + margin-top: 3rem; + padding-left: 1rem; + padding-right: 1rem; + padding-bottom: 6rem; } @media (min-width: 768px) { - .hero { - margin-top: 6rem; - } + .hero { + margin-top: 6rem; + } } .hero-title-wrapper { - justify-content: space-between; - position: relative; + justify-content: space-between; + position: relative; } .hero-title { - font-size: 1.875rem; - line-height: 2.25rem; + font-size: 1.875rem; + line-height: 2.25rem; } @media (min-width: 768px) { - .hero-title { - font-size: 2.25rem; - line-height: 2.5rem; - line-height: 1.625; - } + .hero-title { + font-size: 2.25rem; + line-height: 2.5rem; + line-height: 1.625; + } } @media (min-width: 1024px) { - .hero-title { - font-size: 3rem; - line-height: 1; - } + .hero-title { + font-size: 3rem; + line-height: 1; + } } @media (min-width: 1440px) { - .hero-title { - font-size: 5rem; - } + .hero-title { + font-size: 5rem; + } } @media (min-width: 1800px) { - .hero-title { - font-size: 6rem; - } + .hero-title { + font-size: 6rem; + } } .hero-button { - display: flex; - margin-bottom: 3rem; - padding-top: 1.25rem; + display: flex; + margin-bottom: 3rem; + padding-top: 1.25rem; } @media (min-width: 768px) { - .hero-button { - padding-top: 2.5rem; - } + .hero-button { + padding-top: 2.5rem; + } } .hero-button__content { - --tw-border-opacity: 1; - border-color: rgba(210, 199, 91, var(--tw-border-opacity)); - width: 13rem; + --tw-border-opacity: 1; + border-color: rgba(210, 199, 91, var(--tw-border-opacity)); + width: 13rem; } .hero-button__split { - --tw-bg-opacity: 1; - background-color: rgba(18, 18, 18, var(--tw-bg-opacity)); - --tw-text-opacity: 1; - color: rgba(255, 255, 255, var(--tw-text-opacity)); + --tw-bg-opacity: 1; + background-color: rgba(18, 18, 18, var(--tw-bg-opacity)); + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); } .feature-button { - display: none; - justify-content: center; - margin-bottom: 3rem; + display: none; + justify-content: center; + margin-bottom: 3rem; } @media (min-width: 768px) { - .feature-button { - display: flex; - padding-top: 1.5rem; - } + .feature-button { + display: flex; + } } .feature-button__content { - --tw-border-opacity: 1; - border-color: rgba(196, 91, 40, var(--tw-border-opacity)); - width: 24rem; + --tw-border-opacity: 1; + border-color: rgba(196, 91, 40, var(--tw-border-opacity)); + width: 24rem; } .feature-button__split { - --tw-bg-opacity: 1; - background-color: rgba(241, 226, 85, var(--tw-bg-opacity)); - --tw-text-opacity: 1; - color: rgba(0, 0, 0, var(--tw-text-opacity)); + margin-top: 10px; + --tw-bg-opacity: 1; + background-color: rgba(241, 226, 85, var(--tw-bg-opacity)); + --tw-text-opacity: 1; + color: rgba(0, 0, 0, var(--tw-text-opacity)); } .split-button { - background-color: transparent; - border-radius: 0.75rem; - border-style: solid; - border-width: 2px; - display: flex; - height: 3rem; - position: relative; + background-color: transparent; + border-radius: 0.75rem; + border-style: solid; + border-width: 2px; + display: flex; + height: 3rem; + position: relative; } .split-button__button { - display: flex; - position: absolute; - left: -0.5rem; - top: -0.75rem; + display: flex; + position: absolute; + left: -0.5rem; + top: -0.75rem; } .split-button__split1 { - border-radius: 0.75rem; - display: inline-flex; - align-items: center; - font-weight: 500; - height: 3rem; - margin-right: 0.5rem; - padding-top: 0.75rem; - padding-bottom: 0.75rem; - padding-left: 1.5rem; - padding-right: 1.5rem; + border-radius: 0.75rem; + display: inline-flex; + align-items: center; + font-weight: 500; + height: 3rem; + margin-right: 0.5rem; + padding-top: 0.75rem; + padding-bottom: 0.75rem; + padding-left: 1.5rem; + padding-right: 1.5rem; } .split-button__split2 { - border-radius: 0.75rem; - display: inline-flex; - align-items: center; - font-weight: 500; - height: 3rem; - font-size: 1.125rem; - line-height: 1.75rem; - padding-top: 0.75rem; - padding-bottom: 0.75rem; - padding-left: 0.75rem; - padding-right: 0.75rem; - --tw-text-opacity: 1; - color: rgba(0, 0, 0, var(--tw-text-opacity)); + border-radius: 0.75rem; + display: inline-flex; + align-items: center; + font-weight: 500; + height: 3rem; + font-size: 1.125rem; + line-height: 1.75rem; + padding-top: 0.75rem; + padding-bottom: 0.75rem; + padding-left: 0.75rem; + padding-right: 0.75rem; + --tw-text-opacity: 1; + color: rgba(0, 0, 0, var(--tw-text-opacity)); } .transition { - width: 100%; + width: 100%; } .feature__section { - width: full; - margin-top: -0.5rem; - background-color: #e37743; + width: full; + margin-top: -0.5rem; + background-color: #e37743; } .feature__wrapper { - width: 100%; + width: 100%; } @media (min-width: 640px) { - .feature__wrapper { - max-width: 640px; - } + .feature__wrapper { + max-width: 640px; + } } @media (min-width: 768px) { - .feature__wrapper { - max-width: 768px; - } + .feature__wrapper { + max-width: 768px; + } } @media (min-width: 1024px) { - .feature__wrapper { - max-width: 1024px; - } + .feature__wrapper { + max-width: 1024px; + } } @media (min-width: 1280px) { - .feature__wrapper { - max-width: 1280px; - } + .feature__wrapper { + max-width: 1280px; + } } @media (min-width: 1536px) { - .feature__wrapper { - max-width: 1536px; - } + .feature__wrapper { + max-width: 1536px; + } } .feature__wrapper { - margin-left: auto; - margin-right: auto; - padding-left: 0; - padding-right: 0; - padding-top: 3rem; + margin-left: auto; + margin-right: auto; + padding-left: 0; + padding-right: 0; + padding-top: 3rem; } @media (min-width: 768px) { - .feature__wrapper { - padding-left: 1rem; - padding-right: 1rem; - } -} -.feature__title { - font-size: 1.875rem; - line-height: 2.25rem; - margin-left: auto; - margin-right: auto; + .feature__wrapper { padding-left: 1rem; padding-right: 1rem; - width: 100%; + } +} +.feature__title { + font-size: 1.875rem; + line-height: 2.25rem; + margin-left: auto; + margin-right: auto; + padding-left: 1rem; + padding-right: 1rem; + width: 100%; } @media (min-width: 768px) { - .feature__title { - font-size: 2.25rem; - line-height: 2.5rem; - padding-left: 0; - padding-right: 0; - text-align: center; - width: 60%; - } + .feature__title { + font-size: 2.25rem; + line-height: 2.5rem; + padding-left: 0; + padding-right: 0; + text-align: center; + width: 60%; + } } .feature__cards { - display: flex; - flex-wrap: wrap; - justify-content: center; - padding-top: 3rem; + display: flex; + flex-wrap: wrap; + justify-content: center; + padding-top: 3.5rem; } .feature__card { - display: flex; - justify-content: center; - padding-bottom: 1.5rem; - width: 100%; + display: flex; + justify-content: center; + width: 100%; + margin-bottom: 3.5rem; } @media (min-width: 768px) { - .feature__card { - margin-bottom: 3.5rem; - width: 50%; - } + .feature__card { + margin-bottom: 3.5rem; + width: 50%; + } } @media (min-width: 1024px) { - .feature__card { - width: 33.333333%; - } + .feature__card { + width: 33.333333%; + } } .feature__card__content { - --tw-bg-opacity: 1; - background-color: rgba(208, 79, 34, var(--tw-bg-opacity)); - border-radius: 0; - padding-top: 1rem; - padding-bottom: 1rem; - padding-left: 2rem; - padding-right: 2rem; - width: 100%; + --tw-bg-opacity: 1; + background-color: rgba(208, 79, 34, var(--tw-bg-opacity)); + border-radius: 0; + padding-top: 1rem; + padding-bottom: 1rem; + padding-left: 2rem; + padding-right: 2rem; + width: 100%; } @media (min-width: 768px) { - .feature__card__content { - border-radius: 1.5rem; - height: 20rem; - width: 20rem; - } + .feature__card__content { + border-radius: 1.5rem; + height: 20rem; + width: 20rem; + } } .feature__card__heading { - display: inline-flex; - align-items: center; - padding-bottom: 1.25rem; + display: inline-flex; + align-items: center; + padding-bottom: 1.25rem; } .feature__card__title { - font-size: 1.875rem; - line-height: 2.25rem; - margin-left: 1rem; - margin-bottom: -0.5rem; + font-size: 1.875rem; + line-height: 2.25rem; + margin-left: 1rem; + margin-bottom: -0.5rem; } .feature__card__description { - font-size: 1.125rem; - line-height: 1.75rem; + font-size: 1.125rem; + line-height: 1.75rem; } .components__section { - --tw-bg-opacity: 1; - background-color: rgba(58, 52, 83, var(--tw-bg-opacity)); - flex-direction: column; - align-items: center; - margin-top: -0.5rem; - padding-top: 3rem; - --tw-text-opacity: 1; - color: rgba(255, 255, 255, var(--tw-text-opacity)); - width: 100%; + --tw-bg-opacity: 1; + background-color: rgba(58, 52, 83, var(--tw-bg-opacity)); + flex-direction: column; + align-items: center; + margin-top: -0.5rem; + padding-top: 3rem; + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); + width: 100%; } .component { - width: 100%; + width: 100%; } @media (min-width: 640px) { - .component { - max-width: 640px; - } + .component { + max-width: 640px; + } } @media (min-width: 768px) { - .component { - max-width: 768px; - } + .component { + max-width: 768px; + } } @media (min-width: 1024px) { - .component { - max-width: 1024px; - } + .component { + max-width: 1024px; + } } @media (min-width: 1280px) { - .component { - max-width: 1280px; - } + .component { + max-width: 1280px; + } } @media (min-width: 1536px) { - .component { - max-width: 1536px; - } + .component { + max-width: 1536px; + } } .component { - display: flex; - flex-wrap: wrap; - align-items: center; - margin-left: auto; - margin-right: auto; - margin-bottom: 3rem; - padding-left: 1rem; - padding-right: 1rem; + display: flex; + flex-wrap: wrap; + align-items: center; + margin-left: auto; + margin-right: auto; + margin-bottom: 3rem; + padding-left: 1rem; + padding-right: 1rem; } .component__info { - padding-right: 0; - padding-bottom: 1rem; - width: 100%; + padding-right: 0; + padding-bottom: 1rem; + width: 100%; } @media (min-width: 1024px) { - .component__info { - padding-bottom: 0; - padding-right: 2rem; - width: 33.333333%; - } + .component__info { + padding-bottom: 0; + padding-right: 2rem; + width: 33.333333%; + } } .component__info__title { - padding-bottom: 0.75rem; + padding-bottom: 0.75rem; } .component__example { - width: 100%; + width: 100%; } @media (min-width: 1024px) { - .component__example { - width: 66.666667%; - } + .component__example { + width: 66.666667%; + } } .component__example__code { - --tw-bg-opacity: 1; - background-color: rgba(255, 255, 255, var(--tw-bg-opacity)); - border-radius: 0.75rem; - height: 12rem; + --tw-bg-opacity: 1; + background-color: rgba(255, 255, 255, var(--tw-bg-opacity)); + border-radius: 0.75rem; + height: 12rem; } footer { - --tw-bg-opacity: 1; - background-color: rgba(0, 0, 0, var(--tw-bg-opacity)); - margin-top: -0.5rem; + position: relative; + --tw-bg-opacity: 1; + background-color: rgba(0, 0, 0, var(--tw-bg-opacity)); + margin-top: -0.5rem; } .footer__wrapper { - width: 100%; + padding-top: 80px; + width: 100%; } @media (min-width: 640px) { - .footer__wrapper { - max-width: 640px; - } + .footer__wrapper { + max-width: 640px; + } } @media (min-width: 768px) { - .footer__wrapper { - max-width: 768px; - } + .footer__wrapper { + max-width: 768px; + } } @media (min-width: 1024px) { - .footer__wrapper { - max-width: 1024px; - } + .footer__wrapper { + max-width: 1024px; + } } @media (min-width: 1280px) { - .footer__wrapper { - max-width: 1280px; - } + .footer__wrapper { + max-width: 1280px; + } } @media (min-width: 1536px) { - .footer__wrapper { - max-width: 1536px; - } + .footer__wrapper { + max-width: 1536px; + } } .footer__wrapper { - margin-left: auto; - margin-right: auto; - padding-left: 1rem; - padding-right: 1rem; + margin-left: auto; + margin-right: auto; + padding-left: 1rem; + padding-right: 1rem; } .footer__nav { - display: flex; - flex-direction: column; - justify-content: space-between; - line-height: 2rem; - padding-top: 3rem; - width: 100%; - gap: 2rem; + display: flex; + flex-direction: column; + justify-content: space-between; + line-height: 2rem; + padding-top: 3rem; + width: 100%; + gap: 2rem; } @media (min-width: 768px) { - .footer__nav { - flex-direction: row; - width: 40%; - gap: 0; - } + .footer__nav { + flex-direction: row; + width: 40%; + gap: 0; + } +} +nav ul li:hover { + cursor: pointer; } .footer__nav__category { - --tw-text-opacity: 1; - color: rgba(91, 56, 233, var(--tw-text-opacity)); + --tw-text-opacity: 1; + color: rgba(91, 56, 233, var(--tw-text-opacity)); } .footer__nav__links { - --tw-text-opacity: 1; - color: rgba(255, 255, 255, var(--tw-text-opacity)); - text-transform: uppercase; + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); + text-transform: uppercase; } .footer_copyright { - font-size: 0.875rem; - line-height: 1.25rem; - padding-top: 3rem; - padding-bottom: 3rem; - --tw-text-opacity: 1; - color: rgba(76, 76, 76, var(--tw-text-opacity)); + font-size: 0.875rem; + line-height: 1.25rem; + padding-top: 3rem; + padding-bottom: 3rem; + --tw-text-opacity: 1; + color: rgba(76, 76, 76, var(--tw-text-opacity)); } .documentation { - --tw-bg-opacity: 1; - background-color: rgba(250, 247, 230, var(--tw-bg-opacity)); - --tw-border-opacity: 1; - border-color: rgba(217, 206, 95, var(--tw-border-opacity)); - border-style: solid; - line-height: 2.25rem; + --tw-bg-opacity: 1; + background-color: rgba(250, 247, 230, var(--tw-bg-opacity)); + --tw-border-opacity: 1; + border-color: rgba(217, 206, 95, var(--tw-border-opacity)); + border-style: solid; + line-height: 2.25rem; } .mobile-mode { - background-color: #ede171 !important; + background-color: #ede171 !important; } .top-border { - border-top-width: 4px; + border-top-width: 4px; } .sidebar { - display: flex; + display: flex; } .side-nav > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1rem * var(--tw-space-y-reverse)); + --tw-space-y-reverse: 0; + margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1rem * var(--tw-space-y-reverse)); } .side-nav { - --tw-bg-opacity: 1; - background-color: rgba(242, 239, 217, var(--tw-bg-opacity)); - display: none; - flex-direction: column; - padding-left: 3.5rem; - padding-top: 5rem; - position: relative; - width: 21rem; - z-index: 0; + --tw-bg-opacity: 1; + background-color: rgba(242, 239, 217, var(--tw-bg-opacity)); + display: none; + flex-direction: column; + padding-left: 3.5rem; + padding-top: 5rem; + position: relative; + width: 21rem; + z-index: 0; } .side-nav__bold { font-weight: bold; } @media (min-width: 768px) { - .side-nav { - display: flex; - } + .side-nav { + display: flex; + } } .side-nav__expand > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.25rem * var(--tw-space-y-reverse)); + --tw-space-y-reverse: 0; + margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.25rem * var(--tw-space-y-reverse)); } .side-nav__expand__button { - border-radius: 0.75rem; - cursor: pointer; - display: flex; - align-items: center; - height: 3.5rem; - font-size: 1.125rem; - line-height: 1.75rem; - padding-left: 1rem; - padding-right: 1rem; + border-radius: 0.75rem; + cursor: pointer; + display: flex; + align-items: center; + height: 3.5rem; + font-size: 1.125rem; + line-height: 1.75rem; + padding-left: 1rem; + padding-right: 1rem; } .side-nav__expand__button:hover { - --tw-text-opacity: 1; - color: rgba(76, 76, 76, var(--tw-text-opacity)); + --tw-text-opacity: 1; + color: rgba(76, 76, 76, var(--tw-text-opacity)); } .side-nav__expand__button { - width: 100%; + width: 100%; } .side-nav__link:hover { - --tw-text-opacity: 1; - color: rgba(76, 76, 76, var(--tw-text-opacity)); + --tw-text-opacity: 1; + color: rgba(76, 76, 76, var(--tw-text-opacity)); } .side-nav__expand__icon { - margin-left: auto; - transform: rotate(90deg); - transition: all ease-in-out 0.2s; - transition-property: color, transform; + margin-left: auto; + transform: rotate(90deg); + transition: all ease-in-out 0.2s; + transition-property: color, transform; } -.expanded{ - transform: rotate(0) !important; +.expanded { + transform: rotate(0) !important; } .side-nav__expand__list { - padding-left: 2rem; + padding-left: 2rem; } .side-nav__expand__list2 { - padding-left: 1.5rem; + padding-left: 1.5rem; } .docs { - width: 100%; + width: 100%; } @media (min-width: 640px) { - .docs { - max-width: 640px; - } + .docs { + max-width: 640px; + } } @media (min-width: 768px) { - .docs { - max-width: 768px; - } + .docs { + max-width: 768px; + } } @media (min-width: 1024px) { - .docs { - max-width: 1024px; - } + .docs { + max-width: 1024px; + } } @media (min-width: 1280px) { - .docs { - max-width: 1280px; - } + .docs { + max-width: 1280px; + } } @media (min-width: 1536px) { - .docs { - max-width: 1536px; - } + .docs { + max-width: 1536px; + } } .docs { - margin-left: auto; - margin-right: auto; - margin-left: 0; - margin-bottom: 6rem; - padding-left: 1rem; - padding-right: 1rem; - padding-top: 0; + margin-left: auto; + margin-right: auto; + margin-left: 0; + margin-bottom: 6rem; + padding-left: 1rem; + padding-right: 1rem; + padding-top: 0; } @media (min-width: 768px) { - .docs { - margin-left: 2rem; - padding-top: 8rem; - } + .docs { + margin-left: 2rem; + padding-top: 8rem; + } } .separator { - --tw-border-opacity: 1; - border-color: rgba(226, 224, 213, var(--tw-border-opacity)); - border-style: solid; - border-top-width: 2px; - margin-top: 2.5rem; - margin-bottom: 2.5rem; + --tw-border-opacity: 1; + border-color: rgba(226, 224, 213, var(--tw-border-opacity)); + border-style: solid; + border-top-width: 2px; + margin-top: 2.5rem; + margin-bottom: 2.5rem; } .code-block { - --tw-border-opacity: 1; - border-color: rgba(226, 224, 213, var(--tw-border-opacity)); - border-style: solid; - border-width: 2px; - margin-top: 2.5rem; - margin-bottom: 2.5rem; - width: 100%; + --tw-border-opacity: 1; + border-color: rgba(226, 224, 213, var(--tw-border-opacity)); + border-style: solid; + border-width: 2px; + margin-top: 2.5rem; + margin-bottom: 2.5rem; + width: 100%; } .code-block__content { - margin: 1.5rem; + margin: 1.5rem; } .bullet-list { - margin-top: 0.5rem; - margin-bottom: 0.5rem; - margin-left: 1rem; + margin-top: 0.5rem; + margin-bottom: 0.5rem; + margin-left: 1rem; } .bullet-list__callout > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(0.5rem * var(--tw-space-x-reverse)); - margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); + --tw-space-x-reverse: 0; + margin-right: calc(0.5rem * var(--tw-space-x-reverse)); + margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); } .bullet-list__callout { - display: flex; + display: flex; } code.doc { - overflow-x: auto; - background-color: transparent; - --tw-border-opacity: 1; - border-color: rgba(226, 224, 213, var(--tw-border-opacity)); - border-width: 2px; - display: block; - margin-top: 1rem; - margin-bottom: 1rem; - padding: 1rem; + overflow-x: auto; + background-color: transparent; + --tw-border-opacity: 1; + border-color: rgba(226, 224, 213, var(--tw-border-opacity)); + border-width: 2px; + display: block; + margin-top: 1rem; + margin-bottom: 1rem; + padding: 1rem; } .admon { - border-style: dashed; - border-width: 2px; - margin-top: 2.5rem; - margin-bottom: 2.5rem; - width: 100%; + border-style: dashed; + border-width: 2px; + margin-top: 2.5rem; + margin-bottom: 2.5rem; + width: 100%; } .admon-warning { - --tw-border-opacity: 1; - border-color: rgba(220, 38, 38, var(--tw-border-opacity)); + --tw-border-opacity: 1; + border-color: rgba(220, 38, 38, var(--tw-border-opacity)); } .admon__button { - border-radius: 9999px; - display: flex; - align-items: center; - justify-content: center; - height: 2rem; - margin-left: 1rem; - margin-top: -1rem; - --tw-text-opacity: 1; - color: rgba(255, 255, 255, var(--tw-text-opacity)); - width: 8rem; - gap: 0.5rem; + border-radius: 9999px; + display: flex; + align-items: center; + justify-content: center; + height: 2rem; + margin-left: 1rem; + margin-top: -1rem; + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); + width: 8rem; + gap: 0.5rem; } .admon__button-warning { - --tw-bg-opacity: 1; - background-color: rgba(220, 38, 38, var(--tw-bg-opacity)); + --tw-bg-opacity: 1; + background-color: rgba(220, 38, 38, var(--tw-bg-opacity)); } .admon__button-note { - --tw-bg-opacity: 1; - background-color: rgba(30, 58, 138, var(--tw-bg-opacity)); + --tw-bg-opacity: 1; + background-color: rgba(30, 58, 138, var(--tw-bg-opacity)); } .admon-note { - --tw-border-opacity: 1; - border-color: rgba(30, 58, 138, var(--tw-border-opacity)); + --tw-border-opacity: 1; + border-color: rgba(30, 58, 138, var(--tw-border-opacity)); } .secondary-nav { - margin-bottom: 1.5rem; + margin-bottom: 1.5rem; } @media (min-width: 768px) { - .secondary-nav { - display: none; - } + .secondary-nav { + display: none; + } } .secondary-nav__content { - --tw-border-opacity: 1; - border-color: rgba(210, 199, 91, var(--tw-border-opacity)); - width: 10rem; + --tw-border-opacity: 1; + border-color: rgba(210, 199, 91, var(--tw-border-opacity)); + width: 10rem; } .secondary-nav__split { - --tw-bg-opacity: 1; - background-color: rgba(18, 18, 18, var(--tw-bg-opacity)); - border-radius: 0.75rem; - display: inline-flex; - align-items: center; - justify-content: center; - font-weight: 500; - height: 3rem; - padding-left: 0.5rem; - padding-right: 0.5rem; - --tw-text-opacity: 1; - color: rgba(255, 255, 255, var(--tw-text-opacity)); - width: 9rem; + --tw-bg-opacity: 1; + background-color: rgba(18, 18, 18, var(--tw-bg-opacity)); + border-radius: 0.75rem; + display: inline-flex; + align-items: center; + justify-content: center; + font-weight: 500; + height: 3rem; + padding-left: 0.5rem; + padding-right: 0.5rem; + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); + width: 9rem; } .secondary-nav__split > p { - margin-right: 6px; + margin-right: 6px; } @media (min-width: 768px) { - .md\:flex-row { - flex-direction: row; - } - .md\:ml-8 { - margin-left: 2rem; - } - .md\:pt-32 { - padding-top: 8rem; - } - .md\:w-2\/5 { - width: 40%; - } - .md\:gap-0 { - gap: 0; - } -} - + .md\:flex-row { + flex-direction: row; + } + .md\:ml-8 { + margin-left: 2rem; + } + .md\:pt-32 { + padding-top: 8rem; + } + .md\:w-2\/5 { + width: 40%; + } + .md\:gap-0 { + gap: 0; + } +} .nav-mobile { - display: block !important; + display: block !important; } - .CodeMirror { - height: auto; + height: auto; } - .component__example__code { - overflow: hidden; + overflow: hidden; } - -- GitLab From bc1de87bb1df3517abd60dd6759a4e78e7858541 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 26 Apr 2021 18:07:12 -0600 Subject: [PATCH 089/116] many asciidoc fixes --- .../snabbdom/Shpadoinkle/Backend/Snabbdom.hs | 13 ++- template/Shpadoinkle/Template/TH.hs | 33 +++++- template/test.html | 8 +- website/Shpadoinkle/Website/Partials/Nav.hs | 5 +- .../Shpadoinkle/Website/Partials/Template.hs | 2 +- website/assets/4.svg | 14 +++ website/assets/5.svg | 14 +++ website/assets/6.svg | 14 +++ website/assets/7.svg | 14 +++ website/assets/8.svg | 14 +++ website/assets/9.svg | 14 +++ website/assets/style.css | 105 ++++++++++++++---- website/docs/tutorial/calculator.adoc | 1 + 13 files changed, 217 insertions(+), 34 deletions(-) create mode 100644 website/assets/4.svg create mode 100644 website/assets/5.svg create mode 100644 website/assets/6.svg create mode 100644 website/assets/7.svg create mode 100644 website/assets/8.svg create mode 100644 website/assets/9.svg diff --git a/backends/snabbdom/Shpadoinkle/Backend/Snabbdom.hs b/backends/snabbdom/Shpadoinkle/Backend/Snabbdom.hs index 011e6288..e82e2745 100644 --- a/backends/snabbdom/Shpadoinkle/Backend/Snabbdom.hs +++ b/backends/snabbdom/Shpadoinkle/Backend/Snabbdom.hs @@ -47,7 +47,7 @@ import Control.Monad.Trans.Control (ComposeSt, MonadBaseControl (..), import Control.Monad.Writer (MonadWriter) import Data.FileEmbed (embedStringFile) import Data.Map.Internal (Map (Bin, Tip)) -import Data.Text (Text, words) +import Data.Text (Text, isPrefixOf, words) import GHCJS.DOM (currentDocumentUnchecked) import GHCJS.DOM.Document (createElement, getBodyUnsafe) import GHCJS.DOM.Element (setAttribute, setInnerHTML) @@ -172,11 +172,12 @@ props toJSM i (Props xs) = do | t /= "" -> do t' <- valMakeText t unsafeSetProp k' t' $ case k of - "style" -> attrsObj - "type" -> attrsObj - "autofocus" -> attrsObj - "checked" -> attrsObj - _ -> propsObj + "style" -> attrsObj + "type" -> attrsObj + "autofocus" -> attrsObj + "checked" -> attrsObj + d | "data-" `isPrefixOf` d -> attrsObj + _ -> propsObj | otherwise -> do t' <- valMakeText t unsafeSetProp k' t' propsObj diff --git a/template/Shpadoinkle/Template/TH.hs b/template/Shpadoinkle/Template/TH.hs index 6095efb1..ad74b9f7 100644 --- a/template/Shpadoinkle/Template/TH.hs +++ b/template/Shpadoinkle/Template/TH.hs @@ -20,6 +20,7 @@ import System.Process (proc, import Text.HTML.Parser (Attr (..), Token (..), parseTokens) + data CleanUp = CleanUp | LeaveFile deriving Eq @@ -52,15 +53,43 @@ embedHtml' clean htmlPath = do pure . ListE $ tokenToExp ts +breakClosing :: Text -> [Token] -> ([Token],[Token]) +breakClosing tn = go (0 :: Int) + where + sameTag = \case + TagOpen tn' _ | tn' == tn -> True + TagClose tn' | tn' == tn -> True + _ -> False + go depth ts = case break sameTag ts of + + -- closing tag at the top level, we are done + (before, t@(TagClose tn':_)) + | tn' == tn && depth == 0 -> (before, t) + + -- closing tag found at a deeper level, collect and decrement + (before, t@(TagClose tn'):more) + | tn' == tn -> let (before', rest') = go (depth - 1) more + in (before <> [t] <> before', rest') + + -- sibling opening tag found, decend + (before, t@(TagOpen tn' _):children) + | tn == tn' -> let (before', rest') = go (depth + 1) children + in (before <> [t] <> before', rest') + + x -> x + + + tokenToExp :: [Token] -> [Exp] tokenToExp = let h = UnboundVarE $ mkName "h" text = UnboundVarE $ mkName "text" in \case + TagOpen "hr" attrs:ts -> tokenToExp $ TagSelfClose "hr" attrs:ts TagOpen tn attrs:ts -> let attrs' = ListE $ attrToExp <$> attrs name = asText tn - (children, siblings) = break (\case TagClose tn' | tn' == tn -> True; _ -> False) ts - in AppE (AppE (AppE h name) attrs') (ListE $ tokenToExp children) : tokenToExp (drop 1 siblings) + (children, siblings) = breakClosing tn ts + in AppE (AppE (AppE h name) attrs') (ListE $ tokenToExp children) : tokenToExp siblings TagSelfClose tn attrs:ts -> let attrs' = ListE $ attrToExp <$> attrs name = asText tn diff --git a/template/test.html b/template/test.html index 67aacf9f..c7fd7a92 100644 --- a/template/test.html +++ b/template/test.html @@ -1,5 +1,7 @@
- - Shockazooloo🤠 +
+ + Shockazooloo +
+

Wak!

-

Wak!

diff --git a/website/Shpadoinkle/Website/Partials/Nav.hs b/website/Shpadoinkle/Website/Partials/Nav.hs index af5af2da..0f8842d2 100644 --- a/website/Shpadoinkle/Website/Partials/Nav.hs +++ b/website/Shpadoinkle/Website/Partials/Nav.hs @@ -28,9 +28,10 @@ toActive = \case navLinkDesktop :: MonadJSM m => Route -> Nav -> Html m a -navLinkDesktop fe n = li [ class' nav__link ] $ +navLinkDesktop r n = li [ class' nav__link ] $ a [ goTo $ toRoute n ] [ text $ humanize n ] - : [ img' [ class' nav__link_underline, src $(assetLink "/assets/nav_line.svg") ] | toActive fe == Just n ] + : [ img' [ class' $ if r == RHome then nav__link_underline else nav__link__underline_docs + , src $(assetLink "/assets/nav_line.svg") ] | toActive r == Just n ] navLinkMobile :: MonadJSM m => Nav -> Html m a diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index 14c05679..180ec568 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -59,7 +59,7 @@ headView ev vm = head_ $ , meta' [ content "width=device-width, initial-scale=1.0", name' "viewport" ] , stylesheet $ codemirrorCDN "codemirror.min.css" , stylesheet $ codemirrorCDN "theme/darcula.min.css" - , stylesheet $ highlightCDN "styles/a11y-dark.min.css" + , stylesheet $ highlightCDN "styles/atom-one-light.min.css" , stylesheet $(assetLink "/assets/style.css") , stylesheet "https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" , javascript $ entrypoint ev diff --git a/website/assets/4.svg b/website/assets/4.svg new file mode 100644 index 00000000..c82b58c0 --- /dev/null +++ b/website/assets/4.svg @@ -0,0 +1,14 @@ + + + Group 17 + + + + + + 4 + + + + + diff --git a/website/assets/5.svg b/website/assets/5.svg new file mode 100644 index 00000000..7cfc8d91 --- /dev/null +++ b/website/assets/5.svg @@ -0,0 +1,14 @@ + + + Group 17 + + + + + + 5 + + + + + diff --git a/website/assets/6.svg b/website/assets/6.svg new file mode 100644 index 00000000..d53f5185 --- /dev/null +++ b/website/assets/6.svg @@ -0,0 +1,14 @@ + + + Group 17 + + + + + + 6 + + + + + diff --git a/website/assets/7.svg b/website/assets/7.svg new file mode 100644 index 00000000..716e400d --- /dev/null +++ b/website/assets/7.svg @@ -0,0 +1,14 @@ + + + Group 17 + + + + + + 7 + + + + + diff --git a/website/assets/8.svg b/website/assets/8.svg new file mode 100644 index 00000000..f0890fc4 --- /dev/null +++ b/website/assets/8.svg @@ -0,0 +1,14 @@ + + + Group 17 + + + + + + 8 + + + + + diff --git a/website/assets/9.svg b/website/assets/9.svg new file mode 100644 index 00000000..d5a6d644 --- /dev/null +++ b/website/assets/9.svg @@ -0,0 +1,14 @@ + + + Group 17 + + + + + + 9 + + + + + diff --git a/website/assets/style.css b/website/assets/style.css index c0f841ac..f86335d2 100644 --- a/website/assets/style.css +++ b/website/assets/style.css @@ -509,12 +509,78 @@ video { padding: 1rem 0; } .listingblock { - margin: 2rem 0; - overflow-x: scroll; + margin: 1rem 0; + border-radius: 0.75rem; + overflow-x: auto; } .listingblock > .content > .highlight > code { - border: 2px solid #e2e0d5; - padding: 15px 10px; + padding: 1rem 1.5rem; +} +p code, table code{ + background: #ede17187; + border-radius: 4px; + padding: 5px 3px; + margin: 0 2px; +} +p a{ + color: #0d6661; + text-decoration: underline; +} +p a:hover{ + color: #178781; +} +.conum[data-value]+b{ + display: none; +} +.conum[data-value]{ + border-radius: 100%; + display: inline-block; + height: 20px; + width: 20px; +} +.conum[data-value="1"]{ + background: url(/assets/1.svg); +} +.conum[data-value="2"]{ + background: url(/assets/2.svg); +} +.conum[data-value="3"]{ + background: url(/assets/3.svg); +} +.conum[data-value="4"]{ + background: url(/assets/4.svg); +} +.conum[data-value="5"]{ + background: url(/assets/5.svg); +} +.conum[data-value="6"]{ + background: url(/assets/6.svg); +} +.conum[data-value="7"]{ + background: url(/assets/7.svg); +} +.conum[data-value="8"]{ + background: url(/assets/8.svg); +} +.conum[data-value="9"]{ + background: url(/assets/8.svg); +} +.colist{ + margin: 2rem 0; +} +.colist td{ + vertical-align: top; + padding-bottom: 1rem; + line-height: 1.5rem; +} +.colist .conum{ + margin-top: 3px; +} +.colist td:first-child{ + padding: 0 1rem; +} +code .conum[data-value]{ + transform: translateY(3px) } .hljs-comment { display: none; @@ -705,7 +771,7 @@ body { .nav__link { display: flex; flex-direction: column; - justify-content: center; + justify-content: flex-start; position: relative; text-align: center; width: 7rem; @@ -1282,7 +1348,7 @@ nav ul li:hover { background-color: rgba(242, 239, 217, var(--tw-bg-opacity)); display: none; flex-direction: column; - padding-left: 3.5rem; + padding-left: 1.75rem; padding-top: 5rem; position: relative; width: 21rem; @@ -1340,6 +1406,7 @@ nav ul li:hover { } .docs { width: 100%; + min-width: 10px; } @media (min-width: 640px) { .docs { @@ -1356,16 +1423,6 @@ nav ul li:hover { max-width: 1024px; } } -@media (min-width: 1280px) { - .docs { - max-width: 1280px; - } -} -@media (min-width: 1536px) { - .docs { - max-width: 1536px; - } -} .docs { margin-left: auto; margin-right: auto; @@ -1378,9 +1435,15 @@ nav ul li:hover { @media (min-width: 768px) { .docs { margin-left: 2rem; - padding-top: 8rem; + padding-top: 5rem; } } +.docs h1{ + margin-bottom: 2rem; +} +.docs h2{ + margin: 2rem 0 0.5rem 0; +} .separator { --tw-border-opacity: 1; border-color: rgba(226, 224, 213, var(--tw-border-opacity)); @@ -1389,13 +1452,15 @@ nav ul li:hover { margin-top: 2.5rem; margin-bottom: 2.5rem; } -.code-block { +.hljs{ + background: none !important; +} +.highlight{ + border-radius: 0.75rem; --tw-border-opacity: 1; border-color: rgba(226, 224, 213, var(--tw-border-opacity)); border-style: solid; border-width: 2px; - margin-top: 2.5rem; - margin-bottom: 2.5rem; width: 100%; } .code-block__content { diff --git a/website/docs/tutorial/calculator.adoc b/website/docs/tutorial/calculator.adoc index b6bd6466..727904b7 100644 --- a/website/docs/tutorial/calculator.adoc +++ b/website/docs/tutorial/calculator.adoc @@ -1,5 +1,6 @@ :relfilesuffix: / :relfileprefix: / +:icons: font Here we will build a simple calculator, following pioneers of the space -- GitLab From 8383867cce0e2854845e420ef5b19305a0759685 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 26 Apr 2021 19:11:13 -0600 Subject: [PATCH 090/116] fix admons --- template/Shpadoinkle/Template/TH.hs | 2 + website/assets/style.css | 66 ++++++++++++++++++++--------- 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/template/Shpadoinkle/Template/TH.hs b/template/Shpadoinkle/Template/TH.hs index ad74b9f7..11acab00 100644 --- a/template/Shpadoinkle/Template/TH.hs +++ b/template/Shpadoinkle/Template/TH.hs @@ -56,10 +56,12 @@ embedHtml' clean htmlPath = do breakClosing :: Text -> [Token] -> ([Token],[Token]) breakClosing tn = go (0 :: Int) where + sameTag = \case TagOpen tn' _ | tn' == tn -> True TagClose tn' | tn' == tn -> True _ -> False + go depth ts = case break sameTag ts of -- closing tag at the top level, we are done diff --git a/website/assets/style.css b/website/assets/style.css index f86335d2..b6e0f4da 100644 --- a/website/assets/style.css +++ b/website/assets/style.css @@ -1,4 +1,4 @@ - { + @font-face{ font-family: Adelle; src: url(/assets/Adelle-Regular.woff2) format("woff2"); font-weight: 400; @@ -591,49 +591,71 @@ code .conum[data-value]{ div.admonitionblock { position: relative; margin: 3rem 0; + border-radius: 0.75rem; } -div.admonitionblock > table > tbody > tr > td.icon { +div.admonitionblock td.icon { border-radius: 18px; color: #fff; display: flex; height: 30px; justify-content: flex-start; left: 5px; - padding: 0 1.5rem; position: absolute; top: -15px; - width: 135px; + width: 125px; +} +div.admonitionblock td.icon .fa::after{ + content: attr(title); + width: 100px; + margin-left: 26px; + display: block; + transform: translateY(4px); + text-transform: uppercase; + font-family: sans-serif; +} +div.admonitionblock td.icon .fa{ + background-position: center; + background-repeat: no-repeat; + height: 22px; + width: 22px; + margin-top: 2px; + margin-left: 7px; +} +.icon-caution{ + background: url(/assets/caution_icon.svg); +} +.icon-note{ + background: url(/assets/info_icon.svg); } -div.admonitionblock > table > tbody > tr > td.content { - padding: 20px 10px 15px 1rem; +.icon-warning{ + background: url(/assets/caution_icon.svg); +} +div.admonitionblock .title{ + margin-bottom: 1rem; + font-style: italic; +} +div.admonitionblock td.content { + padding: 2rem 1.5rem; + line-height: 2rem; } div.admonitionblock.caution { border: 2px dashed #ac2a34; } -div.admonitionblock.caution > table > tbody > tr > td.icon { +div.admonitionblock.caution td.icon { background: #ac2a34; } -div.admonitionblock.caution > table > tbody > tr > td.content { - color: #ac2a34; -} div.admonitionblock.note { border: 2px dashed #0f406b; } -div.admonitionblock.note > table > tbody > tr > td.icon { +div.admonitionblock.note td.icon { background: #0f406b; } -div.admonitionblock.note > table > tbody > tr > td.content { - color: #0f406b; -} div.admonitionblock.warning { border: 2px dashed #e37743; } -div.admonitionblock.warning > table > tbody > tr > td.icon { +div.admonitionblock.warning td.icon { background: #e37743; } -div.admonitionblock.warning > table > tbody > tr > td.content { - color: #e37743; -} h1, h2, h3, @@ -1349,7 +1371,7 @@ nav ul li:hover { display: none; flex-direction: column; padding-left: 1.75rem; - padding-top: 5rem; + padding-top: 6rem; position: relative; width: 21rem; z-index: 0; @@ -1439,9 +1461,10 @@ nav ul li:hover { } } .docs h1{ + margin-top: 1.5rem; margin-bottom: 2rem; } -.docs h2{ +.docs h2, .docs h3{ margin: 2rem 0 0.5rem 0; } .separator { @@ -1583,3 +1606,6 @@ code.doc { .component__example__code { overflow: hidden; } +.language-bash{ + cursor: pointer; +} -- GitLab From ccf51d0f826ab5c894f5a83d6657dd9e9a00758d Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 26 Apr 2021 20:18:21 -0600 Subject: [PATCH 091/116] fix typo --- website/Shpadoinkle/Website/Page/Home.hs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/website/Shpadoinkle/Website/Page/Home.hs b/website/Shpadoinkle/Website/Page/Home.hs index 0e74747a..09398377 100644 --- a/website/Shpadoinkle/Website/Page/Home.hs +++ b/website/Shpadoinkle/Website/Page/Home.hs @@ -144,7 +144,7 @@ featureSection = div [ class' feature__section ] data ComponentExample = ComponentExample - { title :: Text + { title' :: Text , description :: Text } deriving Generic @@ -155,7 +155,7 @@ componentExample componentExample l exs cex = onRecord l $ div [ class' component ] [ div [ class' component__info ] [ h2 [ class' component__info__title ] - [ text $ cex ^. #title ] + [ text $ cex ^. #title' ] , p_ [ text $ cex ^. #description ] ] @@ -168,15 +168,15 @@ componentExample l exs cex = onRecord l $ div [ class' component ] componentsSection :: (MonadJSM m, ExampleEffects m) => Examples Example -> Html m (Examples Example) componentsSection exs = div [ class' components__section ] [ componentExample #helloWorld exs $ ComponentExample - { title = "Hello World" + { title' = "Hello World" , description = "Shpadoinkle components are expressed as functions, no classes, inheritance, or JSX style new syntax. Purity of view logic is guaranteed by the type system." } , componentExample #counter exs $ ComponentExample - { title = "State is easy" + { title' = "State is easy" , description = "Components are simply functions that accept a state argument to render. Event listeners provide updates to the state. No props management, digest cycle, hooks, dispatch, or other such ceremony." } , componentExample #todo exs $ ComponentExample - { title = "An Application" + { title' = "An Application" , description = "Using event handlers, and pure functions we can compose applications without any further abstraction." } , img' [ alt "background transition", className "transition", src $(assetLink "/assets/purple_black_transition.svg") ] -- GitLab From 106bfecf7f84a9c2fcac05241fd5c8af7c88e081 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 26 Apr 2021 20:55:01 -0600 Subject: [PATCH 092/116] add tip --- website/assets/style.css | 12 ++++++++++++ website/assets/tip_icon.svg | 1 + 2 files changed, 13 insertions(+) create mode 100644 website/assets/tip_icon.svg diff --git a/website/assets/style.css b/website/assets/style.css index b6e0f4da..da03fb99 100644 --- a/website/assets/style.css +++ b/website/assets/style.css @@ -630,6 +630,9 @@ div.admonitionblock td.icon .fa{ .icon-warning{ background: url(/assets/caution_icon.svg); } +.icon-tip{ + background: url(/assets/tip_icon.svg); +} div.admonitionblock .title{ margin-bottom: 1rem; font-style: italic; @@ -650,12 +653,21 @@ div.admonitionblock.note { div.admonitionblock.note td.icon { background: #0f406b; } +div.admonitionblock.tip { + border: 2px dashed #41af46; +} +div.admonitionblock.tip td.icon { + background: #41af46; +} div.admonitionblock.warning { border: 2px dashed #e37743; } div.admonitionblock.warning td.icon { background: #e37743; } +.paragraph{ + margin-bottom: 2rem; +} h1, h2, h3, diff --git a/website/assets/tip_icon.svg b/website/assets/tip_icon.svg new file mode 100644 index 00000000..d747435a --- /dev/null +++ b/website/assets/tip_icon.svg @@ -0,0 +1 @@ + -- GitLab From 05746975e63fcd557843dd305392f5521db2f5a8 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Tue, 27 Apr 2021 17:16:31 -0600 Subject: [PATCH 093/116] docs looks good --- README.adoc | 106 ---------- console/README.md | 26 +-- core/README.md | 184 +----------------- html/README.md | 52 +---- lens/README.md | 2 +- router/README.md | 41 +--- website/assets/style.css | 64 +++++- website/docs/concepts.adoc | 12 +- website/docs/packages.adoc | 161 ++++++++++----- website/docs/packages/backends.adoc | 9 +- website/docs/packages/console.adoc | 6 +- website/docs/packages/core.adoc | 41 ++-- website/docs/packages/html.adoc | 4 +- website/docs/packages/lens.adoc | 4 +- website/docs/packages/router.adoc | 4 +- website/docs/tutorial.adoc | 2 - website/docs/tutorial/calculator.adoc | 11 +- website/docs/tutorial/composing.adoc | 2 +- .../docs/tutorial/immediate-execution.adoc | 2 +- 19 files changed, 231 insertions(+), 502 deletions(-) diff --git a/README.adoc b/README.adoc index e9592bb6..ad381fb7 100644 --- a/README.adoc +++ b/README.adoc @@ -63,109 +63,3 @@ Join the Shpadoinkle community on https://shpadoinkle.zulipchat.com/register[Zul == Actively Maintained This project is actively maintained by https://platonic.systems[image:https://platonic.systems/logo.svg[Platonic Systems, 25,25] Platonic Systems] - - -== Hackage Matrix - -[options="header"] -|=== -|Package |Version |Dependencies |Availablity -|Core -|https://hackage.haskell.org/package/Shpadoinkle[image:https://img.shields.io/hackage/v/Shpadoinkle.svg[Hackage]] -|http://packdeps.haskellers.com/feed?needle=Shpadoinkle[image:https://img.shields.io/hackage-deps/v/Shpadoinkle.svg[Hackage -Deps]] -|https://matrix.hackage.haskell.org/#/package/Shpadoinkle[image:https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle/badge[Hackage -CI]] - -|Snabbdom -|https://hackage.haskell.org/package/Shpadoinkle-backend-snabbdom[image:https://img.shields.io/hackage/v/Shpadoinkle-backend-snabbdom.svg[Hackage]] -|http://packdeps.haskellers.com/feed?needle=Shpadoinkle-backend-snabbdom[image:https://img.shields.io/hackage-deps/v/Shpadoinkle-backend-snabbdom.svg[Hackage -Deps]] -|https://matrix.hackage.haskell.org/#/package/Shpadoinkle-backend-snabbdom[image:https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle-backend-snabbdom/badge[Hackage -CI]] - -|Static -|https://hackage.haskell.org/package/Shpadoinkle-backend-static[image:https://img.shields.io/hackage/v/Shpadoinkle-backend-static.svg[Hackage]] -|http://packdeps.haskellers.com/feed?needle=Shpadoinkle-backend-static[image:https://img.shields.io/hackage-deps/v/Shpadoinkle-backend-static.svg[Hackage -Deps]] -|https://matrix.hackage.haskell.org/#/package/Shpadoinkle-backend-static[image:https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle-backend-static/badge[Hackage -CI]] - -|ParDiff -|https://hackage.haskell.org/package/Shpadoinkle-backend-pardiff[image:https://img.shields.io/hackage/v/Shpadoinkle-backend-pardiff.svg[Hackage]] -|http://packdeps.haskellers.com/feed?needle=Shpadoinkle-backend-pardiff[image:https://img.shields.io/hackage-deps/v/Shpadoinkle-backend-pardiff.svg[Hackage -Deps]] -|https://matrix.hackage.haskell.org/#/package/Shpadoinkle-backend-pardiff[image:https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle-backend-pardiff/badge[Hackage -CI]] - -|Console -|https://hackage.haskell.org/package/Shpadoinkle-console[image:https://img.shields.io/hackage/v/Shpadoinkle-console.svg[Hackage]] -|http://packdeps.haskellers.com/feed?needle=Shpadoinkle-console[image:https://img.shields.io/hackage-deps/v/Shpadoinkle-console.svg[Hackage -Deps]] -|https://matrix.hackage.haskell.org/#/package/Shpadoinkle-console[image:https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle-console/badge[Hackage -CI]] - -|Developer Tools -|https://hackage.haskell.org/package/Shpadoinkle-developer-tools[image:https://img.shields.io/hackage/v/Shpadoinkle-developer-tools.svg[Hackage]] -|http://packdeps.haskellers.com/feed?needle=Shpadoinkle-developer-tools[image:https://img.shields.io/hackage-deps/v/Shpadoinkle-developer-tools.svg[Hackage -Deps]] -|https://matrix.hackage.haskell.org/#/package/Shpadoinkle-developer-tools[image:https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle-developer-tools/badge[Hackage -CI]] - -|Disembodied -|https://hackage.haskell.org/package/Shpadoinkle-disembodied[image:https://img.shields.io/hackage/v/Shpadoinkle-disembodied.svg[Hackage]] -|http://packdeps.haskellers.com/feed?needle=Shpadoinkle-disembodied[image:https://img.shields.io/hackage-deps/v/Shpadoinkle-disembodied.svg[Hackage -Deps]] -|https://matrix.hackage.haskell.org/#/package/Shpadoinkle-Shpadoinkle-disembodied[image:https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle-disembodied/badge[Hackage -CI]] - -|Lens -|https://hackage.haskell.org/package/Shpadoinkle-lens[image:https://img.shields.io/hackage/v/Shpadoinkle-lens.svg[Hackage]] -|http://packdeps.haskellers.com/feed?needle=Shpadoinkle-lens[image:https://img.shields.io/hackage-deps/v/Shpadoinkle-lens.svg[Hackage -Deps]] -|https://matrix.hackage.haskell.org/#/package/Shpadoinkle-lens[image:https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle-lens/badge[Hackage -CI]] - -|Html -|https://hackage.haskell.org/package/Shpadoinkle-html[image:https://img.shields.io/hackage/v/Shpadoinkle-html.svg[Hackage]] -|http://packdeps.haskellers.com/feed?needle=Shpadoinkle-html[image:https://img.shields.io/hackage-deps/v/Shpadoinkle-html.svg[Hackage -Deps]] -|https://matrix.hackage.haskell.org/#/package/Shpadoinkle-html[image:https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle-html/badge[Hackage -CI]] - -|Router -|https://hackage.haskell.org/package/Shpadoinkle-router[image:https://img.shields.io/hackage/v/Shpadoinkle-router.svg[Hackage]] -|http://packdeps.haskellers.com/feed?needle=Shpadoinkle-router[image:https://img.shields.io/hackage-deps/v/Shpadoinkle-router.svg[Hackage -Deps]] -|https://matrix.hackage.haskell.org/#/package/Shpadoinkle-router[image:https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle-router/badge[Hackage -CI]] - -|Streaming -|https://hackage.haskell.org/package/Shpadoinkle-streaming[image:https://img.shields.io/hackage/v/Shpadoinkle-streaming.svg[Hackage]] -|http://packdeps.haskellers.com/feed?needle=Shpadoinkle-streaming[image:https://img.shields.io/hackage-deps/v/Shpadoinkle-streaming.svg[Hackage -Deps]] -|https://matrix.hackage.haskell.org/#/package/Shpadoinkle-streaming[image:https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle-streaming/badge[Hackage -CI]] - -|Template -|https://hackage.haskell.org/package/Shpadoinkle-template[image:https://img.shields.io/hackage/v/Shpadoinkle-template.svg[Hackage]] -|http://packdeps.haskellers.com/feed?needle=Shpadoinkle-template[image:https://img.shields.io/hackage-deps/v/Shpadoinkle-template.svg[Hackage -Deps]] -|https://matrix.hackage.haskell.org/#/package/Shpadoinkle-template[image:https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle-template/badge[Hackage -CI]] - -|Widgets -|https://hackage.haskell.org/package/Shpadoinkle-widgets[image:https://img.shields.io/hackage/v/Shpadoinkle-widgets.svg[Hackage]] -|http://packdeps.haskellers.com/feed?needle=Shpadoinkle-widgets[image:https://img.shields.io/hackage-deps/v/Shpadoinkle-widgets.svg[Hackage -Deps]] -|https://matrix.hackage.haskell.org/#/package/Shpadoinkle-widgets[image:https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle-widgets/badge[Hackage -CI]] - -|Examples -|https://hackage.haskell.org/package/Shpadoinkle-examples[image:https://img.shields.io/hackage/v/Shpadoinkle-examples.svg[Hackage]] -|http://packdeps.haskellers.com/feed?needle=Shpadoinkle-examples[image:https://img.shields.io/hackage-deps/v/Shpadoinkle-examples.svg[Hackage -Deps]] -|https://matrix.hackage.haskell.org/#/package/Shpadoinkle-examples[image:https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle-examples/badge[Hackage -CI]] - -|=== diff --git a/console/README.md b/console/README.md index 23076c3c..cf283dd5 100644 --- a/console/README.md +++ b/console/README.md @@ -8,28 +8,4 @@ [![Hackage Deps](https://img.shields.io/hackage-deps/v/Shpadoinkle-console.svg)](http://packdeps.haskellers.com/reverse/Shpadoinkle-console) [![Hackage CI](https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle-console/badge)](https://matrix.hackage.haskell.org/#/package/Shpadoinkle-console) -This package exposes some useful debugging functions for tracing the state of Shpadoinkle applications as they change over time. It exposes some type classes currently. - -```haskell -class LogJS (c :: Type -> Constraint) where - logJS :: c a => a -> JSM () - -class LogJS c => Trapper c where - trapper :: c a => JSContextRef -> a -> a - -class Assert (c :: Type -> Constraint) where - assert :: c a => Bool -> a -> JSM () -``` - -These are intended to by used with `TypeApplications`, so you can choose the means of conversion before passing to `console.log`. For example: - -```haskell -main :: IO () -main = runJSorWarp 8080 $ do - ctx <- askJSM - simple runParDiff initial (view . trapper @ToJSON ctx) getBody -``` - -This will log all state by first encoding to JSON with Aeson, then then logging with `JSON.parse` so the browser console has the nice native display. If we change it to `trapper @Show ctx` it will use the `Show` instance instead. - -We also export a handful of `console` bindings such as `console.time`, `console.table`, `console.info`, `console.warn`, `console.debug`, and of course `console.log`. +## [Documentation ->](https://shpadoinkle.org/package/console) diff --git a/core/README.md b/core/README.md index 24f848ff..8bfb05e0 100644 --- a/core/README.md +++ b/core/README.md @@ -8,186 +8,4 @@ [![Hackage Deps](https://img.shields.io/hackage-deps/v/Shpadoinkle.svg)](http://packdeps.haskellers.com/reverse/Shpadoinkle) [![Hackage CI](https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle/badge)](https://matrix.hackage.haskell.org/#/package/Shpadoinkle) -Shpadoinkle is a Haskell UI programming paradigm. - -## The core concept - -Is to model the user interface as a pure function from some model `a` to Html. -This is not a new idea in the slightest. Declaratively describing the view in terms -of the model through a data structure is the dominant approach in UI today. And -for good reason. - -If all we need is to render something based on some `a` we can have `Html` be a -simple data structure where `Html :: Type`: - -```haskell -view :: a -> Html -``` - -This might look something like: - -```haskell -view :: Text -> Html -view username = - H "div" [ ("class", "greeting") ] [ Text $ "Hi there!" <> username ] -``` - -## Events and effects - -Which is all well and good, and something we might expect from a static renderer, -like Heist, or Blaze. Shpadoinkle handles this by allowing for `Html` to have two -type variables associated with events, `Html :: (Type -> Type) -> Type -> Type`. - -The first is typically some Monad you want to use in response to events `m`, and -the second is the payload of those events, typically the model for your view `a`. - -These variables in `Html m a` are strickly about event listeners, so any view -that doesn't have event listeners should be parametic in both `m` and `a`. - -look at a toggle as an example: - -```haskell -toggle :: Applicative m => Bool -> Html m Bool -toggle b = h "div" [] - [ text $ "Currently it's " <> if b then "ON" else "OFF" - , h "button" [ listen "click" (not b) ] [ text "Toggle" ] - ] -``` - -That's it, we have a stateful view. When the user click's on -the "Toggle" button the state will switch. Because we do a pure -state transition in this function, `m` need only be `Applicative`. -We could put `Identity` here if we wanted to, but keeping `m` general -helps our views compose. - -But what if we need to do _more_? Well we can update our `m` to -have more functionality. We can add some logging to the console: - -```haskell -toggle :: Bool -> Html IO Bool -toggle b = h "div" [] - [ text $ "Currently it's " <> if b then "ON" else "OFF" - , h "button" - [ listen' "click" $ do - putStrLn "We toggled!" - return $ not b - ] [ text "Toggle" ] - ] -``` - -What if we want to access some record of capabilities? Or update some -concurrent memory thing? Let's say we have an enterprise grade Monad: - -```haskell -newtype App a = App { runApp :: RIO (TVar Metrics) a } - deriving (Functor, Applicative, Monad, MonadReader (TVar Metrics), MonadIO, MonadJSM) - - -toggle :: Bool -> Html App Bool -toggle b = h "div" [] - [ text $ "Currently it's " <> if b then "ON" else "OFF" - , h "button" - [ listen' "click" $ do - metrics <- ask - liftIO $ do - atomically . modifyTVar metrics $ - \m -> m { toggleCount = toggleCount m + 1 } - putStrLn "We toggled!" - return $ not b - ] [ text "Toggle" ] - ] -``` - -## Composing views - -In Shpadoinkle we can compose views without impedance if the types match, -or are parametric. For example: - -```haskell -hero :: Html m a -hero = h "h1" [] [ text "Online String Reverse" ] - -input :: Html m Text -input = h "input" [ onInput id ] [] - -view :: Text -> Html m Text -view s = h "div" [] - [ hero -- no impedance, this Html is fully generic - , input -- no impedance, this Html has matching types `(Text ~ Text)` - , text $ "Reversed: \"" <> reverse s <> "\"" - ] -``` - -If you have nesting, with different types, -we can resolve the mismatch using 'fmap' like so: - -```haskell -input :: Html m Text -input = h "input" [ onInput id ] [] - -view :: (Int, Text) -> Html m (Int, Text) -view (i,t) = h "div" [] - - -- here we update the `Text` side of the model - -- with the value produced by `input`, and we - -- increment the `Int` as well. - [ (\t_ -> (i + 1, t_)) <$> input - , text $ "Reversed: \"" <> reverse t <> "\"" - , text $ "you have reversed " <> pack (show i) <> " strings" - ] -``` - -## The primitive - -The Shpadoinkle programming model core primitive is the `shpadoinkle` function. - -```haskell -shpadoinkle - :: (Shpadoinkle b m a, Territory t, Eq a) => - => (m ~> JSM) -> (t a -> b m ~> m) -- How to render - -> a -> t a -- What is our model - -> (a -> Html (b m) a) -- What to render - -> b m RawNode -> JSM () -- Actually render -``` - -This is the machine that runs a Shpadoinkle view. To run we need -the following ingredients: - -### `m ~> JSM` - -We need a _Natural Transformation_ from our `m` to `JSM`, so that -we can perform the needed JavaScript effects in JSM from the `m` -you provide. - -### `t a -> b m ~> m` - -This a function that takes a state container of some kind `t`, -and returns a _Natural Transformation_ from our Shpadoinkle backend `b`, -to our monad `m`. Backends kind of works like Monad Transformers, where -`b` wraps our Monad `m`, and needs to be unwrappable. - -### `a` - -This is the initial value of our model. This will be passed to our view -for the first render. - -### `t a` - -This is the state container `t` that will drive the view. When the state -changes, we should re-render the view. The semantic behind determing when -to do this, is upto you via the `Territory` type class. Typically this is -just a `TVar` as that is the provided cannonical implimentation. - -### `a -> Html (b m) a` - -This is the view function, you actual application to render. It takes -the model and returns the html to render, such that it's events produce the -same model. - -### `b m RawNode` - -This is the raw node we that will wrap our view. If you want the Shpadoinkle view -to be the entire page, then you want to pass `document.body` as this node. -You could use this to embed a Shpadoinkle application into another application, -(such a Reflex-dom or Miso). - +## [Documentation ->](https://shpadoinkle.org/packages/core) diff --git a/html/README.md b/html/README.md index 89e34cbb..150881fa 100644 --- a/html/README.md +++ b/html/README.md @@ -8,54 +8,4 @@ [![Hackage Deps](https://img.shields.io/hackage-deps/v/Shpadoinkle-html.svg)](http://packdeps.haskellers.com/reverse/Shpadoinkle-html) [![Hackage CI](https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle-html/badge)](https://matrix.hackage.haskell.org/#/package/Shpadoinkle-html) -This module provides named functions for generating Html, and other browser utilities. - -For example, instead of writing: - -```haskell -view = h "div" [ ("class", PText "foo") ] - [ h "span" [] [ text "hi there" ] ] -``` - -You can write: - -```haskell -view = div "foo" [ span_ [ "hi there" ] ] -``` - -which is a bit nicer, and eleminates the risk of typeos in tag names. It also -provies some nice `IsString` instances for ergonomics. - -## Keyboard - -This module provides pattern synonyms for common key codes. For example: - -```haskell -div [ onKeyup $ \case - Enter -> fireLazors - UpArrow -> jump - DownArrow -> crouch - LeftArrow -> move -1 - RightArrow -> move 1 - ] -``` - -which is a bit nicer, and harder to get wrong than using magic int's to -identify keys. - -## Browser utilities - -We provide high-level APIs to lower-level browser APIs. Including: - -- Local Storage - -I know it's just one right now. -But this is the place to contribute more. Such as: - -- Scroll Position -- XHR Requests -- Visibility API -- Notifications -- ect... - - +## [Documentation ->](https://shpadoinkle.org/packages/html) diff --git a/lens/README.md b/lens/README.md index 0699c0d0..f455a065 100644 --- a/lens/README.md +++ b/lens/README.md @@ -8,4 +8,4 @@ [![Hackage Deps](https://img.shields.io/hackage-deps/v/Shpadoinkle-lens.svg)](http://packdeps.haskellers.com/reverse/Shpadoinkle-lens) [![Hackage CI](https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle-lens/badge)](https://matrix.hackage.haskell.org/#/package/Shpadoinkle-lens) -This module provides lens combinators for working with Shpadoinkle applications. +## [Documentation ->](https://shpadoinkle.org/lens) diff --git a/router/README.md b/router/README.md index 8fda8fc0..f9c3441e 100644 --- a/router/README.md +++ b/router/README.md @@ -9,43 +9,4 @@ [![Hackage CI](https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle-router/badge)](https://matrix.hackage.haskell.org/#/package/Shpadoinkle-router) -A Servant combinator-based router for Shpadoinkle single-page applications. -Consuming this router requires that you provide two types: - -- Type alias for the recognized URIs -- ADT representing views that can be rendered - -The relationship between these two types is surjective. meaning more than one URI -may result in the same route. This is important for backward compatibility, so the -routing schema can evolve while still supporting older schemas. - -Because interactions are done through the ADT, application code should be type-safe, -and route canonically. - -```haskell --- The accepted URIs -type SPA m - = "echo" :> QueryParam "echo" Text :> View m () - :<|> "v2" :> "echo" :> QueryParam "echo" Text :> View m () - :<|> "home" :> View m () - --- The routes that can be rendered -data Route - = Echo (Maybe Text) - | Home - --- Surjection from URIs to routes -routes :: SPA m :>> Route -routes - = REcho - :<|> REcho - :<|> Home - --- Canonical URI for each route -instance Routed SPA Route where - redirect = \case - REcho t -> Redirect (Proxy @("v2" :> "echo" :> QueryParam "echo" Text :> Raw)) ($ t) - Home -> Redirect (Proxy @("home" :> Raw)) id -``` - -The above specification can be used on both the client and the server. See the `servant-crud` example for more on how to use this technique. +## [Documentation ->](https://shpadoinkle.org/packages/router) diff --git a/website/assets/style.css b/website/assets/style.css index da03fb99..94032280 100644 --- a/website/assets/style.css +++ b/website/assets/style.css @@ -668,6 +668,65 @@ div.admonitionblock.warning td.icon { .paragraph{ margin-bottom: 2rem; } +.paragraph:last-child{ + margin-bottom: 0; +} +table.tableblock{ + margin-bottom: 2rem; +} +.tableblock.stretch{ + width: 100%; +} +.ulist ul{ + list-style: square; + padding-left: 2rem; + margin-bottom: 2rem; +} +td.tableblock, th.tableblock { + word-break: break-word +} +td.tableblock { + border-collapse: collapse; + font-size: .83333rem; + margin: 2rem 0 +} +table.tableblock thead th { + border-bottom: 2.5px solid #e1e1e1; + padding: .5rem +} +table.tableblock>:not(thead) th, table.tableblock td { + border-top: 1px solid #e1e1e1; + border-bottom: 1px solid #e1e1e1; + padding: .5rem +} + +td.tableblock>.content>:first-child { + margin-top: 0 +} + +.halign-left { + text-align: left +} + +.halign-right { + text-align: right +} + +.halign-center { + text-align: center +} + +.valign-top { + vertical-align: top +} + +.valign-bottom { + vertical-align: bottom +} + +.valign-middle { + vertical-align: middle +} h1, h2, h3, @@ -692,7 +751,7 @@ h3 { font-size: 2.625rem; } h3 { - font-size: 1.325rem; + font-size: 1.65rem; } } .split-button:active { @@ -1479,6 +1538,9 @@ nav ul li:hover { .docs h2, .docs h3{ margin: 2rem 0 0.5rem 0; } +.docs h3{ + font-weight: bold; +} .separator { --tw-border-opacity: 1; border-color: rgba(226, 224, 213, var(--tw-border-opacity)); diff --git a/website/docs/concepts.adoc b/website/docs/concepts.adoc index ca02fddf..dc133d59 100644 --- a/website/docs/concepts.adoc +++ b/website/docs/concepts.adoc @@ -1,20 +1,14 @@ :icons: font -Shpadoinkle is a new way to program user interfaces with Haskell. - -The core idea here is to model user interfaces as functions with this type: +Shpadoinkle is a new way to program user interfaces with Haskell. The core idea here is to model user interfaces as functions with this type: [source,haskell] ---- view :: Model -> Html m Model ---- -Here, `Model` is a data type representing your application's state. - -The `view` is a function that defines how to (visually) represent your -application's state. - -Changes to application state are dictated by the view. +Here, `Model` is a data type representing your application's state. The `view` is a function that defines how to (visually) represent your +application's state. Changes to application state are dictated by the view. Here is a simple example. It displays an integer. The integer can be incremented by clicking a button. diff --git a/website/docs/packages.adoc b/website/docs/packages.adoc index fb80ecbd..7be67bf8 100644 --- a/website/docs/packages.adoc +++ b/website/docs/packages.adoc @@ -1,7 +1,13 @@ :relfilesuffix: / :relfileprefix: / -:gitlab: https://gitlab.com/platonic/shpadoinkle +:gitlab: https://gitlab.com/platonic/shpadoinkle/-/tree/master/ :hackage: https://hackage.haskell.org/package/Shpadoinkle +:hackagebadge: https://img.shields.io/hackage/v/Shpadoinkle +:deps: http://packdeps.haskellers.com/feed?needle=Shpadoinkle +:depsbadge: https://img.shields.io/hackage-deps/v/Shpadoinkle +:matrix: https://matrix.hackage.haskell.org/#/package/Shpadoinkle +:matrixbadge: https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle +:haddock: https://shpadoinkle.org/ :icons: font Shpadoinkle is segregated into several packages that compose together or can be used separately. @@ -9,47 +15,112 @@ Shpadoinkle is segregated into several packages that compose together or can be [TIP] **Core** and a **Backend** are all that is needed to run a application. -xref:packages/core.adoc[Core] ({gitlab}/core[Gitlab], {hackage}[Hackage], https://shpadoinkle.org/core[Haddock]):: -This stores the core types and logic for Shpadoinkle. - -xref:packages/backends.adoc[Snabbdom Backend] ({gitlab}/backends/snabbdom[Gitlab], {hackage}-backend-snabbdom[Hackage], https://shpadoinkle.org/backend-snabbdom[Haddock]):: -Binding to the high performance Snabbdom.js Virtual DOM library written in JavaScript. - -xref:packages/backends.adoc[Static Backend] ({gitlab}/static[Gitlab], {hackage}-backend-static[Hackage], https://shpadoinkle.org/backend-static[Haddock]):: -Takes Shpadoinkle Html and renders it as a static HTML string. - -xref:packages/backends.adoc[ParDiff Backend] ({gitlab}/backends/pardiff[Gitlab], {hackage}-backend-pardiff[Hackage], https://shpadoinkle.org/backend-pardiff[Haddock]):: -Canonical Virtual DOM written in Haskell, and based on Alignable Functors. - -xref:packages/console.adoc[Console] ({gitlab}/console[Gitlab], {hackage}-console[Hackage], https://shpadoinkle.org/console[Haddock]):: -Support for the native JavaScript browser console. - -Developer Tools ({gitlab}/developer-tools[Gitlab]):: -Chrome extension to support Shpadoinkle debugging. - -Disembodied ({gitlab}/disembodied[Gitlab], https://shpadoinkle.org/disembodied[Haddock]):: -Static site generation for Shpadoinkle views. - -xref:packages/lens.adoc[Lens] ({gitlab}/lens[Gitlab], {hackage}-lens[Hackage], https://shpadoinkle.org/lens[Haddock]):: -Lens combinators for ergonomic composing of heterogeneous components, and after-the-fact modification of views. - -xref:packages/html.adoc[Html] ({gitlab}/html[Gitlab], {hackage}-html[Hackage], https://shpadoinkle.org/html[Haddock]):: -A template-generated DSL defining HTML tags, Properties, and Events. It includes some related Utilities like keyboard support. - -xref:packages/router.adoc[Router] ({gitlab}/router[Gitlab], {hackage}-router[Hackage], https://shpadoinkle.org/router[Haddock]):: - Single Page Application (SPA) router based on https://docs.servant.dev/en/stable/[Servant]. - -xref:packages/widgets.adoc[Widgets] ({gitlab}/widgets[Gitlab], {hackage}-widgets[Hackage], https://shpadoinkle.org/widgets[Haddock]):: -Logical types and structures for common UI components, along with batteries-included implementations. - -Streaming ({gitlab}/streaming[Gitlab], {hackage}-streaming[Hackage], https://shpadoinkle.org/streaming[Haddock]):: -Integration of the streaming library with Shpadoinkle continuations. - -Template ({gitlab}/template[Gitlab], {hackage}-template[Hackage], https://shpadoinkle.org/template[Haddock]):: -Read standard file formats into Shpadoinkle with Template Haskell - -Examples ({gitlab}/examples[Gitlab]):: -Small applications of Shpadoinkle to illustrate programming patterns. - -Isreal ({gitlab}/isreal[Gitlab], https://shpadoinkle.org/isreal[Haddock]):: -Microservice to support playgrounds (Snowman as a service) +[%header] +|=== +| Name/Intro | Gitlab | Haddock | Hackage | Dependencies | Availablity + +| xref:packages/core.adoc[Core] +| {gitlab}core[icon:gitlab[] Gitlab] +| {haddock}core[Haddock] +| {hackage}[image:{hackagebadge}.svg[Hackage]] +| {deps}[image:{depsbadge}.svg[Deps]] +| {matrix}[image:{matrixbadge}/badge[CI]] + +| xref:packages/backends.adoc[Snabbdom Backend] +| {gitlab}backends/snabbdom[icon:gitlab[] Gitlab] +| {haddock}backend-snabbdom[Haddock] +| {hackage}-backend-snabbdom[image:{hackagebadge}-backend-snabbdom.svg[Hackage]] +| {deps}-backend-snabbdom[image:{depsbadge}-backend-snabbdom.svg[Deps]] +| {matrix}-backend-snabbdom[image:{matrixbadge}-backend-snabbdom/badge[CI]] + +| xref:packages/backends.adoc[Static Backend] +| {gitlab}backends/static[icon:gitlab[] Gitlab] +| {haddock}backend-static[Haddock] +| {hackage}-backend-static[image:{hackagebadge}-backend-static.svg[Hackage]] +| {deps}-backend-static[image:{depsbadge}-backend-static.svg[Deps]] +| {matrix}-backend-static[image:{matrixbadge}-backend-static/badge[CI]] + +| xref:packages/backends.adoc[ParDiff Backend] +| {gitlab}backends/pardiff[icon:gitlab[] Gitlab] +| {haddock}backend-pardiff[Haddock] +| {hackage}-backend-pardiff[image:{hackagebadge}-backend-pardiff[Hackage]] +| {deps}-backend-pardiff[image:{depsbadge}-backend-pardiff.svg[Deps]] +| {matrix}-backend-pardiff[image:{matrixbadge}-backend-pardiff/badge[CI]] + +| xref:packages/console.adoc[Console] +| {gitlab}console[icon:gitlab[] Gitlab] +| {haddock}console[Haddock] +| {hackage}-console[image:{hackagebadge}-console[Hackage]] +| {deps}-console[image:{depsbadge}-console.svg[Deps]] +| {matrix}-console[image:{matrixbadge}-console/badge[CI]] + +| Developer Tools +| {gitlab}developer-tools[icon:gitlab[] Gitlab] +| {haddock}developer-tools[Haddock] +| {hackage}-developer-tools[image:{hackagebadge}-developer-tools[Hackage]] +| {deps}-developer-tools[image:{depsbadge}-developer-tools[Haddock]] +| {matrix}-developer-tools[image:{matrixbadge}-developer-tools/badge[CI]] + +| xref:packages/disembodied.adoc[Disembodied] +| {gitlab}disembodied[icon:gitlab[] Gitlab] +| {haddock}disembodied[Haddock] +| {hackage}-disembodied[image:{hackagebadge}-disembodied[Hackage]] +| {deps}-disembodied[image:{depsbadge}-disembodied[Haddock]] +| {matrix}-disembodied[image:{matrixbadge}-disembodied/badge[CI]] + +| xref:packages/lens.adoc[Lens] +| {gitlab}lens[icon:gitlab[] Gitlab] +| {haddock}lens[Haddock] +| {hackage}-lens[image:{hackagebadge}-lens[Hackage]] +| {deps}-lens[image:{depsbadge}-lens[Haddock]] +| {matrix}-lens[image:{matrixbadge}-lens/badge[CI]] + +| xref:packages/html.adoc[Html] +| {gitlab}html[icon:gitlab[] Gitlab] +| {haddock}html[Haddock] +| {hackage}-html[image:{hackagebadge}-html[Hackage]] +| {deps}-html[image:{depsbadge}-html[Haddock]] +| {matrix}-html[image:{matrixbadge}-html/badge[CI]] + +| xref:packages/router.adoc[Router] +| {gitlab}router[icon:gitlab[] Gitlab] +| {haddock}router[Haddock] +| {hackage}-router[image:{hackagebadge}-router[Hackage]] +| {deps}-router[image:{depsbadge}-router[Haddock]] +| {matrix}-router[image:{matrixbadge}-router/badge[CI]] + +| xref:packages/widgets.adoc[Widgets] +| {gitlab}widgets[icon:gitlab[] Gitlab] +| {haddock}widgets[Haddock] +| {hackage}-widgets[image:{hackagebadge}-widgets[Hackage]] +| {deps}-widgets[image:{depsbadge}-widgets[Haddock]] +| {matrix}-widgets[image:{matrixbadge}-widgets/badge[CI]] + +| Streaming +| {gitlab}streaming[icon:gitlab[] Gitlab] +| {haddock}streaming[Haddock] +| {hackage}-streaming[image:{hackagebadge}-streaming[Hackage]] +| {deps}-streaming[image:{depsbadge}-streaming[Haddock]] +| {matrix}-streaming[image:{matrixbadge}-streaming/badge[CI]] + +| Template +| {gitlab}template[icon:gitlab[] Gitlab] +| {haddock}template[Haddock] +| {hackage}-template[image:{hackagebadge}-template[Hackage]] +| {deps}-template[image:{depsbadge}-template[Haddock]] +| {matrix}-template[image:{matrixbadge}-template/badge[CI]] + +| Examples +| {gitlab}examples[icon:gitlab[] Gitlab] +| N/A +| N/A +| N/A +| N/A + +| Isreal +| {gitlab}isreal[icon:gitlab[] Gitlab] +| {haddock}isreal[Haddock] +| {hackage}-isreal[image:{hackagebadge}-isreal[Hackage]] +| {deps}-isreal[image:{depsbadge}-isreal[Haddock]] +| {matrix}-isreal[image:{matrixbadge}-isreal/badge[CI]] +|=== diff --git a/website/docs/packages/backends.adoc b/website/docs/packages/backends.adoc index adda82a7..c05cbd5e 100644 --- a/website/docs/packages/backends.adoc +++ b/website/docs/packages/backends.adoc @@ -1,12 +1,13 @@ -:gitlab: https://gitlab.com/platonic/shpadoinkle +:gitlab: https://gitlab.com/platonic/shpadoinkle/-/tree/master/ +:haddock: https://shpadoinkle.org/ :icons: font Render backends are pluggable in Shpadoinkle. This project currently comes with three backends: |=== -| ParDiff | {gitlab}/backends/pardiff#shpadoinkle-backend-pardiff[icon:gitlab[] Gitlab] | https://shpadoinkle.org/backend-pardiff[Haddock] | -| Snabbdom | {gitlab}/backends/snabbdom#shpadoinkle-backend-snabbdom[icon:gitlab[] Gitlab] | https://shpadoinkle.org/backend-snabbdom[Haddock] | -| Static | {gitlab}/backends/static#shpadoinkle-backend-static[icon:gitlab[] Gitlab] | https://shpadoinkle.org/backend-static[Haddock] | +| ParDiff | {gitlab}backends/pardiff[icon:gitlab[] Gitlab] | {haddock}backend-pardiff[Haddock] | +| Snabbdom | {gitlab}backends/snabbdom[icon:gitlab[] Gitlab] | {haddock}backend-snabbdom[Haddock] | +| Static | {gitlab}backends/static[icon:gitlab[] Gitlab] | {haddock}backend-static[Haddock] | |=== You do not need to change your application to use any of these. Our view code will work without alteration with any backend. The application should still build and run with both GHC and GHCjs. diff --git a/website/docs/packages/console.adoc b/website/docs/packages/console.adoc index d3f380bf..7b767123 100644 --- a/website/docs/packages/console.adoc +++ b/website/docs/packages/console.adoc @@ -1,6 +1,8 @@ +:haddock: https://shpadoinkle.org/ +:gitlab: https://gitlab.com/platonic/shpadoinkle/-/tree/master/ :icons: font -https://shpadoinkle.org/console/index.html[Haddock] +{gitlab}console[icon:gitlab[] Gitlab] | {haddock}console[Haddock] Support for logging to the JavaScript console, using three classes, as follows: @@ -31,3 +33,5 @@ main = runJSorWarp 8080 $ do ---- This will log all state by first encoding to JSON with Aeson, then logging with `JSON.parse` so the browser console has the nice native display. If you change it to `trapper @Show ctx` it will use the `Show` instance instead. + +We also export a handful of `console` bindings such as `console.time`, `console.table`, `console.info`, `console.warn`, `console.debug`, and of course `console.log`. diff --git a/website/docs/packages/core.adoc b/website/docs/packages/core.adoc index e9161b2b..5b5fb976 100644 --- a/website/docs/packages/core.adoc +++ b/website/docs/packages/core.adoc @@ -1,8 +1,10 @@ +:haddock: https://shpadoinkle.org/ +:gitlab: https://gitlab.com/platonic/shpadoinkle/-/tree/master/ :icons: font -https://shpadoinkle.org/core[Haddock] +{gitlab}core[icon:gitlab[] Gitlab] | {haddock}core[Haddock] -This module is for core types and logic. +This package is for core types and logic. == Html Type @@ -234,25 +236,22 @@ There is one application primitive, the `shpadoinkle` function. It is where thes [source,haskell] ---- shpadoinkle :: forall b m a. Backend b m a => Monad (b m) => Eq a - => (m ~> JSM) -> (TVar a -> b m ~> m) -> a -> TVar a -> (a -> Html (b m) a) -> b m RawNode -> JSM () -shpadoinkle toJSM toM initial model view stage = do - let - j :: b m ~> JSM - j = toJSM . toM model - - go :: RawNode -> VNode b m -> a -> JSM (VNode b m) - go c n a = j $ do - !m <- interpret toJSM (view a) - patch c (Just n) m - - setup @b @m @a $ do -- <1> - (c,n) <- j $ do - c <- stage -- <2> - n <- interpret toJSM (view initial) -- <3> - _ <- patch c Nothing n -- <4> - return (c,n) - _ <- shouldUpdate (go c) n model -- <5> - return () + => (m ~> JSM) -> (TVar a -> b m ~> m) -> TVar a -> (a -> Html (b m) a) -> b m RawNode -> JSM () +shpadoinkle toJSM toM model view stage = setup @b @m @a $ do -- <1> + + c <- j stage -- <2> + initial <- readTVarIO model + n <- go c Nothing initial -- <3> + () <$ shouldUpdate (go c . Just) n model -- <4> + + where + + j :: b m ~> JSM + j = toJSM . toM model + + go :: RawNode -> Maybe (VNode b m) -> a -> JSM (VNode b m) + go c n a = j $ patch c n =<< interpret toJSM (view a) + ---- <1> Run the `setup` for the backend. diff --git a/website/docs/packages/html.adoc b/website/docs/packages/html.adoc index e6a78b5e..483b9ace 100644 --- a/website/docs/packages/html.adoc +++ b/website/docs/packages/html.adoc @@ -1,6 +1,8 @@ +:haddock: https://shpadoinkle.org/ +:gitlab: https://gitlab.com/platonic/shpadoinkle/-/tree/master/ :icons: font -https://shpadoinkle.org/html[Haddock] +{gitlab}html[icon:gitlab[] Gitlab] | {haddock}html[Haddock] This package exposes a Haskell-generated template Digital Subscriber Line (DSL) of HTML tags, as well as properties. While not complete, this package is intended to eventually house utilities for web APIs. diff --git a/website/docs/packages/lens.adoc b/website/docs/packages/lens.adoc index bd47954d..df5ffa1e 100644 --- a/website/docs/packages/lens.adoc +++ b/website/docs/packages/lens.adoc @@ -1,8 +1,10 @@ :relfilesuffix: / :relfileprefix: / +:haddock: https://shpadoinkle.org/ +:gitlab: https://gitlab.com/platonic/shpadoinkle/-/tree/master/ :icons: font -https://shpadoinkle.org/lens[Haddock] +{gitlab}lens[icon:gitlab[] Gitlab] | {haddock}lens[Haddock] Lens combinators for Shpadoinkle applications. The primary use case is the composition of heterogeneous `Html` components. diff --git a/website/docs/packages/router.adoc b/website/docs/packages/router.adoc index 6a7c5f8a..16609f84 100644 --- a/website/docs/packages/router.adoc +++ b/website/docs/packages/router.adoc @@ -1,6 +1,8 @@ +:haddock: https://shpadoinkle.org/ +:gitlab: https://gitlab.com/platonic/shpadoinkle/-/tree/master/ :icons: font -https://shpadoinkle.org/router[Haddock] +{gitlab}router[icon:gitlab[] Gitlab] | {haddock}router[Haddock] This package contains a SPA (Single Page Application) router, based on https://www.servant.dev[Servant] combinators. diff --git a/website/docs/tutorial.adoc b/website/docs/tutorial.adoc index 0426d223..c77cefe0 100644 --- a/website/docs/tutorial.adoc +++ b/website/docs/tutorial.adoc @@ -5,8 +5,6 @@ _This is for those who would like to think they know precisely what they mean._ * xref:tutorial/calculator.adoc[Part 2, Simple Calculator ->] -* xref:tutorial/immediate-execution.adoc[Part 3, Immediate Execution ->] -* xref:tutorial/composing.adoc[Part 4, Composing HTML Elements ->] == Setup diff --git a/website/docs/tutorial/calculator.adoc b/website/docs/tutorial/calculator.adoc index 727904b7..0eb629cc 100644 --- a/website/docs/tutorial/calculator.adoc +++ b/website/docs/tutorial/calculator.adoc @@ -2,15 +2,10 @@ :relfileprefix: / :icons: font -Here we will build a simple calculator, following pioneers of the space - -http://weblog.luite.com/wordpress/?p=127[Luite Stegemann], -https://reflex-frp.org/tutorial[Ryan Trinkle], and -https://keera.co.uk/2020/05/28/building-a-reactive-calculator-in-haskell-1-5/[Ivan Perez]. +Here we will build a simple calculator, following pioneers of the space http://weblog.luite.com/wordpress/?p=127[Luite Stegemann], https://reflex-frp.org/tutorial[Ryan Trinkle], and https://keera.co.uk/2020/05/28/building-a-reactive-calculator-in-haskell-1-5/[Ivan Perez]. * xref:tutorial.adoc[<- Part 1, Basic concepts] * xref:tutorial/immediate-execution.adoc[Part 3: Immediate execution ->] -* xref:tutorial/composing.adoc[Part 4, Composing HTML Elements ->] == Hello World @@ -51,7 +46,7 @@ To start, we will need a simple single number input: num :: Int -> Html m Int num x = input' [ value . pack $ show x -- <1> - , onInput (const . fromMaybe 0 . readMay . unpack) -- <2> +, onInput (const . fromMaybe 0 . readMay . unpack) -- <2> ] ---- @@ -164,7 +159,7 @@ Now if you are looking at the above code and starting to twitch because you are You can see the final code https://gitlab.com/platonic/shpadoinkle/-/blob/master/examples/Calculator.hs[here] running below: ++++ - + ++++ Next we emulate a real-world immediate execution calculator in part 3. diff --git a/website/docs/tutorial/composing.adoc b/website/docs/tutorial/composing.adoc index 748c544a..b4ea9153 100644 --- a/website/docs/tutorial/composing.adoc +++ b/website/docs/tutorial/composing.adoc @@ -2,7 +2,7 @@ :relfileprefix: / :icons: font -xref:tutorial/immediate-execution.adoc[<- Back to part 3, Emulating Immediate Execution] +* xref:tutorial/immediate-execution.adoc[<- Back to part 3, Emulating Immediate Execution] Composing HTML elements is straightforward in Shpadoinkle when the elements all have the same model type; nesting them in each other works automatically. diff --git a/website/docs/tutorial/immediate-execution.adoc b/website/docs/tutorial/immediate-execution.adoc index b2c94a14..a38eb020 100644 --- a/website/docs/tutorial/immediate-execution.adoc +++ b/website/docs/tutorial/immediate-execution.adoc @@ -2,8 +2,8 @@ :relfileprefix: / :icons: font -* xref:tutorial.adoc[<- See part 1, Basic concepts] * xref:tutorial/calculator.adoc[<- Back to part 2, Simple calculator] +* xref:tutorial/composing.adoc[Go to part 4, Composing Heterogeneous Elements ->] All the prior articles on writing a calculator in GHCjs feature old school functionality, namely https://en.wikipedia.org/wiki/Calculator_input_methods#Immediate_execution[immediate execution], which is a terrible UX. If you ever make an actual calculator app, at least let your users type into an input the expression they want evaluated. However, this choice makes good sense, as it requires a state machine and some other properties that make it good for learning. -- GitLab From ba65227243a665f1e68f408fb78089e91888c266 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Tue, 27 Apr 2021 17:18:22 -0600 Subject: [PATCH 094/116] update tag line --- README.adoc | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/README.adoc b/README.adoc index ad381fb7..7765bceb 100644 --- a/README.adoc +++ b/README.adoc @@ -14,7 +14,7 @@ https://hoogle.shpadoinkle.org[image:https://img.shields.io/badge/-Hoogle-lightg = Shpadoinkle -https://www.youtube.com/watch?v=0CizU8aB3c8[Shpadoinkle] is a Haskell UI programming paradigm. +https://www.youtube.com/watch?v=0CizU8aB3c8[Shpadoinkle] is a new Functional UI programming paradigm. == Documentation @@ -23,15 +23,9 @@ https://www.youtube.com/watch?v=0CizU8aB3c8[Shpadoinkle] is a Haskell UI program * https://shpadoinkle.org/docs/tutorial/index.html[Tutorial] * https://shpadoinkle.org/docs/packages/index.html[Packages] -=== Contribute to Documentation - -Help is always appreciated on documentation. Haddocks and READMEs work using standard tooling. If you wish to contribute to the Antora Documentation, simply: +== Community -[source,bash] ----- -$ nix-shell docs -$ serve-docs ----- +Join the Shpadoinkle community on https://shpadoinkle.zulipchat.com/register[Zulip]! == Snowman @@ -56,9 +50,6 @@ $ nix-shell nix/lint.nix --run 'hlint .' Correct version of hlint is also available within `nix-shell`. -== Community - -Join the Shpadoinkle community on https://shpadoinkle.zulipchat.com/register[Zulip]! == Actively Maintained -- GitLab From 0004dc07bc3f445cece3a0fd98c5c48bfe6328f7 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Tue, 27 Apr 2021 17:58:08 -0600 Subject: [PATCH 095/116] keybase was already handled --- website/default.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/website/default.nix b/website/default.nix index 53904f20..c78d4872 100644 --- a/website/default.nix +++ b/website/default.nix @@ -44,6 +44,5 @@ in ln -s ${./assets} $out/assets ln -s ${opti (setMay pkgsJS.haskell.packages.${util.compilerjs}.Shpadoinkle-website "run")}/bin/run.jsexe/${file} $out/all.min.js ${setMay pkgs.haskell.packages.${compiler}.Shpadoinkle-website "disembodied"}/bin/disembodied -o $out - cp ${./keybase.txt} $out/keybase.txt echo ${pkgs.lib.commitIdFromGitRepo ../.git} > $out/version '' -- GitLab From b273e3050171e63b5c961d8d4d198fd267ecda3e Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Wed, 28 Apr 2021 17:43:03 -0600 Subject: [PATCH 096/116] actual assets fix --- website/Shpadoinkle/Website/Partials/Nav.hs | 2 +- website/Shpadoinkle/Website/Types/Nav.hs | 8 ++++---- website/default.nix | 7 ++++++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/website/Shpadoinkle/Website/Partials/Nav.hs b/website/Shpadoinkle/Website/Partials/Nav.hs index 0f8842d2..60e62b8e 100644 --- a/website/Shpadoinkle/Website/Partials/Nav.hs +++ b/website/Shpadoinkle/Website/Partials/Nav.hs @@ -23,7 +23,7 @@ toActive = \case RHome -> Just NHome RGettingStarted _ -> Just NGettingStarted RTutorial _ -> Just NTutorial - RSandbox -> Just NSandbox + -- RSandbox -> Just NSandbox _ -> Nothing diff --git a/website/Shpadoinkle/Website/Types/Nav.hs b/website/Shpadoinkle/Website/Types/Nav.hs index fa13e042..e2af1629 100644 --- a/website/Shpadoinkle/Website/Types/Nav.hs +++ b/website/Shpadoinkle/Website/Types/Nav.hs @@ -7,7 +7,7 @@ module Shpadoinkle.Website.Types.Nav where -import Shpadoinkle.Website.Types.Route as R (Route (RGettingStarted, RHome, RSandbox, RTutorial)) +import Shpadoinkle.Website.Types.Route as R (Route (RGettingStarted, RHome, RTutorial)) import Shpadoinkle.Website.Types.Route.GettingStarted (Route (RGSIndex)) import Shpadoinkle.Website.Types.Route.Tutorial (Route (RTIndex)) import Shpadoinkle.Widgets.Types (Humanize (..)) @@ -17,7 +17,7 @@ data Nav = NHome | NGettingStarted | NTutorial - | NSandbox +-- | NSandbox deriving (Eq, Ord, Enum, Bounded) @@ -26,7 +26,7 @@ instance Humanize Nav where NHome -> "Home" NGettingStarted -> "Get Started" NTutorial -> "Tutorial" - NSandbox -> "Sandbox" +-- NSandbox -> "Sandbox" toRoute :: Nav -> R.Route @@ -34,6 +34,6 @@ toRoute = \case NHome -> RHome NGettingStarted -> RGettingStarted RGSIndex NTutorial -> RTutorial RTIndex - NSandbox -> RSandbox +-- NSandbox -> RSandbox diff --git a/website/default.nix b/website/default.nix index c78d4872..9d198b71 100644 --- a/website/default.nix +++ b/website/default.nix @@ -41,7 +41,12 @@ in } '' mkdir $out ${brand.icons.mkIcons brand.logo.thumbnail "$out"} - ln -s ${./assets} $out/assets + mkdir $out/assets + for asset in ${./assets}/* + do + ln -s $asset $out/assets/$(basename $asset) + done + ln -s ${brand.logo.full.svg { color = "bright_yellow"; }} $out/assets/landing_logo.svg ln -s ${opti (setMay pkgsJS.haskell.packages.${util.compilerjs}.Shpadoinkle-website "run")}/bin/run.jsexe/${file} $out/all.min.js ${setMay pkgs.haskell.packages.${compiler}.Shpadoinkle-website "disembodied"}/bin/disembodied -o $out echo ${pkgs.lib.commitIdFromGitRepo ../.git} > $out/version -- GitLab From 41682750b4aed29e93c9054749e7b0d240c12677 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 29 Apr 2021 22:38:57 -0600 Subject: [PATCH 097/116] 404 --- html/Shpadoinkle/Html/Property.hs | 13 ++ website/Shpadoinkle-website.cabal | 5 + .../Shpadoinkle/Website/Page/Documentation.hs | 4 +- .../Shpadoinkle/Website/Page/FourOhFour.hs | 124 ++++++++++++++++++ website/Shpadoinkle/Website/Types/SPA.hs | 2 +- website/Shpadoinkle/Website/View.hs | 7 +- website/assets/game/CowBoyDrawWeapon.png | Bin 0 -> 1992 bytes website/assets/game/CowBoyHolsterWeapon.png | Bin 0 -> 2238 bytes website/assets/game/CowBoyIdle.png | Bin 0 -> 1804 bytes website/assets/game/CowBoyQuickDrawShot.png | Bin 0 -> 3650 bytes website/assets/game/CowBoyRapidFire.png | Bin 0 -> 22764 bytes website/assets/game/CowBoyShoot.png | Bin 0 -> 2353 bytes website/assets/game/CowBoySmokingIdle.png | Bin 0 -> 1992 bytes website/assets/game/CowBoyWalking.png | Bin 0 -> 2239 bytes website/assets/style.css | 3 + website/default.nix | 5 +- 16 files changed, 154 insertions(+), 9 deletions(-) create mode 100644 website/Shpadoinkle/Website/Page/FourOhFour.hs create mode 100644 website/assets/game/CowBoyDrawWeapon.png create mode 100644 website/assets/game/CowBoyHolsterWeapon.png create mode 100644 website/assets/game/CowBoyIdle.png create mode 100644 website/assets/game/CowBoyQuickDrawShot.png create mode 100644 website/assets/game/CowBoyRapidFire.png create mode 100644 website/assets/game/CowBoyShoot.png create mode 100644 website/assets/game/CowBoySmokingIdle.png create mode 100644 website/assets/game/CowBoyWalking.png diff --git a/html/Shpadoinkle/Html/Property.hs b/html/Shpadoinkle/Html/Property.hs index c4962792..faf5afc3 100644 --- a/html/Shpadoinkle/Html/Property.hs +++ b/html/Shpadoinkle/Html/Property.hs @@ -5,7 +5,9 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TupleSections #-} {-# OPTIONS_GHC -fno-warn-orphans #-} +{-# OPTIONS_GHC -fno-warn-redundant-constraints #-} -- | This module provides a DSL for HTML properties @@ -80,6 +82,17 @@ for' :: Text -> (Text, Prop m a) for' = textProperty "htmlFor" +styleProp :: [(Text, Text)] -> (Text, Prop m a) +styleProp = textProperty "style" . intercalate ";" . fmap subStyle + where subStyle (k,v) = k <> ":" <> v + + +px, toEm, rem :: (Num a, Show a) => a -> Text +px = (<> "px") . pack . show +toEm = (<> "em") . pack . show +rem = (<> "rem") . pack . show + + $(msum <$> mapM mkBoolProp [ "checked", "selected", "hidden", "autocomplete", "autofocus", "disabled", "autoplay", "controls", "loop" , "multiple", "novalidate", "readonly", "required", "ismap", "usemap", "default'", "reversed" diff --git a/website/Shpadoinkle-website.cabal b/website/Shpadoinkle-website.cabal index 00076f7b..c44f7747 100644 --- a/website/Shpadoinkle-website.cabal +++ b/website/Shpadoinkle-website.cabal @@ -48,6 +48,7 @@ executable disembodied Shpadoinkle.Website.Style Shpadoinkle.Website.Page.Home Shpadoinkle.Website.Page.Documentation + Shpadoinkle.Website.Page.FourOhFour Shpadoinkle.Website.Partials.Template Shpadoinkle.Website.Types.Effects.Example Shpadoinkle.Website.Types.Effects.Hooglable @@ -77,6 +78,7 @@ executable disembodied build-depends: Shpadoinkle + , Shpadoinkle-backend-snabbdom , Shpadoinkle-console , Shpadoinkle-disembodied , Shpadoinkle-html @@ -100,6 +102,7 @@ executable disembodied , network-uri , servant , servant-server + , stm , text >=1.2.3 && <1.3 , time , unliftio @@ -125,6 +128,7 @@ executable run Shpadoinkle.Website.Partials.Social Shpadoinkle.Website.Page.Home Shpadoinkle.Website.Page.Documentation + Shpadoinkle.Website.Page.FourOhFour Shpadoinkle.Website.Types.CurrentYear Shpadoinkle.Website.Types.Nav Shpadoinkle.Website.Types.Home @@ -174,6 +178,7 @@ executable run , network-uri , random , servant + , stm , text >=1.2.3 && <1.3 , time , unliftio diff --git a/website/Shpadoinkle/Website/Page/Documentation.hs b/website/Shpadoinkle/Website/Page/Documentation.hs index 9d56f7c2..2c158a5a 100644 --- a/website/Shpadoinkle/Website/Page/Documentation.hs +++ b/website/Shpadoinkle/Website/Page/Documentation.hs @@ -101,8 +101,8 @@ sideNavLink cur toR r = li_ docsWrap :: (MonadJSM m, Humanize r) => r -> [Html m a] -> Route -> Html m a docsWrap r content' cur = section [ class' flex ] [ nav [ class' side_nav ] - [ expandable cur RGettingStarted "Getting Started" - , top cur RConcepts "Basic Concept" Nothing + [ top cur RConcepts "Basic Concept" Nothing + , expandable cur RGettingStarted "Getting Started" , expandable cur RTutorial "Tutorial" , expandable cur RPackages "Reference" ] diff --git a/website/Shpadoinkle/Website/Page/FourOhFour.hs b/website/Shpadoinkle/Website/Page/FourOhFour.hs new file mode 100644 index 00000000..808ec2e4 --- /dev/null +++ b/website/Shpadoinkle/Website/Page/FourOhFour.hs @@ -0,0 +1,124 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE ExtendedDefaultRules #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TupleSections #-} +{-# OPTIONS_GHC -fno-warn-type-defaults #-} + + +module Shpadoinkle.Website.Page.FourOhFour where + + +import Control.Concurrent.STM (TVar, atomically, + modifyTVar, retry) +import Control.Monad.IO.Class (liftIO) +import Data.Text (Text) +import GHC.Generics (Generic) +import GHCJS.DOM (currentDocumentUnchecked, + currentWindowUnchecked) +import GHCJS.DOM.Document (createElement) +import GHCJS.DOM.Element (setId) +import GHCJS.DOM.RequestAnimationFrameCallback (RequestAnimationFrameCallback, + newRequestAnimationFrameCallback) +import GHCJS.DOM.Window (Window, + requestAnimationFrame) +import Language.Javascript.JSaddle (toJSVal) +import Shpadoinkle (JSM, NFData, + RawNode (..), + newTVarIO, + shpadoinkle) +import Shpadoinkle.Backend.Snabbdom (runSnabbdom) +import Shpadoinkle.Html as H +import Shpadoinkle.Html.TH.AssetLink (assetLink) +import UnliftIO.Concurrent (forkIO, threadDelay) + + +default (Text) + + +data Direction = FaceLeft | FaceRight + deriving (Eq, Ord, Show, Generic, NFData) + + +data Axis = X | Y + + +newtype Position (a :: Axis) = Position { unPosition :: Float } + deriving stock (Eq, Ord, Show, Generic) + deriving newtype (Num) deriving anyclass (NFData) + + +newtype Velocity (a :: Axis) = Velocity { unVelocity :: Float } + deriving stock (Eq, Ord, Show, Generic) + deriving newtype (Num) deriving anyclass (NFData) + + +newtype Clock = Clock { unClock :: Double } + deriving stock (Eq, Ord, Show, Generic) + deriving newtype (Num) deriving anyclass (NFData) + + +data Game = Game + { x :: Position 'X + , y :: Position 'Y + , vx :: Velocity 'X + , vy :: Velocity 'Y + , clock :: Clock + , direction :: Direction + } deriving (Eq, Ord, Show, Generic, NFData) + + + +spriteWidth, spriteHeight, spriteCount :: Int +spriteWidth = 384 +spriteHeight = 48 +spriteCount = 8 + + +game :: Game -> Html m Game +game g = H.div' [ id' "avatar", styleProp styles ] + where + styles = + [ ("height", px spriteHeight) + , ("width", px innerWidth) + , ("background-image", "url(" <> (if vx g > 0 then walking else idle) <> ")") + , ("background-position", px (innerWidth * (spriteCount - spriteTime)) <> ", 0") + ] + innerWidth = spriteWidth `Prelude.div` spriteCount + spriteTime = floor (unClock (clock g) / (1000 / 8)) `mod` spriteCount + walking = $(assetLink "/assets/game/CowBoyWalking.png") + idle = $(assetLink "/assets/game/CowBoyIdle.png") + + +tick :: Clock -> Game -> Game +tick d g = g { clock = d } + + +animate :: Window -> TVar Game -> JSM RequestAnimationFrameCallback +animate win model = newRequestAnimationFrameCallback $ \d -> () <$ do + liftIO . atomically $ modifyTVar model $ tick $ Clock d + requestAnimationFrame win =<< animate win model + + +play :: JSM RawNode +play = do + win <- currentWindowUnchecked + doc <- currentDocumentUnchecked + elm <- createElement doc "div" + setId elm "game" + model <- newTVarIO $ Game 0 0 0 0 0 FaceRight + _ <- requestAnimationFrame win =<< animate win model + raw <- RawNode <$> toJSVal elm + _ <- forkIO $ threadDelay 1 + >> shpadoinkle id runSnabbdom model game (pure raw) + return raw + + +view :: Html m a +view = baked $ (, retry) <$> play diff --git a/website/Shpadoinkle/Website/Types/SPA.hs b/website/Shpadoinkle/Website/Types/SPA.hs index 003ae66b..c0e8c4d4 100644 --- a/website/Shpadoinkle/Website/Types/SPA.hs +++ b/website/Shpadoinkle/Website/Types/SPA.hs @@ -79,7 +79,7 @@ routes :<|> RSandbox :<|> RFourOhFour - :<|> RHome + :<|> RFourOhFour instance Routed (SPA m) Route where diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index 163e7447..dbf14232 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -28,6 +28,7 @@ import Shpadoinkle.Isreal.Types as Swan import Shpadoinkle.Lens (onSum) import Shpadoinkle.Website.Component.LiveExample import qualified Shpadoinkle.Website.Page.Documentation as Documentation +import qualified Shpadoinkle.Website.Page.FourOhFour as FourOhFour import qualified Shpadoinkle.Website.Page.Home as Home import Shpadoinkle.Website.Partials.Template import Shpadoinkle.Website.Style (documentation) @@ -46,10 +47,6 @@ import UnliftIO.Async (Concurrently (..), default (Text) -fourOhFour :: Html m a -fourOhFour = h2_ [ "404" ] - - topClass :: Route -> ClassList topClass = \case RHome -> mempty @@ -65,7 +62,7 @@ view cy vm = div [ class' $ topClass cur ] . wrapper cy (vm ^. #mobileMenu) cur (RPackages pr, MStatic) -> Documentation.packages pr cur (RTutorial tutr, MStatic) -> Documentation.tutorial tutr cur (RSandbox, MStatic) -> "" - _ -> fourOhFour + _ -> FourOhFour.view where cur = vm ^. #currentRoute diff --git a/website/assets/game/CowBoyDrawWeapon.png b/website/assets/game/CowBoyDrawWeapon.png new file mode 100644 index 0000000000000000000000000000000000000000..bc605a64d8c30db78bb9a6ae08a9cb792cac4f42 GIT binary patch literal 1992 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 literal 0 HcmV?d00001 diff --git a/website/assets/game/CowBoyIdle.png b/website/assets/game/CowBoyIdle.png new file mode 100644 index 0000000000000000000000000000000000000000..4cd492e6646a036eac56954448b4810b5dd10e08 GIT binary patch literal 1804 zcmeAS@N?(olHy`uVBq!ia0y~yU<_bjU@+idV_;x#&sH*LU|?V@4sv&5Sa(k5B?AN7 zF;5rAkcv5PXZw4^gp0J-2j5vS;fPd6p%`-~%aJyrOR)=2uF`aBKIyhn@xd`YC-bc( z6Pxa)242wFrR}5`kx@RW$z<0`r-ezb9NQWf9J(+gYQh$=AZEd3+{N+lW8VKZ{`{`` z`<&-L9gIF#`0uYizw`N>?`cdNO$r#u@#OXf1(Au9k5^|M_ba(BdwY%EsVZ#=p#aB% zp0yQPJ46EC@ha&5Kcm)C&A_6_sowwn3PVDA`Q9b_`L!1u`2YIAo@rcnq#fq{o9V;$ zoq^$xG(&kk-wLJ`H*Uu2&pduc;?L*r!LKVrx-yh`8ne^$v-x_ zo+VYYCXwskN;cm_hD^@twxt}|4BN6bFLci27DMKOkbs?L4Lh8&nw^v%++kq&|8FJxyK06^4JYOc&o@Wb&v#wv!TaFb z-kDMiJ+U=?+U9AD4E)P}GcI`d_lTGEyE`%p57%TYV`MOy#qi+#e$}%_cRAc(Sd@44 z%WKVqV&6*)HHo`SUe~{IF)u5-e)p8>fly|KfW<*BJ2yqf>J;WNEUOpM`x`7Cze(sva8p5QY(pb#wap;Iw^@plAk3R4EF7x2k{P4dgYuL3IKQNcqpYvI>CAxTS z)w}yUEdzGd<}W*T^}gG+ug^`V@ArAjH6@*%W-j!3{e#s~Wt7%~&=Uap*SF<8^Qgm>x2S^u7XT)3;g zH15t`)`n^O&;NS7q@jr4VMe{kls{)#y0jNfxx;vm_s#L+uN$jl_@*9jZ~C*m>1_St z#l|NV{=GQyLFuH@hs^W8s+TfuWnHlAZWb@Y)+daM{w`*6;52U3imle@=NH}782H7! zvhcgus@rQd`uVRuwO9XEeLLW8p6k+V{S(Vym95TS>-=M#{mhz_Ux#>l|X z!=Jc#v2nNk^Ep=axrV`C%!M=d?h;k0TAHozG0*F|oc{ldf+4%Uy*lP}F+P3ouGaf@ ze;E#}X8OWW`ZNA+9_x#odHf51Gdui0oXT)u{ri1|0aL1O*Q_*knfLYh%kK_nS%Mf> zJZFP{8e{Fu1&%~4A z*RyLEzQ6kV_n>#?zd6M_e$Qs%OnFnMp%*7T=kM9Ov-?gdp8A>mdB1%HXZZf;4=E!2 ztzTc~uuF+C=oNhF_fq)EWl%PftF-@X7n^zgD$us}AcjsNMOq)12YlzvuCK5C5EAx+^*G zXYtzKoa;*MPey4Qo~nO#^X7x}2VZ#^zQxLz)=O8ejaO%#Qq8dA_w5JQ7##M@mD zzQp}J{c^m@Df!m-GDd%=r`z+y&HgOEjZfo2@!w@y3+^xrK8W&N)2XD<|= zy|etmeT{Y3^ItKYvSqmQSGqNbIfJe1-`v{6s#Cc=Ke^k_XG*m5UcN55sC4z0lMkM^ zbJ>g5-+z7mB;S-`28Mh8_6cj~-ub(j`-#J+z0fy#C<}Mb<43 zRy^;rnln$c==<|S$Hh-wQxh$Vu{aQ^WM*`6^6Ka(VhuljyZ-r`EPeRNtFV(cei!dw z|6_f-(QjSe)OqjTIRCxT7wq=uuhPGNoS&aOc$fX}saTwV(UEP&i|V4+h)X}svQ|rp zaJbs?dUCF{o9M^y-hqD}_XRH1_!s%S|BVCdLBad~{w@D{GwMcRR>Al02kZSK)DQfR TI#|fSz`)??>gTe~DWM4fHZ*$O literal 0 HcmV?d00001 diff --git a/website/assets/game/CowBoyQuickDrawShot.png b/website/assets/game/CowBoyQuickDrawShot.png new file mode 100644 index 0000000000000000000000000000000000000000..90a6b0cdf9bf5f6ae46e606dc2bf5236ee46912f GIT binary patch literal 3650 zcmeAS@N?(olHy`uVBq!ia0y~yU`}9QU@+idV_;w?{~~#gfq{XsILO_JVcj{ImkbQN ze>`0rLn`LHotxVca$WNH|6nuIEa~`e{mCi8oUGi|M|w9GL`O15;^+UHkIvm~{QAA+^PQWY&n$kolR>dXz=?wt!b9rG5>^h*4{mgH3VK?c`0{%0F-G?`-uIe|g`qgG@ehqL|^ExA%l+iW3{xd^@2dfUHE7vjZ!onClT_QHGq7yXL5|IZ)!%YF8N zWl!Z=|M0&%O>X^d{J?Z6^!v#1nUg2_4c>8Joe{L*#DAyxBsikxgxiI9dO%h z?NFZy(v{kIFn)`~g*WmC@9J1zx%cf+M!#b2|7jZf>&idGoPV8nPtK#ZDrooq`$x+R z?)~$!ThTDFi2cytW1p?_?#Xo=T^YFO+req+X^*E~w@dif!~E;AJ=d4z^&&za{J%=6 zoc%ZJ?&`{nlYg`HqLLlfLvU#iEbVp@d!s=M~*BIQp`UT|$W*S|W>S#r~&;$__B zb1Ku!7?#AfWS*D*)$jQK*uwu3lAru@m07p_mstES=eO0xclIEFir^Xs1$?gt`%w~8iRHG8}vUf*$wok8pMX5r;WUWMr2`rQ8SOFz@!m-4MF ze}gqsgg*G^vUToMwlBLZ*UbHIBX7*{-Da1j&JEo1D|?|k{5`ol;%cP` z!-sa})rD;B>X-#n#0iFJk`fAt%yTYonF(W}JxMy!+rvi7orfnSLdE+_L}FJ0thZmN^r@oZfZs$F0B3H;U7WUY@oolYCpd z;AZ@0P-0B_B)(fRi|5}W<{dNs98LIrdeKdLwen|vEL)B%Je1F7OnY3FeBEjB{%^tu z`px3)XZ1Xf*@U$EQcoJithm(A9SD}`?RsoXtYZOgFZ z%UaWn%ExC{oiEL07dgC5wQ~K_N6WMRHg3$lll?G?N3CA3B)8$1-HPM)_4dWwV%#Ws z>)x`q`d6oC2z_7=>S_F!dSSm~#lL9_@7jl&>}LK2a)!sR<3`MBObiV__rJ}2@pIjU z+CA5!cl?&k>@B=pyXX3AyL+#B^Y6xd-nnib1H*!i{I5Qimp;>ex9$76bsk^u_-T{} z{TA6SS#&=YlxwvGH1{<`{0W}>H|gDp#kOpj|0B3IIAzPVUaNUo`0wf0)c?2sJijW> zT(Fwsv-pIqe`1T+Hw7}>wlvw3Flm=v#*z9TM-*!+{#_6KyLYp;&F85Nd6I=&_m-En zPLICz-^T5~K=$YM3!i4Do%-AJJNvx++kb5#j}v}R_xNjNuHwCb+@?^XYpHp$h8PtrIMAI+GvTiWDlfbGBk))fg)ehar} z{j+=jhKoNp*C9IkWnoaOk8*DPhoyTN8e*g44(++N-1h(VjE|;|YN{tskG?hkwC>yU zs*x>6U*`Yqy22*J`E*~{|0~n?Z~U9;sZbxJ%aruZ|L-sTvp*L@OHs#L_7i_M@aoTK znbtjjQAMp%RQmtwCl5})F08!Oqw;UJ#|_zljI+rLELPmxUs0>Hq14zwdg+tQ%E!Ly z{td#@+4eL(N;vv4tM)?iYF=)}A8cn;*NOZ-GCktg-{tlt^2Pak`ad(oy_na)e4p>T z`|adw-*XQlJ!0e-YeVU^CS9bw)em3|6k9%sUKX! zcv9SGSp$C*_usiYuY28}Z}&@QUew>Jfa>@1xgs#^|^nz$Yedk0GTy6 zPOz@HbLVJ16GL82(Ivb60S^=I{PlKuf45eI!JsBzs-d91_;1Lq%^nUrcYplC+b-(|;U~dH~sEabn*uVT{{*^r8TmM=HZlNnKVt==aOmKYn z?~wM2A4RER%p7(Td7q`oY4APTyRhBc^+t1Xnf1f?vv;2@W}lXro*sV5ig$8qef>L$ zEzkBf{`=?%s;}Fj5zXfL{|1+Vz3ui}S$6JK>UQ;qZ#Vy|`m=Q9v-Q1=j}mVB+z|fm zKXqSIL8jQ@3wHY_tPcAk7WH{X-6rn6?e(pn%a<+72%8=CuHiOo?X|z$Yb#@3+-jIR zH#Vzy{=bu`7HR&EFnegw{6s#&_4KTl%Cp&JBDZf!`Z;^%*@BhIe+)xDo0rJH|GH%T zVk3$F%8UD}^R`Y+;&8sTzqU>>z06T3c6sssqGhMgpF5|wJpY$?iPG*pELV`)5exUr z%(~0?_g}+5iA&%5O_;CQ?VqqYUE_?|d55PwK>-Plr%zo{(`n24m+1XS<-_m&6$#~$ zA4RXd)BDMuJ4xWkxpV6-2^od|kv{mYdujZNJFFK||NLCf3rcX1*3E@Cd@sJUe>py3 zN_~>Iz~L9p)wlkquDS6~PkqufCW#gM*shDjepBOG#{ci<$J^)b_}nVUx^`=o{r?9a zOnDxByrd?hxAFU(gtIpTrk{_8n%Z6VKdOE=L;2sW+xLQ6Y5y3E{!QdI_?@(+@2$FF z`oTvRp8t9pyHnu2ytBkQwf?vNr@U9&TxW9R!p6TUR@>(D+%Y&9T0c8@^R>2lXEtB2 z6J_|Y^LX7emv_HcKFhmR7yA67$yBMPe~?z*C7yrldT#$QalXC#4gZUC@(Q*8Bi1n* zJ8AFxIC+y~XYl50i~ir(7ju7?tlXN$&E2vF$rq2s?EUvGJ4kp<Zka%w;I~}!b;^zH&uKTKXA3+ zD{qz*-pR*(;+e`uCY{%t6I9~1>}R|PY8N|!vPpMZ{n>Xd8EwW1k_opb?O~s?e`4Cg zhAsAcFa7Y0F|Fs_oDi8;&kJrxJJnlmIU~dPkafv236J7KOghYOY|Urf-lpj1f0jLIr1d;3J{b}FV9A+~5ePk22(!}%^&kPt6Bu{m3_}B1!*O$yY z+f*z07q>3n`@4UdxjfX(+<#LqzFB|s?u4fjCKFpFR62A$&l>HhTJd~YapvN?^?#K& z+c3gp74hpTW~Z!>(BY@105P=A2SL*3QsaX^>R6LazV4s5e3I2 z))^}$-mcucAbH)Qlm8!Fl}_!Hd-ihG{<9a(KAcsjcN|=TaD$yZ>u>hX-LGXjiit1Og4A2m$z=F4>96E%6II59v+=s{ChmrzQ^(hF9bvbL>Za&tL$BYV%9 zkB{D-Hu|OKKuAjRLqA~E< zO$|aAf(#4`io^o2<5Ns`N>SuMQ`sWu>pk+hZ-bDto*2!d(|P`YzlR^Gy6%(RK!hqfhelKZ!SF3!c4t zF2g)zdr@YkX^^?4t+bW?;w9(%PGmByYyG+1d?(j|tp@+so&3WMwdz9F)>}KJp&S%y zLFAAKoy|C+RvF~0hHOLQt;Qkd_vUh3-Fka&z}*)&mIcJ>edaFS8O@Nu;aa@a_!jTg zwJ}j~y;pqw&bOUAt?@EFy-2FTf71T!N&6YrM4Ntmwwc1_(k$FAI{oSVVb_&;CIPw|9?GyC`E9{9s+_@C*6 z=e+v3$vedu7#1i!yKg$Po-w26PweDBt#(_B%|RiIjvY3Wn9~MVOt=-9GpNpz*>_Qtj!}!j(%WU*5d%vys=YTZa1$rhl?v;1WH1wRDAQc<|)Q49r&?_B|Ckb#(>r zjQxt<;^$h5s-9jdy1e$w(=R(G?~GzE}Kzbt!=AcZ1|rkX711JI!m8dmi+tsCVlt9`Nyl@ zADt(k-|$mhf$_XOf6w#zj2S*A=a26e2Ai@&_|18AFyg)|^(IvN}-Opx$|!-W<-SUrsH3 zx-9zT>6gXo#oHM|w65Q|(eNT>+UoriE`6@~_+}-zs{3oB7aynjw~lk-~{0N z>`DLk_`BzMe(Cwu7@Pk*2h?5fyO+4_Ubt(j_vx)Q{x`l?F>crJ0KezE+{625zFK$iS^Wx{{ae=Cm3+EqydOG#e`{@Ffylbv+ zTf4pWm7d;weXH#YINSH_G@WwU-}dwAWzmdU%Jqr4<#+6aPu*Xv|7X|Bu!d7EpUd}H z2bTPEvlm~g!*BQD=aT1tpB|K+Jn#FP^Y;w@GxZw$KXmKNeuv;Q`%M(T##BGZ4qmwH z;zeK2_1XVeXFdA)d{f20$^@JLJQEz&eHUE%Je7IUvul42#hiM~(AEads5^gdH~*}^ zA@0+5oloTnPxMa?&jQkFr*y)OGd^#9YM*4iVs3UTiYfW{X62ISzJBM`Z1uOk*uVWQ z-+mF%+Wju|_I~rNZ-yHmIA#oL3dF|P&3|2LwCc-0r=aJRdeimSPx<$m8zeKeN$?|bmu-N9Ju$Gif)rO%ySI{mNr7C)!*w=P$2-*s-2qkp2sKEFTk zKfcV`tFD&OffZc;V-~U0;tcv}*Wj^ncpXKh_4%;uW+%=SOP&`km!b zvM!eWvwpzTOV3YVdhW*dc>ceS-k#@wUp>7XKR ztU}cB;&<;a-yeV3Il0!9dBe1*qQ+f6|LF%v|2%ClVgHjW!?xZ%oO=7(Z6{C%@oHQ2 z%hQi;2Jehsz%cppX3z7xchuN@nf2?M_5Z0~b~*el%dwyE>%^ksT_sER_j}CEmsFh_ z`b_*;kR1Dmjci3$Ui0`DzPB};{Q8n-@z;Nc?R_g{4(vJ^f9>(acJ<;|tIU6P-+o-2 za-1FF5$W{`%gM=do}8D7~x{PLAihdRBif z_tn_AKd~D6;nVCn=6-LLwqCevcHZB@6?_WjpY>lS{$tgc{B!&EOV97?PTu%@z2BV9 zvkR5w|JY5L9&NT?-b(+}AMPj5em>unv*r2xvnLO@iT?ff{^--sMN@gx_gFvtz28Ck z%>LAy8K3jr7RJO%#d67RGZ9~|dI`9I^O$A-MGQqeC@ zKlzf=cUo_c7L@%QWpcAe2b)B36`$6mnJXx8Px$WJ!YyFTeoU-CWv z%Xj7lnk(bm7gg?izV7eB>z8fWzpI~psa&n!@~Us{kIGZ0w|#rN%lu`fxb=RIs(U|9 z*)H5=9(~-o^!>iCWiOup{x@^;!q1D|+5fq`>%VVh-<{7d-IMeB=koX7RdHMP zv+tLcmfrt+ZnSf${$5MDRlo1n&*zB!ao7Al-(2ypHy*oBp2u#Wv#7G}$AiVZ7M=)vccfv<$~GEmN}Q2*M4lz)aRKn^ZESKL8m{T@3*m$T($e}`P~b5O^=W4 zi?e$9H{Fk|NZVH2>;FBS=83=T_U@>$ds)d44yl9YC<^TQKh15rQ@d{O(N|F)f17-L z&u*{;l%VqOzqzk4dsy~yW7Nx;d8Yqt-5GGV?=AZx_r927le?JfAAK$m_^WFD%q*hhGJo_==?(P5I_b=qmpIxh7axZ#$m{V!LZJkMo z`OfO{6?$8bK0dxVMqFaf$9FDUOn&V>rE~xK+Naf}M^|L;Y_5JgHH9KLe zoKJ0AEOqM}wa@I|d)4T_+tp8h?=RiBG$ZrNKCivyYzwYtoxH#G#Ql3-=i`5yXE<4x zGs9XW$h67nK{X6VVq<3AVAhtN*oc$@8bL-k%INZ-3Qi`#b5y^WVR;*SDVQ_NujK=IMU%`u@Y_ezQBC z^+C^5vzC6}{{F&s=TiNDPk!^3GaWFvx&8IMJGwDff1I-QJb!!7Q}d07j5qc_S||N` zLw_E_jTK(=^)G+_?)K<2gTnq7_kDwZKVN_Jl^@IVTeG+Kw7;^u{7<7Sk>N+nrRVqC z-~0dlx#OpJ!!MWT_xGCXv-tO%U3mY7=i($k?VF>{Hd7snknYt z&*uj_zFpsIcRptK)BgPut9~DwYoD=W@;=x7|HXdo3;pu9(Wd>O=-GoWzL!QHygt3u z=jWI>rky82LiWGS zS9lKFzSR7<^gEUYMPs2g(Y+$B z#T&ky&;5RHkHD(xM8#>Vt?jS-n==F~i?RHBBmR&7igSOs4=fKc{O|Vs>Xk+H*Y7qs zefQR``_CA#pr-!+Ue%EAuQ%tL-#K}~>({@0+2@Q?yX)f3vyOjbUbxG){4M`pzPFY; z?0@YN6Pla-QumkLqZ^y`+v@hM-~TBp{%=jxm%4d(_FBIzjs7IQz+&5%! z&);QYV4b+%V9)RzbDn#Lm*M)mli}v+JKuqG_|9L<3nG*c?z-v`Xp)|Fa_yzx%nKqu zZRfxEPwvL-Z@aGd*3S6mz`*AF@0;SS`*Z6r6(}=^JdBt7_xgYO`+LoA^iJC}&d=Lb z{Ca}7y2CWKs7p6Dyky8SQp{j`)FZ7^khtSS1J5Sq*#`^f7KY93;=ZQwcuG`S`^L1e z@KmD)9chm15~9~w8d;24CUiHN2<>L(WjU-mm*qrn$)(@zN2e`ifA(_D_b>0iRNKwp zzyIq#|M(M^&;OkJS>RM`;onbM;X!eMwWZ$-_j;b6U3TO2!aIg*YZu;`Az1Ca^B?oS z`70R?&-uBX>HA)TO)rEPRwzEZf9gzTz1o!9+qA!xx;&3RC4K#-gGx~F&AgV$GFJHH*wh}S(@@6Wg@Dfs=YfOm)N|Kv>7Ua9-_+t2%! z^;;CCM3>&)KIvXG>o2W<^=I~ruK4;ZNcza0SyQf0ZRB|g<|kNu$g4xHG@$H;GuS(g z6L8Gc7~z!+nwi)u358(u4#3@0Z3vo_J2@wY&u+$TGJsy5>Su^0QD zljGdWFRC&yI4@cG(cz*hLqOZl=}Nt?-{?HKcYW6`uASlyCfnY6{r-NixOVNr7`OAC zllQH!6L`)ja_Wz@jjZ*{s{!wxUguw1q&n5`KhvMtHUG9VZHmHjh%ph1g$6t-bu|_H6c}BDwMkqt6}A=t<~)+q$MujB#tE0g^)0QZ?0)p? zY3RBodsfS3YCeero2YSVPv5&m-V6)cbRxG}y{wF$A0>b4ZH~lEfs{K>{AY^%_%ohs+OJZ9zv2x4a(}5ZIPhlc>;gL(omhpVwKX&{ z(z=x|m{#O_hpqLix|jX>dTY^b`Dp1=U-y^CsXk>muwmLR|NPplT8-_S6H}a3uG|0n z(AdSbs+c{lO7Bap-G-N)S<{&gzVz{0=0aCzpT#ecerTt z|LpA>wfnhh=l`zS9=dxq}2# zKOWg{mlj3Lm-{+1zqM$}_h*;Zx7X#h>st8FXY+9QzcfT^s@Lwamb$#&#m8RkXW)FX ze~Q=AOaG3)>->8Eo8&Kj2d1@;`FWrID%&IX>$~OFb4R_MclM^wlRf1+UwD4mr;iKo zoGSHiD_UuL{kqgC+1M(pDb)$pZ+8B@Qh2_-$o~F^)&RX%Q$D}%a9g~8`NI9m$4blE z_^Q{g)~?V!uwK;kzn|sPEf*4N?RTtOl(fRy^LPFfJ6@h?Kez9xnq5ENKF?3%`kwM^ z4e`14>v#TK8Sw5_be-TSMjr8b|M~on`&F4vsZM+SBw*eD#@jakb=Oa*lzO1M^7of( z%dfQ;SxdXM{fm7qUtgj4xl)gB&zG1NziSdM3t!b+8szzV!S(LOEW`gnT1V~wS6?|g zpYQAT7w@kgV*TuF*>DFG2H)}n=1XUg*&Yb(dw=EIWyqhigHjP&G2WM&aaZopwzT{ZcUBG_M*F!7sdqs(*Iqd zas6lW(}j1omD+PYowg=Q_LQq`jDh{jN(R?#+4>BP>)Rif{@vcQ=+}F23(DcX_QIIY ziS6x0`Fk!3oGQ)#bvr;W>(&*0Ra^c9iHr58J?_?D|K`K}^jT?1dl7ryUc)KbzfQO( zKmVH*tnq#E)_VCfvYovqt zvU!0&b7jzJBrf?8WnLT3Jpx{q$#k;;TtA^M zWgp9{89%r8RQ>*wfAVuc!@_0%rv>K!{XR8xdu!3f>!EM1F7W7e6$d3qbnH+%Fe!_Q z?ZHYZ>$DP$@5k2NU3h29w{2dhe#+k6JKK5ZKgndLo!|81Bu?f2`{cVYCi>Cmu1(LS z=W{(>rWa==V6*ArgVl_nLZBot==)3FgVl@cr=E%VnWC}1C|2is{mtXfJHKgeoF9FC zQMc31a~1RZxW0Z?T^-iW7ri5)&h;n*&s4#)XBWnl{Odipb>-FQ|Fz$aOFQrEo2=ea z^n2TWsZ(!rW<`4ZXFbvTbNimC_xr&Kc6P;!eF1r|XWe$%xh(j+_LBeqGTz>A=llI( z&;E5QBpMbPZBxBgzc{Pb@7>)=-(L6b+gE+|i#_*KyI*r77sedFb0g;V-z%>d-cdY$ zmh&mslwI%J|9mfYZ!MaB-#~v#_KdCDCKWaGJTdz3<~o1&w`ocDr@yd~efQh`r;~=+ zIqUmDX?3m7tDoP4%m3DCm~Z;&pmF{F z4T)2>_w8+`c<0`^A=r8+n(N%<>wi{I&M~ZQm{TcG|hTdV5RJ z*KdEy-@Pes<*WWMF`fIT%+>7JSj~RsAHJ^C&;(6<$nspqIK&$^>+&KR~@6$y!>%>=t|H`lYm0#I>V}Ig$HdFm? z?>NxYGKk-%i+$#9ko9A5WV)Tp7I7>1Cm-+Od%8^KTGUgoHn;cFZd{Mv_2%)$J5wUo z*jL2`fP=4k-^Z0pzPs6O`}Hs&FYEWp&~;0;<-CdwF5x_2FnO1M`S-kaOXjTN{{C{o zH_hG7JFWfa1fSSzE3B@%cRLqb)6TY4VT=Yxj%{1=*8TFW+fF+d{VLJ>yZz~|^7f*; zvFqE5j@LhU5wI@5T4UOsYwNt7c3!i!4PCdyhYMu?`*z#OoRTeyxwUWp zE%`m`b#K)>m)$Wid|J3PsP}*Eg7=3%gyw(FU;F+3NBeztQ>qQ8XifE+d1ilo;kkzk z|84uf^x1`_r{!;n@B02`@+{}?b?+}HJAa?%wejET^-Mcge*I?+O10u&YyN*(@_gyH ziN;n=wIAfy$I4rs|HI8A{?+Wi*w;VbUuD)yo|^mV(S4tBX-W>)HKNKmNSG{b^@hL-~ULVQ&9xqkrj}uHX0q z+p0*qDNG!vIX&P_dY)%)>w}cEfwJ%C z37q=c_dj8(_DY}Z$Jz_;=&p|AeR?k5!1BuS{}mVYooc^&&$qBDc|Uc&h1wKv-}!9! zChhWfkGBWc@ZxVTr|U+`p31d&`y=4pA^E@F8r$DHXoP7^-L>f8tZk3mIyWY`zo{&b z@b@>FS@q7(%K!ELfa+Jd|9Z7n{m-l3YdOWb;73P*-l~^ZK2MrA{nl*ejqATx&zF;a z)%;lho|sks<6~9#r_CGcC@3|_T{%>u^Pw^F#s%F<`|NZ$MoIY1x z{q@JXu;$J4X;CHf{@r@J`|Q>O3wmUQU&{ycW%EAG`dj)mV}DQAx2upGe(^k~_=(rs z9Ab_BPn&-F_x#0Ke?RZ{4m%q6Jbp()-BfKx59j~7>+k)_4}7=h-|vNYu6zq#9phO! zyXv|~k-qcdVVgV~lzg)DyIIBc*XAkaD@WaLyWGeBv}$_~-_u{|J)BP$$?f|reDDeP z!YkqY`=%_s6i~v*uvO>Q{aw}O;X5aNT2%XCP3mgromXmqeeSO@p3=SR@zEv6Rm=Z( zRXgom`F43*(d?@0AwhfxHcZ|1zl`nq6L8Vs`t|<*PY2>#ivoLn*S8f_|8iH|YreH` z{db<0CRy1`{dX@r)snv+ZGHBm(f#>RzSElO&rnNfeb``mus`&2KsAXD`z zkG1V<;qzMqFIHOFsk^)W-tzqJfdyB7{h591%vArjBKO`%@l(EcCVrcLw{+&2{r|I1 zZ@XarZ0ovp7h-nnS^SUtxz})E_4+^gmz50}ggVddm$xZN(I`Kr`9H+d@^IhJ@3}MU z8y#1E{keU`^ZmEu^7)?L+y7zn!kCb?=lA5LAUFTn4_UxxV z&)ki=8^8Z{d3q^)_Pkr7r(EyrU$sju*9f2T;`ebziF-vaFT3vjZ~1jzA5&{w`rg0R z=B;t*Uw_6olt25t|EcTm_iNqf2%MT5+hsdF=AL-a9psZor ze_t@oH#?vE>8@&X^}Xg*_fFLRE7UOGJ1Oau#q$&5Lh~g)f0#R;%8`QF3z)NAS8g;z4=tCT9@qHJGVbM4%IMR3pd<{YUSC@N_w%1yD#P204m-Ty{?}d~_4{O&c+k12@phKqm~U=Uy{2FOt|xA-KXd5B zUqAD=_`93F+hacE_s`n@np32W^9og;Ug@!ZXQMjh_svzj8~0tfEd1K|Vx_6p-x%qo z@l1!E0zbd+;(pd%1TKtSpI@Dtt?ylR&p_bS{@GQ#oj$)m(C58R_M~age!G&mihplk zbAP?s=>EL5uRCkTRur_HF>PW#8&)*3VKzuW!w zdp;G<`#)`S^56HZeA55-%?}MZ^IpQ*>}_S?y#GwiL06tvEryg2>$USib-}vw(=P(v z;V1*_{&P=o)BBwNX#>}&VLPR-r${@{}D%gVn0 zwOv?$Tkrp^y9+;?eOTb63Z$Tr%gr;cwf;PCJ9`M1KEHc)C}f=jpru4<9f5_j~#N z|3z=UuV1Vz!u|TCXLVf6rTyMgX={BeWhONIi;lLl`n!B#%;gig3-9#oP_|vwoTI$e!;!-Tmz| z_p+t6eAY=$kQWf4-gGzrDTay3SqcSJP*k^>fVq9xi^QF1C-OZtv2$ z*4@AUYCkD0`76|>|2aR`@c*>%D}Ss%yx5cPUbNk2gZh_Jo}_=h|Gtau3ZLJ0EBT+U z{?|X!bF&xEa?9a=`tR?W_?9C3=t>uj^3So%3+vxk$M@O#oBa=~ShUEw~ac)WHm)HK|(1=+3{r-xWXZNdq|9L;< zo3iuYBJcOzp6{>LH!gbm=}-87S*eS4bw?-d=YIMwzR-Wg|9kr%-MdivORHJ1nhV<& zpaUBQ!vqMZ!wABpPkIEDFkadI+pKK4U6I1q#;;3umlt39e9rPy>yqbtHt9Qr=idETqhY>lqy3cMKlNuj{r$d=eYW%S+FyrmtoMGc811z4 z(z&^=fB%Voib^}YW~*xqw@N_%-|g3)-w#Zl?^~4*OWCbO?!IyyUuTNP`QGkZ^!feI z|Aj#s<)0t@7x-nnH#PJPw`KjoCokUDzObnktvIdltHk&F{VQqa_vd}tBe*z@_bE8# z)$Oyoc0PSyeUZlY`ua8h`!aVMmc(5-u=w+&dEp|pq7~gg%QKeDt6yB4n|%3Qz`Gqc z{acE*+WmKPeg5QJpW(tgQ|DVOn^d&Q?|%Ek(|@Al0zcQGJoRcjCaW(e+S~T){UO%R{QD#-q=#=Y>~d%OM(Zinzoum;?~GnA-SOmH z)HbVLnyqOlgbE z?$%?JNISECs^{J1?!C2UFDuLS|0ZgDukd$X@xLzfXb^9L_2Z@jbQ-TvF=wz8*u zXH>-hq_I|3$LiOpNzE0EeccUm9+({IzIod15`@OWD5IcA047k_?# z_?}#RbN#e`{Gan#>vlbw?Rma>@fD$0`|FE--t~OXs=)g>zjCYH|93a8ioDu?eBbwD z7b+Q+Jp7rTnpOKxxAGsi!IGf;|96I@&;RdxH$1qz&M))x?2XswPtUy(5opW6)Aw_` zk==i82h*FAL*oLgH|;Ap{AkJdynD7*SC-4)68kOl`4DU7_gcr5AzG&NUN_)ubN>_r zgG8UFi(?2VVKC5oh6=K7;M&C8g?El?9cU7jrG0ms(jV$Ii(t5SFZo|dbuIt(*1A-O_$$USDwy~6gl;F@B2+l zzKf;08kC1^_}^FkX+Nl)z3250&V_gWG5$X<(GZpLZ*9Pa|L-Dxoefy``0mDiw?2P< zyYPMry-WXwu48Fv7Mxpu{?7r+ zg)v+HPkWX7Z|~b=&-HghUU58)^2%4)E8eg$_1{`u{#*&kQ?k`p8nwci4jlaOXS-A9 z{r@)q`}%=?eO`6li_&i6^K*r*@*loW z{ZuY|c#{8SY|BM)uBE{-20geI0)f^qC@kF|&!A)m9a~e5w`BdLDX=@y}lK z%lvO=TvIbO?c8>yKZvPO!1GAM+jA3K#bTEnYS>jUyOmWu$ywoG%{$eT2=PG#xkWtn!fW|&Ogna6HvTx<1x&-=>vmG%3-9^QF=ukp$Hb9Z+B zU-5H{A)Vn0*jxwdtdA|dzEE8|EmqdF4g+o zf_wkJyJN_8Hg=E6yfj$`uFwDS_gDNEPuOPr?d$rsXZ!x2wOzQ=_uz5ce@7M{JUOrW zU+aa4mCt|I6~`BRidkLFu)*%#{=bht?`JryQ2$$X*8cUf-L>z2u>1cnnpL_$_}?sS z?ezskT-dkrFK@tpfWV+qUc6MiVUFINowlzlmv6I~_jS+S?&F*0g`Yb4Y~z~ee}AO^ zmvDVn_wRCkT5Sx&F4g6`Ct5E*ZytZ5N{->~r2Tg-{+_?>Tx?x_UiruN z=Mm$B!tz|cx-wjH>vuo=8_y87tKj*z z3wK_Bf5UtB>ZIvQUF?@{*;%>%sV_suu_l8_`JEV$rxK>=+>eoRedbmy?eyGtTAuH+D2|3zZ*JG`_`=qFf7@|=U8S@%C@Vet8_)0} z?*30r^hXver)^Ex|Vr5ce5&m8QAiIsv7l9-rv$a zo$YHb!v*%{U;W|#m@Wu6pSpj??C$LzrQ+lMK2GaDA7ozmcvfz-bMo1p%E%W+Zi2>|2aPU!}|K;&*c6^-TPzvH|Wg<+ug~< z>-R|g{&a7zVVNAmO&+cKz5lr#X4m}F@cjpxsJ@W?_!8_q9B>kc5f}7u9N7|#;~=2H zqSaZEwLzyjJw=!Jz)xruFly%*yT*`yW5IyZW)nX$GE# zKe1OAeENRk;*CP?_N3Q)e{Oqic_8Y^`&FBsz3JL`hwPu{P3lJE3o z-k+y$b@iDH9zS`%s_V)7Yrm6!R{wF)Vdt0A_?-R4dwREx&Ve%%_OF(@@_RqS8JnF3 zvOl>QX8KH{<|mU?%yeEFZg%U(Ym|)(#zj$;g)BU z7h7QTX}fgo`TIfltfH%!8;qXhn-@KQZ~Z>aR`1Oh?~Rt<;~Tb~yq|aF)4%*LwRc;c zBme8jBy6bpml0}L&*D&6R28~#4k&9@^D}6v)Q79qC%W|x*J@i3BW}$1RebKfaOY;4 z_vzU_>BXPpUWzh!+^G9{M#W-h%q7_wmV0$?m>0c%R_|O~B--$G!v4%p z;=j^|WurN~it$9|@2kd>AH>{VU84G>Mn3D;-r>j)9r66UTAY`m*uy` z&+i>gUbyp)-HznwiN|XfR4RY@WqV=I#cx`{6Q46=PhOo_|LgSosCz1F-~asmJUzKM zT5jTghU|&^Ge4DEz1z=_J$Zko^XdED>YL=Q$Lx)V#22q_eR1)B)`;1Ee5IpcO=n)+ z`o;I`q-OsOk$em>(a(=BuM)!cq|;m)yVdtz7@n0_+%{pa+5*tUf& zV|Q+|;X3Dg>z-=r>t}oZKUr5VQ@u}I?|JRMo%4^^p8Xwpo;e}+{hat!j|z*QUtA{r zeA7Jf&{xmJEBPk0$M|YbT^+df$@@o=Ke>4(gYrq{yZsE6pj72mM+#7mR;_Otr#+cQ5=Tz=W?o{jvh$*URG zw1W&Z*gagUn}&|~JvDkJ-|y<@W>!wFi`*4j)tqO$Gdns%@7G_Z1M0J;#3{7*Pgh%f z;?CRRg&Pg*_b)kFoN(Yt7CR^$S=oI4l{tV~{Rs!2BCCg| zwmx0?ZKi3F=z`);=8hm^yribb!5UG>bVJ!t=p@X+(PKF~HIAX}$=uK03wN%ao&4YA zxxMi`muGtMv3zsIr~a-q@fEI;^S#`&h@B~5zyCz{A~nZ(%o`@fylwl#nxOjgHt$dF z174rDOMik5Y%pk_yq|X>o*`_~{<{jGcr)0sQD9OOYM|Fpoct%4aG z@_U<#Cx>lkXs|jF%J5>|A68H|2#xV?(q)DV@kLS$yVUC&XBz13MNUp|_S>K#&>*+O c(L4ApUvp-sfZ1`Uzaam3y85}Sb4q9e03uvPaR2}S literal 0 HcmV?d00001 diff --git a/website/assets/game/CowBoyShoot.png b/website/assets/game/CowBoyShoot.png new file mode 100644 index 0000000000000000000000000000000000000000..53e79247a8985b4020b0d6161a3c426412e1c631 GIT binary patch literal 2353 zcmeAS@N?(olHy`uVBq!ia0y~yVEDkmz+k|^#=yX!zxTTu0|NtNage(c!@6@aFBup( zUVFMYhE&XXJ2yK=|$HyOB`_cU6yx2n4H`|yD zen?$p2>M|x@+$OD^5JEvceh8ou6o*X`;mP2m-F+QYNVEHHHc(5FN{;&wziiw;Y;tk z3-7*^Gkk2VS-!tH^D@_@ZJUL*ACLd#KWTM+9P1ix$$Pt3F?8^k#eDc$C?P4&ub%b1 z#c+r2KZU=(Z#SvlXDTaYRN>0o_UGH)EQYN3O;33aUH$0(OZxWHC-cJ@yEPbUrOoC# zX-hQC%eW_GtyVkNHh((fhOD1Hx7PLEDq<<9{wT-u`j_@Q=gtu@WIwlyVbYfEGJm}imhPIi^nTQ0o!XjSwhg(d z9mc-L8CR_QXU32|t8ep~sD)R8&rUta*tRQAOV&I;c;AMGqw&8d`zkZU$o!x2Z)rW> zlh6YzxgNaSmCW$x?}5z>%g(=_GymvLe?N`f`DQb>C5wkQFTeTY$F()LDxT@4gns>} zX>^JQ6fV!->*u`R`j@}gV59i0ZAMdy?v|fB&lDh4_a!#)?sMN||61os$4JUB1cZgY z-*iM*SZ>OX<}c<-k3_6gg*Q2*Bpf@Rci_eE&H39t^d|^r-DH?|NKksM(et{l{a^ai zXI)t9D|4~*WJIJmi~Z97Qx7_Z>FnzJ6~I65P~hvgmx|+$$NxI6yrv=9pFKt(NBHHR za6K-eH=2vpO{5zl`LrMWw*BXKT(AD3`-9(dUl+>l7FC&~QF*@c`+N&l;mO}mf1Rz7 zA-CwkU;ay`n}1)bpOW(@TxZw1{iRFyGrrX4*!|a4rg8E9|Eq3Q8$5QK%I(csqTAB* z_v_{KhRS{SU8LeyIPU%=a;sJ9><6d%NC&@<*ZqHP;Wn+=@_coMc-EF%>Zfk(e0qAv z$@t|_ubN)6pQ_a3^QZL7 zkJtZR-{0l@c5<9=WZ|=|hyTB+oI3MpLUaATpT?K2x>|@B-IYugW%_LYTDkb%@(ahF zT;wbhJFKmLn2BTmr=lf`zuPDLxO@HcTR)3k^$uSzO6_4h^80^kovC*Hf7^RiFV_pN zy_KjGc<*;Xi~06n>gF%LJ4es{mw76G)A=j)kJqjJQ+a$ngMIJpa+kmVFGqE4dh{jx z*SETl-^@*{tVNv^z5n*6OtPQ)Dk^yPzJCg}u?NG~+A%$Ub$>%;dnJ#e#7w(G2hKFE zNwQNF-*~9MqTz(Y71r`V@jJcmdZntBPu|;5*&eoXU2nB-tG`*uTic9J_3z@Ve{T5L zzGuUi;;3`>QoDD3x-2iQ+17OQ%e}UpQEUEB+Hbn4_P^e?wLFFIZcMvzb?Ux<30DfA z`~UgPC%>%&DY?q-xYW#TQ7LAx@6rdMxL$PZ`;|QQ~BTdk$uae zYS&rmS0+#0e?i)kX%#=u=Z1z?k1qv#bC!v@vx|BzDwBFN`?g$#Y^~nrBe$6jeA-#t z;{H4+;Kq4z@%@L=-#)GRzcZNYz-)bZiWdZuy@0_Cx2p+O64XSzo?tp6W8b=HTa2hhdssrH@sS0^=8e>-^#M0Idw&ImH*zI zVx3U`ue(a>qzT94Jr43d4qp$>sALFlUM@S)W#fx~kAF0D-E*`=<{#{RT#?N9wzB33W2+SeMaJt(~;NboLqX|IUo zH}(k~2Olvl`(BgS=yxsU$$gV#Q+M+ka z{}t+~hBmQ;$WMQ5zH#@udCIb)G2E7~!Yo@^E_W`LzIM@aPtUoBsSGjMueRUP4xaRD zcT3AZH>OqncT2y%S+{z-ho%v|CZClhz>~|&3 z{W9O_($D72pV{A>tv~&))r>Evw$ggvqupVpa|PRMGoAib8+~&4wsl#Zn1A&RuBB@{ zp36(uFMn9}zr-i>AGh{{S?Tv4y!j)3d0qLe;|wz42VVZ+f1Am+p!yfn>M5I)ZvU|E zWj}krkabOQ*}ra2_C=@dW6OOstG}=>+AZd!Rlm35f9=m5!FmUdPR#tXw&YYB`_cH2 zf85+IXN~h6nzRca9^8Fu?}fWJo}OAh|8HQ(btjMK|4+YN7NB{5rES90D7QQBKlVrJ z{8gWDo>`c8pUWew6h{9^pUPKSOXuwOjaKS(7Ap`tp)}2cH}XK{g=5DbPm9f()~Zv; zwyA;fenRo4^&+}8=eH!JHy>NNWVN@-HKjETlSL06+W5Ng^qxQdA={l+aF}MV&aXZ5 zDq4Q_wBKyABTdBq&-c6g>rUjq2k$^Nt!UzT=J1wTTe|`dIc7|qF}>Unq)kFXDx9tD z*w&)_#+4oK&wL1S+P*C5pQ6QC`8%@p6aMW~TGQ}V>4N?AYUSB-aZ)i(9v;_cJ7w=CCS2N_vgWpl-tu3~yC3skvJY>W>-5@H!268IrIklwr%Lf=bE(Kr zzg$rHXK8((&rMgIzZqt`D`tu9=8^talwej;(ypl!@JQzFBmQ@N_8$LzCWbFmw)l20 fs#8VuKl_0rZM!DA)SqQwU|{fc^>bP0l+XkKMbfc{ literal 0 HcmV?d00001 diff --git a/website/assets/game/CowBoySmokingIdle.png b/website/assets/game/CowBoySmokingIdle.png new file mode 100644 index 0000000000000000000000000000000000000000..c6d0196ad26651a8684f43532961e5524062af19 GIT binary patch literal 1992 zcmeAS@N?(olHy`uVBq!ia0y~yVBEmKz+k|^#=yW((5dgnz`(#*9OUlAup{sKO(+L=)$6=wSERnn0D-4PbJIr^XL7N>&mK* z{z{8nc>bzX^ncUU*HaZLc+WKc=hTqibM)W6t3MNeywAGR*!3q=@N)d!t*^O$_s_DM za^TzcLt3+Ud)RSz8#6Ra|9|${o=pqtr?noH6s%XMmrr|kEcjt9S9sCq&6l;W&7LVO zzS?W@yZv{{^&B)fZ~XA?i)~)~?{VsH=5HI1zdF#mIWy#FeAm)5N(>2kb*rAu?fAFe zYQ~Y3oYObwcd8`3&S%OvEUg>&i~Zxp`&;ZE_`hniX74gEHh&kiRww7L z^u3IOL1~8JELWwr)o=Sdd-g5^L(4~CDaHl4f72K~Z2H@LKXB##%?CnX?)23aSDpMq zUPJE4I>srV^rkh;-#deW;bQHEkL*Rq9oF!x?)}Sn({N5n{cVXSCCB&wE;L|fSiVg+ z;rT&nhKBh1TdWL~@6X@OK5)~yR*R{A0@t6=y9ZMq@^idzc4bUVd%y43Y6|%T^gWUb2a|uadalFBc~NNB2)?$pWrF>`v!j zS(L9={BH1G`NQ3KUDZEVx9whd=3BU7-C~W+f3M25XZ6P~=hX2CDEawNzx>yYf1ElBAr;a>H~y{iNcbIHZEfG5d#>3{kY`@vu}j5T=T=^h zpXT(>@%!EB>mR&XxG!Ww!GitLFZ8+pK7N?9T>q}zIfes={eN$G^;^DcoiyJPDXIDY zHc9;NRM+QPx7d>L^89~hKi=i9C}n(`vfTHvbeAj(!;7r!L(k=N!!JHy_`9_8^?F^X zD*h9q7yQ;WmX?<-*u7c#`@Y9t8uvU`=w^;*im1GxvT(mn;{PC-{gy3{&D#y8&GXKb z*z;w2>%UDb$tRD+vw5$RFWLTeyI;b7dztemXEz)CEaAO$ypMt5((%40akC$+fOy^ zFO%It23h&ZpV*l#vnTLEyx4rM9U3*Crt2-3tG40e$>Tqs-#*Wf!hUew#@!5?tiP;S zdGqPARZM?VHJ`07{Lr>=zs|Ay*O|_h*E{R}U0c_wJfoKT;_ut{U&PkgZmHjW_Sqcg z`p6gh-nPG&S6@pyzctb3$B(^ldJ-&GWF0F1Z}5<5h1kDyX$g*%&D;3md48RCs*iki zQT~M5gV^Nqjn8jwXbAoJ<3)TPV~B5kI;YOLSQDv*`*prdUn=*v*6YB&-`=7Qp}#d( z9(*qCwQljtuy+RF0Q>PSp6^R}dkEu`C)Zz?>-I7+^s!mh-oM??kg~a;;@6JtwOkAz zK7X;+Gt1}wo4Rnn&euowA`xakYMk@r4_(;*OXRb8%r4tG8~-}8u9m#f*Lzp<@(+fB z@5jVN_&d%_+ zB1wvF8x~)iUBU&nWY*uuY6-bNo~>WVu*oms?B2gSH6P3iu3xY=-TPqa;koudA~iv& zE_J2({~(s{jsLbDFJ*l4L~2In34XhuTJIM<%6M&eXJ@IQ>8AfaY+cgx{%yJtKdtIh z{_C4f>i_?V?wrRt?@PIRaen=)o6V~wAA~Z7oU;EBdHZenQ<0>NTIblohJU5v5e>nTkxbooVzial+1~JWO{KzfR(c znAz}uPkrdwo~06B`u(p8&3GRw1NPIt-;+}gNJ=O!dH2DV@$!C~83#BNOC5ixi8OD^ zc57WwUjr8C-l->iWO}w2!(Z=*Z@Ty0yA@bA&9?sk#$C(~pv1xbH+5lss>STax%>ZH znA%Qmt8~%5&I2|d6rEEK26wawIPE#|GUUsF-D@i^pZGTI4Sy}m=Jk=w3Z;DDCe&Ck gE4K(dQvJ_7#r)fZ)TZcE1_lNOPgg&ebxsLQ0K)U|rT_o{ literal 0 HcmV?d00001 diff --git a/website/assets/game/CowBoyWalking.png b/website/assets/game/CowBoyWalking.png new file mode 100644 index 0000000000000000000000000000000000000000..b9dec3e6929f2280dbd015cecb1f750748f8a3bf GIT binary patch literal 2239 zcmeAS@N?(olHy`uVBq!ia0y~yU~FJuU@+idV_;yAs9Iacz`(#*9OUlAu3NZkcv5P=Q?JHgi9R%AIy~08kL-2+Rb9ZG0BTl%!cD)uqeM{v0;ejlSy)2wnr{G zvsS-enBevC)k=*Qu1%~PzfNLd;?-Ihq&-E0#rKtyRLF*~m>F`{w%2|(%1_(;Iqlwe z-+l5rX{D3??En9J|M$J-v)42za5UjSdpK*j1zIe2nSF12zxTfE&)x3amnVfM#s-A^ zE1n%-)cE(%&Hol}xz3puT#RG0_#L0}?|$LWqaxdw%D(ZmRMyxla;g8{ZT7t_c6PP% z-^xp0t9YM14{DtJKec7@|9zj|?@`ur+{VZD;l+`da)CL2%n$GWQ)6fF|J<6#M>j9M z@p{_)^OIL~HvjkZ`1iTp>7TLmLhj7%3|f=J555#*N&g@F?w;nA183`{C;d-aFyX&X zYp})v(>dh=ksluJuT-i3n3H)n-nn;jzv07o^)i$GKUy&1e_xs1$v@A!bXaxl=bu+v znj6w_hG9k2q^vEqd4UDr|6e>*vcSJ-hwJe*n(w6IxrNIAGoJikb-w;tqjz;;>nE0H z`;Fzu20h^j{`{F` z0x^I5AN{%OyH9lsulRq}|0SAA|98%3_@Z>~P4&sNpy`SZ4F3NuGjc@x{xMIgpD(iV z;>&2q6|s$42XtDRf2uuypO(zM==s0kiax+ND@+>gr*fnkL?d?nA z?w>MsJ#_Q`N&DxstwMOY{mqjezI0=7s4ZdndQmXsJn!R&)7I9xEIV+s-r>`G;rl<& z-(kpMU6I_Q#qi^PP1X&jDJ)+Z3P0UnvhR5;L$6iUw!Kd0w}sCWy{Xy!e^Pyv$jtvS zaV&k>3#JDARM2%$lW{t7vVQK*R@Xltuc|Xhnab=fmv&gpw`%u)3yYunKbJT6|FQn- zYOuNC;OgKV|39+#I_#Ad;k)vEPj?^t{{?ndLNE7S5m(In_qw~kcVB#+`~BPf>*rg{ z*t{sG;Pd^qC;KHdp4eaJTyT}C@80g-gR6rh_l4@s__gz`!_MF97gw)euFTO)4_>T~ban>pC6Z zJ^w+)_sNI8Z1tKcB~fni%wEsF^7(yF<`0n$VtjAk@iw&81^WjztauS#@U8y+?3>R% zZ=Swyb6oc7W~S84glt!_3pq1>?yr5k`PuPjzb1$OGd5-5Vof<3(-6fu;n4PfMnCc= zZ7ZI4Y+=)sCBfU+mK|Q$IMwif=8kjq5A)gFGnv#cdNo)$o&CSIR(sarw-apH|E2wsK4D zv1)y-&S%E#5@;>(-kxF4y0tFr7O#@;xVFGQZ7*Biua&?5fB$=0@j`?{Xc)%KDCr;*FDmS54N~YxujRMX1~;{ zF?AUZ`yeCl)#xbhrsQ*K4%u_>s}8&eqQ`y@~@|E zX;~Mu)4HZk@tQRK>|M2wQa-zOTV2RaseAc$e&O~YjSF{Nq<0;EF2-!SJoH=awFghQ zU&@BFYWXeMrT21{(glB=?286_77M)+Zi|}n=on*%-mEJo+E-@W__B4DVOGG&J-tT1 z@~YN7SP`Ai@N56hRY!k@b$#OVz1UY-S@zH-z-r|^GsA`N(jz@T*}eO6YgxMbskKbMVPGwLljl)m$od-YHLEP)b-DTgn6GcYhPc)I$ztaD0e0sv#T BYZ?Fm literal 0 HcmV?d00001 diff --git a/website/assets/style.css b/website/assets/style.css index 94032280..1587c9f2 100644 --- a/website/assets/style.css +++ b/website/assets/style.css @@ -1683,3 +1683,6 @@ code.doc { .language-bash{ cursor: pointer; } +#avatar{ + margin: 100px; +} diff --git a/website/default.nix b/website/default.nix index 9d198b71..ce293b97 100644 --- a/website/default.nix +++ b/website/default.nix @@ -29,6 +29,9 @@ let brand = import ./brand.nix { inherit chan; }; opti = if optimize then util.doCannibalize else (x: x); file = if optimize then "all.min.js" else "all.js"; + assets = util.gitignore [ + "landing_logo.svg" + ] ./assets; setTarget = x: y: with pkgs.haskell.lib; dontHaddock (overrideCabal x (_: { buildTarget = y; installPhase = '' runHook preInstall ./Setup copy ${y} @@ -42,7 +45,7 @@ in mkdir $out ${brand.icons.mkIcons brand.logo.thumbnail "$out"} mkdir $out/assets - for asset in ${./assets}/* + for asset in ${assets}/* do ln -s $asset $out/assets/$(basename $asset) done -- GitLab From 94d3212ee7c97112195a7148c7645df9191a9dcb Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Fri, 30 Apr 2021 15:05:37 -0600 Subject: [PATCH 098/116] cowboy 404, plus snabbdom bug fix --- backends/snabbdom/README.md | 4 +- .../snabbdom/Shpadoinkle/Backend/Snabbdom.hs | 5 +- .../Shpadoinkle/Backend/Snabbdom/Setup.js | 14 +-- .../Shpadoinkle/Backend/Snabbdom/Setup_src.js | 10 +- core/Shpadoinkle/Core.hs | 9 +- .../Shpadoinkle/Website/Page/FourOhFour.hs | 109 +++++++++++++----- website/Shpadoinkle/Website/Run.hs | 3 +- website/assets/game/CowBoyDrawWeapon.png | Bin 1992 -> 0 bytes website/assets/game/CowBoyHolsterWeapon.png | Bin 2238 -> 0 bytes website/assets/game/CowBoyQuickDrawShot.png | Bin 3650 -> 0 bytes website/assets/game/CowBoyRapidFire.png | Bin 22764 -> 0 bytes website/assets/game/CowBoySmokingIdle.png | Bin 1992 -> 0 bytes website/assets/style.css | 9 +- 13 files changed, 111 insertions(+), 52 deletions(-) delete mode 100644 website/assets/game/CowBoyDrawWeapon.png delete mode 100644 website/assets/game/CowBoyHolsterWeapon.png delete mode 100644 website/assets/game/CowBoyQuickDrawShot.png delete mode 100644 website/assets/game/CowBoyRapidFire.png delete mode 100644 website/assets/game/CowBoySmokingIdle.png diff --git a/backends/snabbdom/README.md b/backends/snabbdom/README.md index cf9a5c1c..2b426911 100644 --- a/backends/snabbdom/README.md +++ b/backends/snabbdom/README.md @@ -20,13 +20,13 @@ In order to re-build `Shpadoinkle/Backend/Snabbdom/Setup.js`, it needs to be bun ```bash # clone outside the source directory of Shpadoinkl -git clone git@gituhub.com:snabbdom/snabbdom.git +git clone git@github.com:snabbdom/snabbdom.git # compile its typescript so we can bundle the javascript cd snabbdom && npm install && npm run compile # Setup_src.js imports the snabbdom sources locally, so we need to manually move it cp path-to-shpadoinkle/backends/snabbdom/Shpadoinkle/Backend/Snabbdom/Setup_src.js build/package/ # nix is most reliable for running parcel -nix-shell -p nodePackages.parcel-bundler --command "parcel build Setup_src.js" +nix-shell -p nodePackages.parcel-bundler --command "parcel build build/package/Setup_src.js" # copy the bundled output as Setup.js cp dist/Setup_src.js path-to-shpadoinkle/backends/snabbdom/Shpadoinkle/Backend/Snabbdom/Setup.js ``` diff --git a/backends/snabbdom/Shpadoinkle/Backend/Snabbdom.hs b/backends/snabbdom/Shpadoinkle/Backend/Snabbdom.hs index e82e2745..a6d258bd 100644 --- a/backends/snabbdom/Shpadoinkle/Backend/Snabbdom.hs +++ b/backends/snabbdom/Shpadoinkle/Backend/Snabbdom.hs @@ -163,9 +163,8 @@ props toJSM i (Props xs) = do [vnode] -> g vnode [_, vnode] -> g vnode _ -> return () - unsafeSetProp "insert" f' hooksObj - unsafeSetProp "update" f' hooksObj - + void $ jsg3 "insertHook" "insert" f' hooksObj + void $ jsg3 "insertHook" "update" f' hooksObj PText t | k == "className" -> forM_ (words t) $ \u -> unsafeSetProp (toJSString u) jsTrue classesObj diff --git a/backends/snabbdom/Shpadoinkle/Backend/Snabbdom/Setup.js b/backends/snabbdom/Shpadoinkle/Backend/Snabbdom/Setup.js index ce44c046..b4ec73c7 100644 --- a/backends/snabbdom/Shpadoinkle/Backend/Snabbdom/Setup.js +++ b/backends/snabbdom/Shpadoinkle/Backend/Snabbdom/Setup.js @@ -2,7 +2,9 @@ parcelRequire=function(e,r,t,n){var i,o="function"==typeof parcelRequire&&parcel "use strict";function e(e,t,o,r,d){return{sel:e,data:t,children:o,text:r,elm:d,key:void 0===t?void 0:t.key}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.vnode=e; },{}],"BfzD":[function(require,module,exports) { "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.primitive=e,exports.array=void 0;var r=Array.isArray;function e(r){return"string"==typeof r||"number"==typeof r}exports.array=r; -},{}],"B5vY":[function(require,module,exports) { +},{}],"VeuO":[function(require,module,exports) { +"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.h=o;var e=require("./vnode.js"),r=i(require("./is.js"));function t(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return t=function(){return e},e}function i(e){if(e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var r=t();if(r&&r.has(e))return r.get(e);var i={},n=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if(Object.prototype.hasOwnProperty.call(e,o)){var u=n?Object.getOwnPropertyDescriptor(e,o):null;u&&(u.get||u.set)?Object.defineProperty(i,o,u):i[o]=e[o]}return i.default=e,r&&r.set(e,i),i}function n(e,r,t){if(e.ns="http://www.w3.org/2000/svg","foreignObject"!==t&&void 0!==r)for(var i=0;i0?c:s.length,y=m>0?m:s.length,k=-1!==c||-1!==m?s.slice(0,Math.min(g,y)):s,b=e.elm=i(v)&&i(d=v.ns)?h.createElementNS(d,k,v):h.createElement(k,v);for(g0&&b.setAttribute("class",s.slice(y+1).replace(/\./g," ")),d=0;ds?y(e,null==r[m+1]?null:r[m+1].elm,r,f,m,n):b(e,t,v,s))}(c,m,g,r):i(g)?(i(e.text)&&h.setTextContent(c,""),y(c,null,g,0,g.length-1,r)):i(m)?b(c,m,0,m.length-1):i(e.text)&&h.setTextContent(c,""):e.text!==t.text&&(i(m)&&b(c,m,0,m.length-1),h.setTextContent(c,t.text)),null===(f=null==s?void 0:s.postpatch)||void 0===f||f.call(s,e,t)}}return function(t,r){var n,l,o,i=[];for(n=0;n { ]); window.patchh = (a,b) => patch(a,b); window.vnode = h; - window.potato = (n, e) => n.elm.appendChild(e); + window.potato = (n,e) => n.elm.appendChild(e); + window.insertHook = (k,f,o) => { + const p = o[k] + o[k] = (vnode) => { + if(p){ p(vnode); } + f(vnode); + } + }; + cb(); }; diff --git a/core/Shpadoinkle/Core.hs b/core/Shpadoinkle/Core.hs index 502cba23..57536ab3 100644 --- a/core/Shpadoinkle/Core.hs +++ b/core/Shpadoinkle/Core.hs @@ -23,6 +23,7 @@ -} +{-# OPTIONS_GHC -Wno-unused-imports #-} module Shpadoinkle.Core ( -- * Base Types Html(..), Prop(..), Props(..), fromProps, toProps @@ -51,6 +52,7 @@ module Shpadoinkle.Core ( ) where +import Control.Applicative (liftA2) import qualified Control.Categorical.Functor as F import Control.Category ((.)) import Control.PseudoInverseCategory (EndoIso (..), @@ -59,8 +61,9 @@ import Control.PseudoInverseCategory (EndoIso (..), PseudoInverseCategory (piinverse), ToHask (piapply)) import Data.Kind (Type) -import Data.Map as M (Map, singleton, toList, - unionWithKey) +import Data.Map as M (Map, foldl', insert, + mapEither, singleton, + toList, unionWithKey) import Data.String (IsString (..)) import Data.Text (Text, pack) import GHCJS.DOM.Types (JSM, MonadJSM, liftJSM) @@ -148,7 +151,7 @@ fromProps = M.toList . getProps instance Applicative m => Semigroup (Props m a) where - Props xs <> Props ys = Props (unionWithKey go xs ys) + Props xs <> Props ys = Props $ unionWithKey go xs ys where go k old new = case (old, new) of (PText t, PText t') | k == "className" -> PText (t <> " " <> t') diff --git a/website/Shpadoinkle/Website/Page/FourOhFour.hs b/website/Shpadoinkle/Website/Page/FourOhFour.hs index 808ec2e4..cd79fd61 100644 --- a/website/Shpadoinkle/Website/Page/FourOhFour.hs +++ b/website/Shpadoinkle/Website/Page/FourOhFour.hs @@ -5,7 +5,9 @@ {-# LANGUAGE ExtendedDefaultRules #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE KindSignatures #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TupleSections #-} @@ -36,6 +38,9 @@ import Shpadoinkle (JSM, NFData, import Shpadoinkle.Backend.Snabbdom (runSnabbdom) import Shpadoinkle.Html as H import Shpadoinkle.Html.TH.AssetLink (assetLink) +import Shpadoinkle.Keyboard (pattern Ctrl, + pattern LeftArrow, + pattern RightArrow) import UnliftIO.Concurrent (forkIO, threadDelay) @@ -46,63 +51,103 @@ data Direction = FaceLeft | FaceRight deriving (Eq, Ord, Show, Generic, NFData) -data Axis = X | Y - - -newtype Position (a :: Axis) = Position { unPosition :: Float } - deriving stock (Eq, Ord, Show, Generic) - deriving newtype (Num) deriving anyclass (NFData) +data State = Idle | Walking | Shooting Clock + deriving (Eq, Ord, Show, Generic, NFData) -newtype Velocity (a :: Axis) = Velocity { unVelocity :: Float } - deriving stock (Eq, Ord, Show, Generic) - deriving newtype (Num) deriving anyclass (NFData) +newtype Position = Position { unPosition :: Float } + deriving stock Generic + deriving newtype (Eq, Ord, Num, Show) deriving anyclass (NFData) newtype Clock = Clock { unClock :: Double } - deriving stock (Eq, Ord, Show, Generic) - deriving newtype (Num) deriving anyclass (NFData) + deriving stock Generic + deriving newtype (Eq, Ord, Num, Show) deriving anyclass (NFData) data Game = Game - { x :: Position 'X - , y :: Position 'Y - , vx :: Velocity 'X - , vy :: Velocity 'Y + { position :: Position , clock :: Clock + , state :: State , direction :: Direction } deriving (Eq, Ord, Show, Generic, NFData) - -spriteWidth, spriteHeight, spriteCount :: Int -spriteWidth = 384 -spriteHeight = 48 -spriteCount = 8 +spriteDim :: Int +spriteDim = 48 game :: Game -> Html m Game -game g = H.div' [ id' "avatar", styleProp styles ] +game g = H.div [id' "game"] . pure $ H.div' + [ id' "avatar" + , styleProp styles + , onGlobalKeyDown $ \case + LeftArrow -> \g' -> g' { state = Walking, direction = FaceLeft } + RightArrow -> \g' -> g' { state = Walking, direction = FaceRight } + Ctrl -> \g' -> g' { state = Shooting (clock g') } + _ -> id + , onGlobalKeyUp $ \case + LeftArrow -> \g' -> g' { state = Idle } + RightArrow -> \g' -> g' { state = Idle } + _ -> id + ] where styles = - [ ("height", px spriteHeight) - , ("width", px innerWidth) - , ("background-image", "url(" <> (if vx g > 0 then walking else idle) <> ")") - , ("background-position", px (innerWidth * (spriteCount - spriteTime)) <> ", 0") + [ ("height", px spriteDim) + , ("width", px spriteDim) + , ("background-image", "url(" <> spriteImage <> ")") + , ("background-position", px (spriteDim * (spriteCount - spriteTime)) <> ", 0") + , ("position", "absolute") + , ("bottom", "0") + , ("transform", + "translate3d(" <> px (position g) <> ",0,0) " + <> "scaleX(" <> (case direction g of FaceLeft -> "-1"; FaceRight -> "1") <> ")" + ) ] - innerWidth = spriteWidth `Prelude.div` spriteCount - spriteTime = floor (unClock (clock g) / (1000 / 8)) `mod` spriteCount - walking = $(assetLink "/assets/game/CowBoyWalking.png") - idle = $(assetLink "/assets/game/CowBoyIdle.png") + spriteImage = toSpriteImage $ state g + spriteCount = toSpriteCount $ state g + spriteTime = toSpriteTime (clock g) (state g) + + +fps :: Num n => n +fps = 12 + + +toSpriteTime :: Clock -> State -> Int +toSpriteTime c gs = floor (unClock since / (1000 / fps)) `mod` toSpriteCount gs + where since = case gs of Shooting c' -> c - c'; _ -> c + + +toSpriteImage :: State -> Text +toSpriteImage = \case + Shooting _ -> $(assetLink "/assets/game/CowBoyShoot.png") + Walking -> $(assetLink "/assets/game/CowBoyWalking.png") + Idle -> $(assetLink "/assets/game/CowBoyIdle.png") + + +toSpriteCount :: State -> Int +toSpriteCount = \case + Shooting _ -> 5 + Walking -> 8 + Idle -> 8 tick :: Clock -> Game -> Game -tick d g = g { clock = d } +tick d g = g + { clock = d + , position = if state g /= Walking then position g else + let moveBy = Position . realToFrac $ 100 * (unClock (d - clock g) / 1000) in case direction g of + FaceLeft -> position g - moveBy + FaceRight -> position g + moveBy + , state = case state g of + Shooting _ | toSpriteTime d (state g) == toSpriteCount (state g) - 1 -> Idle + _ -> state g + } animate :: Window -> TVar Game -> JSM RequestAnimationFrameCallback animate win model = newRequestAnimationFrameCallback $ \d -> () <$ do - liftIO . atomically $ modifyTVar model $ tick $ Clock d + liftIO . atomically . modifyTVar model . tick $ Clock d requestAnimationFrame win =<< animate win model @@ -112,7 +157,7 @@ play = do doc <- currentDocumentUnchecked elm <- createElement doc "div" setId elm "game" - model <- newTVarIO $ Game 0 0 0 0 0 FaceRight + model <- newTVarIO $ Game 24 0 Idle FaceRight _ <- requestAnimationFrame win =<< animate win model raw <- RawNode <$> toJSVal elm _ <- forkIO $ threadDelay 1 diff --git a/website/Shpadoinkle/Website/Run.hs b/website/Shpadoinkle/Website/Run.hs index 8c6a347f..3c077e9a 100644 --- a/website/Shpadoinkle/Website/Run.hs +++ b/website/Shpadoinkle/Website/Run.hs @@ -57,7 +57,7 @@ import Shpadoinkle.Run (Env (Dev), import Shpadoinkle.Run (runJSorWarp) #endif -import Shpadoinkle.DeveloperTools +-- import Shpadoinkle.DeveloperTools import Shpadoinkle.Website.Types.CurrentYear (getCurrentYear) import Shpadoinkle.Website.Types.Effects.Hooglable (Hooglable (..)) import Shpadoinkle.Website.Types.Effects.Swan (Swan (..)) @@ -116,7 +116,6 @@ app :: JSM () app = do mutex <- Examples <$> newTVarIO Nothing <*> newTVarIO Nothing <*> newTVarIO Nothing model <- newTVarIO =<< runReaderT (runApp (start RFourOhFour)) mutex - withDeveloperTools model yc <- getCurrentYear fullPageSPA' @(SPA JSM) (flip runReaderT mutex . runApp) diff --git a/website/assets/game/CowBoyDrawWeapon.png b/website/assets/game/CowBoyDrawWeapon.png deleted file mode 100644 index bc605a64d8c30db78bb9a6ae08a9cb792cac4f42..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1992 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 diff --git a/website/assets/game/CowBoyQuickDrawShot.png b/website/assets/game/CowBoyQuickDrawShot.png deleted file mode 100644 index 90a6b0cdf9bf5f6ae46e606dc2bf5236ee46912f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3650 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 diff --git a/website/assets/game/CowBoySmokingIdle.png b/website/assets/game/CowBoySmokingIdle.png deleted file mode 100644 index c6d0196ad26651a8684f43532961e5524062af19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1992 zcmeAS@N?(olHy`uVBq!ia0y~yVBEmKz+k|^#=yW((5dgnz`(#*9OUlAup{sKO(+L=)$6=wSERnn0D-4PbJIr^XL7N>&mK* z{z{8nc>bzX^ncUU*HaZLc+WKc=hTqibM)W6t3MNeywAGR*!3q=@N)d!t*^O$_s_DM za^TzcLt3+Ud)RSz8#6Ra|9|${o=pqtr?noH6s%XMmrr|kEcjt9S9sCq&6l;W&7LVO zzS?W@yZv{{^&B)fZ~XA?i)~)~?{VsH=5HI1zdF#mIWy#FeAm)5N(>2kb*rAu?fAFe zYQ~Y3oYObwcd8`3&S%OvEUg>&i~Zxp`&;ZE_`hniX74gEHh&kiRww7L z^u3IOL1~8JELWwr)o=Sdd-g5^L(4~CDaHl4f72K~Z2H@LKXB##%?CnX?)23aSDpMq zUPJE4I>srV^rkh;-#deW;bQHEkL*Rq9oF!x?)}Sn({N5n{cVXSCCB&wE;L|fSiVg+ z;rT&nhKBh1TdWL~@6X@OK5)~yR*R{A0@t6=y9ZMq@^idzc4bUVd%y43Y6|%T^gWUb2a|uadalFBc~NNB2)?$pWrF>`v!j zS(L9={BH1G`NQ3KUDZEVx9whd=3BU7-C~W+f3M25XZ6P~=hX2CDEawNzx>yYf1ElBAr;a>H~y{iNcbIHZEfG5d#>3{kY`@vu}j5T=T=^h zpXT(>@%!EB>mR&XxG!Ww!GitLFZ8+pK7N?9T>q}zIfes={eN$G^;^DcoiyJPDXIDY zHc9;NRM+QPx7d>L^89~hKi=i9C}n(`vfTHvbeAj(!;7r!L(k=N!!JHy_`9_8^?F^X zD*h9q7yQ;WmX?<-*u7c#`@Y9t8uvU`=w^;*im1GxvT(mn;{PC-{gy3{&D#y8&GXKb z*z;w2>%UDb$tRD+vw5$RFWLTeyI;b7dztemXEz)CEaAO$ypMt5((%40akC$+fOy^ zFO%It23h&ZpV*l#vnTLEyx4rM9U3*Crt2-3tG40e$>Tqs-#*Wf!hUew#@!5?tiP;S zdGqPARZM?VHJ`07{Lr>=zs|Ay*O|_h*E{R}U0c_wJfoKT;_ut{U&PkgZmHjW_Sqcg z`p6gh-nPG&S6@pyzctb3$B(^ldJ-&GWF0F1Z}5<5h1kDyX$g*%&D;3md48RCs*iki zQT~M5gV^Nqjn8jwXbAoJ<3)TPV~B5kI;YOLSQDv*`*prdUn=*v*6YB&-`=7Qp}#d( z9(*qCwQljtuy+RF0Q>PSp6^R}dkEu`C)Zz?>-I7+^s!mh-oM??kg~a;;@6JtwOkAz zK7X;+Gt1}wo4Rnn&euowA`xakYMk@r4_(;*OXRb8%r4tG8~-}8u9m#f*Lzp<@(+fB z@5jVN_&d%_+ zB1wvF8x~)iUBU&nWY*uuY6-bNo~>WVu*oms?B2gSH6P3iu3xY=-TPqa;koudA~iv& zE_J2({~(s{jsLbDFJ*l4L~2In34XhuTJIM<%6M&eXJ@IQ>8AfaY+cgx{%yJtKdtIh z{_C4f>i_?V?wrRt?@PIRaen=)o6V~wAA~Z7oU;EBdHZenQ<0>NTIblohJU5v5e>nTkxbooVzial+1~JWO{KzfR(c znAz}uPkrdwo~06B`u(p8&3GRw1NPIt-;+}gNJ=O!dH2DV@$!C~83#BNOC5ixi8OD^ zc57WwUjr8C-l->iWO}w2!(Z=*Z@Ty0yA@bA&9?sk#$C(~pv1xbH+5lss>STax%>ZH znA%Qmt8~%5&I2|d6rEEK26wawIPE#|GUUsF-D@i^pZGTI4Sy}m=Jk=w3Z;DDCe&Ck gE4K(dQvJ_7#r)fZ)TZcE1_lNOPgg&ebxsLQ0K)U|rT_o{ diff --git a/website/assets/style.css b/website/assets/style.css index 1587c9f2..ab056e85 100644 --- a/website/assets/style.css +++ b/website/assets/style.css @@ -1683,6 +1683,11 @@ code.doc { .language-bash{ cursor: pointer; } -#avatar{ - margin: 100px; +#game{ + height: 200px; + width: 48px; + margin: 0 auto; + position: relative; + background: darkred; + transform: translate3d(0,-8px,0); } -- GitLab From 5752b580b7220b672910e3ce073147288cf3488a Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 3 May 2021 12:51:53 -0600 Subject: [PATCH 099/116] profile --- cabal.project | 5 ++++- examples/Shpadoinkle-examples.cabal | 19 +++++++++++++++++++ examples/Stress.hs | 29 +++++++++++++++++++++++++++++ nix/overlay-shpadoinkle.nix | 2 +- nix/overlay.nix | 2 +- 5 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 examples/Stress.hs diff --git a/cabal.project b/cabal.project index 3103dcf8..c2094007 100644 --- a/cabal.project +++ b/cabal.project @@ -18,4 +18,7 @@ packages: core , isreal -tests: true +library-profiling: True +executable-profiling: True +tests: True +benchmarks: True diff --git a/examples/Shpadoinkle-examples.cabal b/examples/Shpadoinkle-examples.cabal index fe33edda..d339439c 100644 --- a/examples/Shpadoinkle-examples.cabal +++ b/examples/Shpadoinkle-examples.cabal @@ -380,3 +380,22 @@ executable widgets , text default-language: Haskell2010 + + +executable stress + import: ghc-options, ghcjs-options + main-is: Stress.hs + build-depends: + base >=4.12.0 && <4.16 + , text + , containers + , generic-lens + , lens + + , Shpadoinkle + , Shpadoinkle-html + , Shpadoinkle-lens + , Shpadoinkle-backend-snabbdom + , Shpadoinkle-backend-pardiff + + default-language: Haskell2010 diff --git a/examples/Stress.hs b/examples/Stress.hs new file mode 100644 index 00000000..3401cdbf --- /dev/null +++ b/examples/Stress.hs @@ -0,0 +1,29 @@ +{-# LANGUAGE OverloadedStrings #-} + + +module Main where + + +import Data.Text (Text, pack) +import Shpadoinkle (Html, JSM) +-- import Shpadoinkle.Backend.Snabbdom (runSnabbdom, stage) +import Shpadoinkle.Backend.ParDiff (runParDiff, stage) +import Shpadoinkle.Html (div_, input', onInput, text, + value) +import Shpadoinkle.Run (runJSorWarp, simple) + + +view :: Text -> Html m Text +view x = div_ + [ input' [ onInput const, value x ] + , div_ $ (\y -> div_ [ text $ pack (show y) <> x ]) <$> [(0::Int)..50] + ] + + +app :: JSM () +app = simple runParDiff "" view stage + + +main :: IO () +main = runJSorWarp 8080 app + diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index e515df16..93ce7212 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -1,4 +1,4 @@ -{ compiler, isJS }: self: super: with super.lib; let +{ compiler, isJS, enableLibraryProfiling ? false }: self: super: with super.lib; let nixpkgs-unstable = builtins.fetchTarball { diff --git a/nix/overlay.nix b/nix/overlay.nix index 569ac2e5..1078192e 100644 --- a/nix/overlay.nix +++ b/nix/overlay.nix @@ -3,5 +3,5 @@ with (import ./base-pkgs.nix { inherit chan; } {}).lib; foldl' composeExtensions (_: _: {}) [ (import ./overlay-reflex.nix { inherit compiler isJS enableLibraryProfiling; }) - (import ./overlay-shpadoinkle.nix { inherit compiler isJS; }) + (import ./overlay-shpadoinkle.nix { inherit compiler isJS enableLibraryProfiling; }) ] -- GitLab From 21af7ae2a0146412cc705e055a5a3821efac4ef5 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 3 May 2021 16:37:22 -0600 Subject: [PATCH 100/116] mofit --- .gitlab-ci.yml | 8 ++++---- nix/base.nix | 18 ++++++++++++++++-- nix/overlay-shpadoinkle.nix | 6 ++++++ shell.nix | 2 +- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d4fc2380..2aa647b4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -86,28 +86,28 @@ Dev Shell GHC 8.6.5 on Linux: needs: - Packages GHC 8.6.5 on Linux script: - - nix-shell --run "echo works" --argstr compiler ghc865 --fallback + - nix-shell --run "echo works" --arg enableLibraryProfiling true --fallback Dev Shell GHCJS 8.6 on Linux: stage: Linux Shell needs: - Packages GHCJS 8.6 on Linux script: - - nix-shell --run "echo works" --argstr compiler ghc865 --arg isJS true --fallback + - nix-shell --run "echo works" --arg enableLibraryProfiling true --arg isJS true --fallback Dev Shell GHC 8.6.5 on Darwin: stage: Darwin Shell needs: - Packages GHC 8.6.5 on Darwin script: - - nix-build --run "echo works" --argstr compiler ghc865 --argstr system x86_64-darwin --fallback + - nix-build --run "echo works" --arg enableLibraryProfiling true --argstr system x86_64-darwin --fallback Dev Shell GHCJS 8.6 on Darwin: stage: Darwin Shell needs: - Packages GHCJS 8.6 on Darwin script: - - nix-shell --run "echo works" --argstr compiler ghc865 --argstr system x86_64-darwin --arg isJS true --fallback + - nix-shell --run "echo works" --arg enableLibraryProfiling true --argstr system x86_64-darwin --arg isJS true --fallback Website: stage: Documentation diff --git a/nix/base.nix b/nix/base.nix index f6206ad6..078f0d16 100644 --- a/nix/base.nix +++ b/nix/base.nix @@ -27,8 +27,22 @@ let docker = import ../examples/servant-crud/docker.nix { inherit compiler chan; }; - ghcTools = with haskell.packages.${compiler}; - [ easy-hls cabal-install ghcid hpack pkgs.stylish-haskell pkgs.hlint ]; + # eventlog2html = (import (pkgs.fetchFromGitHub { + # owner = "mpickering"; + # repo = "eventlog2html"; + # rev = "a18ec810328c71122ccc630fccfcea5b48c0e937"; + # sha256 = "1y24md2x4zs1vjahfljgfyvxhpk097s1mjddha4rpcf99f0djd93"; + # }) {}).eventlog2html; + + eventlog2html = (import ../../eventlog2html {}).eventlog2html; + + + ghcTools = with haskell.packages.${compiler}; with haskell.lib; + [ easy-hls pkgs.stylish-haskell pkgs.hlint] + ++ (if enableLibraryProfiling then [ eventlog2html ] else []) + ++ map disableLibraryProfiling + ([ cabal-install ghcid ] + ++ (if enableLibraryProfiling then [ hp2pretty ghc-prof-flamegraph ] else [])); cannibal = if optimizeJS then util.doCannibalize else id; diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index 93ce7212..49d694f8 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -183,6 +183,11 @@ in { hpkgs = { + mkDerivation = args: hsuper.mkDerivation (args // { + inherit enableLibraryProfiling; + doCheck = if enableLibraryProfiling then false else args.doCheck; + }); + Shpadoinkle = call "Shpadoinkle" ../core; Shpadoinkle-backend-snabbdom = call "Shpadoinkle-backend-snabbdom" ../backends/snabbdom; Shpadoinkle-backend-static = call "Shpadoinkle-backend-static" ../backends/static; @@ -217,6 +222,7 @@ in { quickcheck-classes = dontJS (doJailbreak (hself.callCabal2nix "quickcheck-classes" "${quickcheck-classes-src}/quickcheck-classes" {})); quickcheck-classes-base = doJailbreak (hself.callCabal2nix "quickcheck-classes-base" "${quickcheck-classes-src}/quickcheck-classes-base" {}); primitive-addr = doJailbreak hsuper.primitive-addr; + trie-simple = dontHaddock (unmarkBroken hsuper.trie-simple); # Diff = dontJS (if compiler == "ghc844" then appendPatch hsuper.Diff ./Diff-Test.patch else hsuper.diff); } // forThese dontJS [ diff --git a/shell.nix b/shell.nix index 7423693d..83b2f9f3 100644 --- a/shell.nix +++ b/shell.nix @@ -1 +1 @@ -{ isJS ? false }: import ./examples/shell.nix { inherit isJS; } +(import ./nix/base.nix).shell -- GitLab From 9c98e2f81bb8fd61f00c15f5c28f0cc3f7bf9350 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 3 May 2021 21:01:03 -0600 Subject: [PATCH 101/116] profiling with ghc works --- cabal.project | 3 --- examples/Shpadoinkle-examples.cabal | 1 - examples/Stress.hs | 18 +++++++++--------- nix/overlay-shpadoinkle.nix | 2 +- nix/sandblast.sh | 2 -- 5 files changed, 10 insertions(+), 16 deletions(-) diff --git a/cabal.project b/cabal.project index c2094007..64e566fe 100644 --- a/cabal.project +++ b/cabal.project @@ -18,7 +18,4 @@ packages: core , isreal -library-profiling: True -executable-profiling: True tests: True -benchmarks: True diff --git a/examples/Shpadoinkle-examples.cabal b/examples/Shpadoinkle-examples.cabal index d339439c..3971d7fb 100644 --- a/examples/Shpadoinkle-examples.cabal +++ b/examples/Shpadoinkle-examples.cabal @@ -383,7 +383,6 @@ executable widgets executable stress - import: ghc-options, ghcjs-options main-is: Stress.hs build-depends: base >=4.12.0 && <4.16 diff --git a/examples/Stress.hs b/examples/Stress.hs index 3401cdbf..2441b2bf 100644 --- a/examples/Stress.hs +++ b/examples/Stress.hs @@ -4,24 +4,24 @@ module Main where -import Data.Text (Text, pack) -import Shpadoinkle (Html, JSM) --- import Shpadoinkle.Backend.Snabbdom (runSnabbdom, stage) -import Shpadoinkle.Backend.ParDiff (runParDiff, stage) -import Shpadoinkle.Html (div_, input', onInput, text, - value) -import Shpadoinkle.Run (runJSorWarp, simple) +import Data.Text (Text, pack) +import Shpadoinkle (Html, JSM) +import Shpadoinkle.Backend.Snabbdom (runSnabbdom, stage) +-- import Shpadoinkle.Backend.ParDiff (runParDiff, stage) +import Shpadoinkle.Html (div_, input', onInput, text, + value) +import Shpadoinkle.Run (runJSorWarp, simple) view :: Text -> Html m Text view x = div_ [ input' [ onInput const, value x ] - , div_ $ (\y -> div_ [ text $ pack (show y) <> x ]) <$> [(0::Int)..50] + , div_ $ (\y -> div_ [ text $ pack (show y) <> x ]) <$> [(0::Int)..500] ] app :: JSM () -app = simple runParDiff "" view stage +app = simple runSnabbdom "" view stage main :: IO () diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index 49d694f8..6321b3de 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -185,7 +185,7 @@ in { mkDerivation = args: hsuper.mkDerivation (args // { inherit enableLibraryProfiling; - doCheck = if enableLibraryProfiling then false else args.doCheck; + doCheck = if enableLibraryProfiling then false else args.doCheck or true; }); Shpadoinkle = call "Shpadoinkle" ../core; diff --git a/nix/sandblast.sh b/nix/sandblast.sh index af7901b3..c4726b78 100755 --- a/nix/sandblast.sh +++ b/nix/sandblast.sh @@ -6,5 +6,3 @@ rm client & rm -r *dist* & rm -- **/*dist* & rm roster.db & -rm -- **/*.cabal & -rm -- */docs/**/*.html & -- GitLab From 1c772b8df757bdf32f793fbfbaa7300d0386c67c Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Mon, 10 May 2021 17:11:59 -0600 Subject: [PATCH 102/116] no stress --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9df25952..2a1f8f4a 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ tags /website/favicon.html /website/assets/landing_logo.svg .cache +stress* -- GitLab From 195ed8a3b0ffbed59eaca1f560fbeff06577c67d Mon Sep 17 00:00:00 2001 From: Krzysztof Gogolewski Date: Fri, 14 May 2021 17:25:50 +0000 Subject: [PATCH 103/116] Minor optimizations for the marketing branch --- .../pardiff/Shpadoinkle/Backend/ParDiff.hs | 2 + .../snabbdom/Shpadoinkle/Backend/Snabbdom.hs | 3 + core/Shpadoinkle/Continuation.hs | 105 ++++++++++++------ core/Shpadoinkle/Core.hs | 1 + nix/util.nix | 2 +- 5 files changed, 75 insertions(+), 38 deletions(-) diff --git a/backends/pardiff/Shpadoinkle/Backend/ParDiff.hs b/backends/pardiff/Shpadoinkle/Backend/ParDiff.hs index 3587cb0f..284dacd8 100644 --- a/backends/pardiff/Shpadoinkle/Backend/ParDiff.hs +++ b/backends/pardiff/Shpadoinkle/Backend/ParDiff.hs @@ -363,6 +363,8 @@ patch' parent old new = do return new +{-# SPECIALIZE interpret' :: forall a. NFData a => (JSM ~> JSM) -> Html (ParDiffT a JSM) a -> ParDiffT a JSM (ParVNode a) #-} + interpret' :: forall m a . MonadJSM m diff --git a/backends/snabbdom/Shpadoinkle/Backend/Snabbdom.hs b/backends/snabbdom/Shpadoinkle/Backend/Snabbdom.hs index a6d258bd..1e4c5182 100644 --- a/backends/snabbdom/Shpadoinkle/Backend/Snabbdom.hs +++ b/backends/snabbdom/Shpadoinkle/Backend/Snabbdom.hs @@ -137,8 +137,10 @@ traverseWithKey_ f = go go (Bin 1 k v _ _) = f k v go (Bin _ k v l r) = go l *> f k v *> go r {-# INLINE traverseWithKey_ #-} +{-# SPECIALIZE traverseWithKey_ :: (k -> a -> JSM ()) -> Map k a -> JSM () #-} +{-# SPECIALIZE props :: NFData a => (JSM ~> JSM) -> TVar a -> Props (SnabbdomT a JSM) a -> JSM Object #-} props :: Monad m => NFData a => (m ~> JSM) -> TVar a -> Props (SnabbdomT a m) a -> JSM Object props toJSM i (Props xs) = do o <- create @@ -256,4 +258,5 @@ stage = liftJSM $ do setInnerHTML b "" _ <- appendChild b elm RawNode <$> toJSVal elm +{-# SPECIALIZE stage :: SnabbdomT a JSM RawNode #-} diff --git a/core/Shpadoinkle/Continuation.hs b/core/Shpadoinkle/Continuation.hs index 21368816..fef796e5 100644 --- a/core/Shpadoinkle/Continuation.hs +++ b/core/Shpadoinkle/Continuation.hs @@ -57,7 +57,7 @@ import GHC.Conc (retry) import GHCJS.DOM (currentWindowUnchecked) import GHCJS.DOM.Window (Window) import GHCJS.DOM.WindowOrWorkerGlobalScope (clearTimeout, setTimeout) -import Language.Javascript.JSaddle (MonadJSM, fun) +import Language.Javascript.JSaddle (MonadJSM, fun, JSM) import UnliftIO (MonadUnliftIO, TVar, UnliftIO, askUnliftIO, atomically, liftIO, @@ -84,7 +84,7 @@ import UnliftIO.Concurrent (forkIO) -- (even if it changed since the start of computing the Continuation), and the updates made -- so far, although those updates are not committed to the real state until the Continuation -- finishes and they are all done atomically together. -data Continuation m a = Continuation (a -> a, a -> m (Continuation m a)) +data Continuation m a = Continuation (a -> a) (a -> m (Continuation m a)) | Rollback (Continuation m a) | Merge (Continuation m a) | Pure (a -> a) @@ -104,24 +104,26 @@ done = pur id -- | A monadic computation of a pure state updating function can be turned into a Continuation. +{-# SPECIALIZE impur :: JSM (a -> a) -> Continuation JSM a #-} impur :: Applicative m => m (a -> a) -> Continuation m a -impur m = kleisli . const $ (\f -> Continuation (f, const (pure done))) <$> m +impur m = kleisli . const $ (\f -> Continuation f (const (pure done))) <$> m -- | This turns a Kleisli arrow for computing a Continuation into the Continuation which -- reads the state, runs the monadic computation specified by the arrow on that state, -- and runs the resulting Continuation. kleisli :: (a -> m (Continuation m a)) -> Continuation m a -kleisli = Continuation . (id,) +kleisli = Continuation id -- | A monadic computation can be turned into a Continuation which does not touch the state. +{-# SPECIALIZE causes :: JSM () -> Continuation JSM a #-} causes :: Applicative m => m () -> Continuation m a causes m = impur (id <$ m) causedBy :: m (Continuation m a) -> Continuation m a -causedBy = Continuation . (id,) . const +causedBy = Continuation id . const -- | A continuation can be forced to write its changes midflight. @@ -131,13 +133,13 @@ merge = Merge -- | Sequences two continuations one after the other. before :: Applicative m => Continuation m a -> Continuation m a -> Continuation m a -Pure f `before` Continuation (g, h) = Continuation (g . f, h) +Pure f `before` Continuation g h = Continuation (g . f) h Pure _ `before` Rollback g = g -Pure f `before` Merge g = Continuation (f, const (pure (Merge g))) +Pure f `before` Merge g = Continuation f (const (pure (Merge g))) Pure f `before` Pure g = Pure (g.f) Merge f `before` g = Merge (f `before` g) Rollback f `before` g = Rollback (f `before` g) -Continuation (f, g) `before` h = Continuation . (f,) $ fmap (`before` h) . g +Continuation f g `before` h = Continuation f $ fmap (`before` h) . g after :: Applicative m => Continuation m a -> Continuation m a -> Continuation m a @@ -152,12 +154,14 @@ after = flip before -- territory, then you should probably be using 'writeUpdate' instead of 'runContinuation', -- because 'writeUpdate' will allow each stage of the Continuation to see any extant updates -- made to the territory after the Continuation started running. +{-# SPECIALIZE runContinuation :: Continuation JSM a -> a -> JSM (a -> a) #-} runContinuation :: Monad m => Continuation m a -> a -> m (a -> a) runContinuation = runContinuation' id +{-# SPECIALIZE runContinuation' :: (a -> a) -> Continuation JSM a -> a -> JSM (a -> a) #-} runContinuation' :: Monad m => (a -> a) -> Continuation m a -> a -> m (a -> a) -runContinuation' f (Continuation (g, h)) x = do +runContinuation' f (Continuation g h) x = do i <- h (f x) runContinuation' (g.f) i x runContinuation' _ (Rollback f) x = runContinuation' id f x @@ -176,89 +180,105 @@ instance Continuous Continuation where -- | Given a natural transformation, change a Continuation's underlying functor. +{-# SPECIALIZE hoist :: (forall b. JSM b -> n b) -> Continuation JSM a -> Continuation n a #-} hoist :: Functor m => (forall b. m b -> n b) -> Continuation m a -> Continuation n a hoist _ (Pure f) = Pure f hoist f (Rollback r) = Rollback (hoist f r) hoist f (Merge g) = Merge (hoist f g) -hoist f (Continuation (g, h)) = Continuation . (g,) $ \x -> f $ hoist f <$> h x +hoist f (Continuation g h) = Continuation g $ \x -> f $ hoist f <$> h x -- | Apply a lens inside a Continuation to change the Continuation's type. +{-# SPECIALIZE liftC' :: (a -> b -> b) -> (b -> a) -> Continuation JSM a -> Continuation JSM b #-} liftC' :: Functor m => (a -> b -> b) -> (b -> a) -> Continuation m a -> Continuation m b liftC' f g (Pure h) = Pure (\x -> f (h (g x)) x) liftC' f g (Rollback r) = Rollback (liftC' f g r) liftC' f g (Merge h) = Merge (liftC' f g h) -liftC' f g (Continuation (h, i)) = Continuation (\x -> f (h (g x)) x, \x -> liftC' f g <$> i (g x)) +liftC' f g (Continuation h i) = Continuation (\x -> f (h (g x)) x) (\x -> liftC' f g <$> i (g x)) -- | Apply a traversal inside a Continuation to change the Continuation's type. +{-# SPECIALIZE liftCMay' :: (a -> b -> b) -> (b -> Maybe a) -> Continuation JSM a -> Continuation JSM b #-} liftCMay' :: Applicative m => (a -> b -> b) -> (b -> Maybe a) -> Continuation m a -> Continuation m b liftCMay' f g (Pure h) = Pure $ \x -> maybe x (flip f x . h) $ g x liftCMay' f g (Rollback r) = Rollback (liftCMay' f g r) liftCMay' f g (Merge h) = Merge (liftCMay' f g h) -liftCMay' f g (Continuation (h, i)) = - Continuation (\x -> maybe x (flip f x . h) $ g x, maybe (pure done) (fmap (liftCMay' f g) . i) . g) +liftCMay' f g (Continuation h i) = + Continuation (\x -> maybe x (flip f x . h) $ g x) ( maybe (pure done) (fmap (liftCMay' f g) . i) . g) -- | Given a lens, change the value type of @f@ by applying the lens in the Continuations inside @f@. +{-# SPECIALIZE liftC :: Functor m => (a -> b -> b) -> (b -> a) -> Continuation m a -> Continuation m b #-} liftC :: Functor m => Continuous f => (a -> b -> b) -> (b -> a) -> f m a -> f m b liftC f g = mapC (liftC' f g) -- | Given a traversal, change the value of @f@ by apply the traversal in the Continuations inside @f@. +{-# SPECIALIZE liftCMay :: Applicative m => (a -> b -> b) -> (b -> Maybe a) -> Continuation m a -> Continuation m b #-} liftCMay :: Applicative m => Continuous f => (a -> b -> b) -> (b -> Maybe a) -> f m a -> f m b liftCMay f g = mapC (liftCMay' f g) -- | Change a void continuation into any other type of Continuation. +{-# SPECIALIZE voidC' :: Continuation JSM () -> Continuation JSM a #-} voidC' :: Monad m => Continuation m () -> Continuation m a -voidC' f = Continuation . (id,) $ \_ -> do +voidC' f = Continuation id $ \_ -> do _ <- runContinuation f () return done -- | Change the type of the f-embedded void Continuations into any other type of Continuation. +{-# SPECIALIZE voidC :: Monad m => Continuation m () -> Continuation m a #-} +{-# SPECIALIZE voidC :: Continuation JSM () -> Continuation JSM a #-} voidC :: Monad m => Continuous f => f m () -> f m a voidC = mapC voidC' -- | Forget about the Continuations. +{-# SPECIALIZE forgetC :: Continuation m a -> Continuation m b #-} +{-# SPECIALIZE forgetC :: Continuation JSM a -> Continuation JSM b #-} forgetC :: Continuous f => f m a -> f m b forgetC = mapC (const done) --- | Change the type of a Continuation by applying it to the left coordinate of a tuple. +{-# SPECIALIZE leftC' :: Continuation JSM a -> Continuation JSM (a,b) #-} leftC' :: Functor m => Continuation m a -> Continuation m (a,b) leftC' = liftC' (\x (_,y) -> (x,y)) fst -- | Change the type of @f@ by applying the Continuations inside @f@ to the left coordinate of a tuple. +{-# SPECIALIZE leftC :: Continuation JSM a -> Continuation JSM (a,b) #-} leftC :: Functor m => Continuous f => f m a -> f m (a,b) leftC = mapC leftC' -- | Change the type of a Continuation by applying it to the right coordinate of a tuple. +{-# SPECIALIZE rightC' :: Continuation JSM b -> Continuation JSM (a,b) #-} rightC' :: Functor m => Continuation m b -> Continuation m (a,b) rightC' = liftC' (\y (x,_) -> (x,y)) snd -- | Change the value type of @f@ by applying the Continuations inside @f@ to the right coordinate of a tuple. +{-# SPECIALIZE rightC :: Continuation JSM b -> Continuation JSM (a,b) #-} rightC :: Functor m => Continuous f => f m b -> f m (a,b) rightC = mapC rightC' -- | Transform a Continuation to work on 'Maybe's. If it encounters 'Nothing', then it cancels itself. +{-# SPECIALIZE maybeC' :: Continuation JSM a -> Continuation JSM (Maybe a) #-} maybeC' :: Applicative m => Continuation m a -> Continuation m (Maybe a) maybeC' (Pure f) = Pure (fmap f) maybeC' (Rollback r) = Rollback (maybeC' r) maybeC' (Merge f) = Merge (maybeC' f) -maybeC' (Continuation (f, g)) = Continuation . (fmap f,) $ +maybeC' (Continuation f g) = Continuation (fmap f) $ \case Just x -> maybeC' <$> g x Nothing -> pure (Rollback done) -- | Change the value type of @f@ by transforming the Continuations inside @f@ to work on 'Maybe's using maybeC'. +{-# SPECIALIZE maybeC' :: Continuation JSM a -> Continuation JSM (Maybe a) #-} maybeC :: Applicative m => Continuous f => f m a -> f m (Maybe a) maybeC = mapC maybeC' @@ -273,14 +293,16 @@ comaybe f x = fromMaybe x . f $ Just x -- The resulting Continuation acts like the input Continuation except that -- when the input Continuation would replace the current value with 'Nothing', -- instead the current value is retained. +{-# SPECIALIZE comaybeC' :: Continuation JSM (Maybe a) -> Continuation JSM a #-} comaybeC' :: Functor m => Continuation m (Maybe a) -> Continuation m a comaybeC' (Pure f) = Pure (comaybe f) comaybeC' (Rollback r) = Rollback (comaybeC' r) comaybeC' (Merge f) = Merge (comaybeC' f) -comaybeC' (Continuation (f,g)) = Continuation (comaybe f, fmap comaybeC' . g . Just) +comaybeC' (Continuation f g) = Continuation (comaybe f) ( fmap comaybeC' . g . Just) -- | Transform the Continuations inside @f@ using comaybeC'. +{-# SPECIALIZE comaybeC :: Continuation JSM (Maybe a) -> Continuation JSM a #-} comaybeC :: Functor m => Continuous f => f m (Maybe a) -> f m a comaybeC = mapC comaybeC' @@ -303,21 +325,22 @@ mapRight f (Right x) = Right (f x) -- If the state is in a different Either-branch when the Continuation -- completes than it was when the Continuation started, then the -- coproduct Continuation will have no effect on the state. +{-# SPECIALIZE eitherC' :: Continuation JSM a -> Continuation JSM b -> Continuation JSM (Either a b) #-} eitherC' :: Applicative m => Continuation m a -> Continuation m b -> Continuation m (Either a b) -eitherC' f g = Continuation . (id,) $ \case +eitherC' f g = Continuation id $ \case Left x -> case f of Pure h -> pure (Pure (mapLeft h)) Rollback r -> pure . Rollback $ eitherC' r done Merge h -> pure . Merge $ eitherC' h done - Continuation (h, i) -> - (\j -> Continuation (mapLeft h, const . pure $ eitherC' j (Rollback done))) + Continuation h i -> + (\j -> Continuation (mapLeft h) ( const . pure $ eitherC' j (Rollback done))) <$> i x Right x -> case g of Pure h -> pure (Pure (mapRight h)) Rollback r -> pure . Rollback $ eitherC' done r Merge h -> pure . Merge $ eitherC' done h - Continuation (h, i) -> - (\j -> Continuation (mapRight h, const . pure $ eitherC' (Rollback done) j)) + Continuation h i -> + (\j -> Continuation (mapRight h) (const . pure $ eitherC' (Rollback done) j)) <$> i x @@ -326,14 +349,16 @@ eitherC' f g = Continuation . (id,) $ \case -- the types inside the coproduct. The Continuations in the resulting -- structure will only have effect on the state while it is in the branch -- of the coproduct selected by the input value used to create the structure. +{-# SPECIALIZE eitherC :: (a -> Continuation JSM a) -> (b -> Continuation JSM b) -> Either a b -> Continuation JSM (Either a b) #-} eitherC :: Applicative m => Continuous f => (a -> f m a) -> (b -> f m b) -> Either a b -> f m (Either a b) eitherC l _ (Left x) = mapC (\c -> eitherC' c (pur id)) (l x) eitherC _ r (Right x) = mapC (eitherC' (pur id)) (r x) -- | Transform the type of a Continuation using an isomorphism. +{-# SPECIALIZE contIso :: (a -> b) -> (b -> a) -> Continuation JSM a -> Continuation JSM b #-} contIso :: Functor m => (a -> b) -> (b -> a) -> Continuation m a -> Continuation m b -contIso f g (Continuation (h, i)) = Continuation (f.h.g, fmap (contIso f g) . i . g) +contIso f g (Continuation h i) = Continuation (f.h.g) (fmap (contIso f g) . i . g) contIso f g (Rollback h) = Rollback (contIso f g h) contIso f g (Merge h) = Merge (contIso f g h) contIso f g (Pure h) = Pure (f.h.g) @@ -344,7 +369,7 @@ contIso f g (Pure h) = Pure (f.h.g) instance Applicative m => F.Functor EndoIso EndoIso (Continuation m) where map :: EndoIso a b -> EndoIso (Continuation m a) (Continuation m b) map (EndoIso f g h) = - EndoIso (Continuation . (f,) . const . pure) (contIso g h) (contIso h g) + EndoIso (Continuation f . const . pure) (contIso g h) (contIso h g) -- | You can combine multiple Continuations homogeneously using the 'Monoid' typeclass @@ -354,17 +379,17 @@ instance Applicative m => F.Functor EndoIso EndoIso (Continuation m) where -- all of them are done. A merge in any one of the branches will cause all of -- the changes that branch can see to be merged. instance Applicative m => Semigroup (Continuation m a) where - (Continuation (f, g)) <> (Continuation (h, i)) = - Continuation (f.h, \x -> (<>) <$> g x <*> i x) - (Continuation (f, g)) <> (Rollback h) = - Rollback (Continuation (f, fmap (<> h) . g)) - (Rollback h) <> (Continuation (_, g)) = - Rollback (Continuation (id, fmap (h <>) . g)) + (Continuation f g) <> (Continuation h i) = + Continuation (f.h) (\x -> (<>) <$> g x <*> i x) + (Continuation f g) <> (Rollback h) = + Rollback (Continuation f (fmap (<> h) . g)) + (Rollback h) <> (Continuation _ g) = + Rollback (Continuation id (fmap (h <>) . g)) (Rollback f) <> (Rollback g) = Rollback (f <> g) (Pure f) <> (Pure g) = Pure (f.g) - (Pure f) <> (Continuation (g,h)) = Continuation (f.g,h) - (Continuation (f,g)) <> (Pure h) = Continuation (f.h,g) - (Pure f) <> (Rollback g) = Continuation (f, const (pure (Rollback g))) + (Pure f) <> (Continuation g h) = Continuation (f.g) h + (Continuation f g) <> (Pure h) = Continuation (f.h) g + (Pure f) <> (Rollback g) = Continuation f (const (pure (Rollback g))) (Rollback f) <> (Pure _) = Rollback f (Merge f) <> g = Merge (f <> g) f <> (Merge g) = Merge (f <> g) @@ -376,12 +401,13 @@ instance Applicative m => Monoid (Continuation m a) where mempty = done +{-# SPECIALIZE writeUpdate' :: NFData a => (a -> a) -> TVar a -> (a -> JSM (Continuation JSM a)) -> JSM () #-} writeUpdate' :: MonadUnliftIO m => NFData a => (a -> a) -> TVar a -> (a -> m (Continuation m a)) -> m () writeUpdate' h model f = do i <- readTVarIO model m <- f (h i) case m of - Continuation (g,gs) -> writeUpdate' (g . h) model gs + Continuation g gs -> writeUpdate' (g . h) model gs Pure g -> atomically (writeTVar model . g . h =<< readTVar model) Merge g -> do atomically $ writeTVar model . h =<< readTVar model @@ -392,16 +418,18 @@ writeUpdate' h model f = do -- | Run a Continuation on a state variable. This may update the state. -- This is a synchronous, non-blocking operation for pure updates, -- and an asynchronous, non-blocking operation for impure updates. +{-# SPECIALIZE writeUpdate :: NFData a => TVar a -> Continuation JSM a -> JSM () #-} writeUpdate :: MonadUnliftIO m => NFData a => TVar a -> Continuation m a -> m () writeUpdate model = \case - Continuation (f,g) -> void . forkIO $ writeUpdate' f model g - Pure f -> atomically (writeTVar model . f =<< readTVar model) - Merge f -> writeUpdate model f - Rollback f -> writeUpdate model f + Continuation f g -> void . forkIO $ writeUpdate' f model g + Pure f -> atomically (writeTVar model . f =<< readTVar model) + Merge f -> writeUpdate model f + Rollback f -> writeUpdate model f -- | Execute a fold by watching a state variable and executing the next -- step of the fold each time it changes. +{-# SPECIALIZE shouldUpdate :: forall a b. Eq a => (b -> a -> JSM b) -> b -> TVar a -> JSM () #-} shouldUpdate :: forall a b m. MonadJSM m => MonadUnliftIO m => Eq a => (b -> a -> m b) -> b -> TVar a -> m () shouldUpdate sun prev currentModel = do -- get the current state of the model @@ -457,6 +485,7 @@ newtype ContinuationT model m a = ContinuationT -- | This adds the given Continuation to the Continuation being built up in the monadic context -- where this function is invoked. +{-# SPECIALIZE commit :: Continuation JSM model -> ContinuationT model JSM () #-} commit :: Applicative m => Continuation m model -> ContinuationT model m () commit = ContinuationT . pure . ((),) @@ -464,6 +493,7 @@ commit = ContinuationT . pure . ((),) -- | This turns a monadic computation to build up a Continuation into the Continuation which it -- represents. The actions inside the monadic computation will be run when the Continuation -- is run. The return value of the monadic computation will be discarded. +{-# SPECIALIZE voidRunContinuationT :: ContinuationT model JSM a -> Continuation JSM model #-} voidRunContinuationT :: Functor m => ContinuationT model m a -> Continuation m model voidRunContinuationT m = kleisli . const $ snd <$> runContinuationT m @@ -473,6 +503,7 @@ voidRunContinuationT m = kleisli . const $ snd <$> runContinuationT m -- into a Continuation which reads the current state of the model, -- runs the resulting monadic computation, and runs the Continuation -- resulting from that computation. +{-# SPECIALIZE kleisliT :: (model -> ContinuationT model JSM a) -> Continuation JSM model #-} kleisliT :: Applicative m => (model -> ContinuationT model m a) -> Continuation m model kleisliT f = kleisli (pure . voidRunContinuationT . f) diff --git a/core/Shpadoinkle/Core.hs b/core/Shpadoinkle/Core.hs index 57536ab3..ea935bba 100644 --- a/core/Shpadoinkle/Core.hs +++ b/core/Shpadoinkle/Core.hs @@ -142,6 +142,7 @@ listenM_ k = listenC k . causes newtype Props m a = Props { getProps :: Map Text (Prop m a) } +{-# SPECIALIZE toProps :: [(Text, Prop JSM a)] -> Props JSM a #-} toProps :: Applicative m => [(Text, Prop m a)] -> Props m a toProps = foldMap $ Props . uncurry singleton diff --git a/nix/util.nix b/nix/util.nix index 19d7994c..af12053a 100644 --- a/nix/util.nix +++ b/nix/util.nix @@ -19,7 +19,7 @@ rec echo "╙──────────────────────────────────────────────────────────────────────" echo "" - ${pkgs.closurecompiler}/bin/closure-compiler -O "SIMPLE" \ + ${pkgs.closurecompiler}/bin/closure-compiler --compilation_level ADVANCED_OPTIMIZATIONS \ --jscomp_warning=checkVars \ --js_output_file=$output \ --isolation_mode IIFE \ -- GitLab From 1773d642c7e6d8709e57782cb7024e2820edc52e Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Fri, 14 May 2021 20:34:46 -0600 Subject: [PATCH 104/116] integrate mini-sandbox --- .../Website/Component/LiveExample.hs | 49 ++++++----- website/Shpadoinkle/Website/Page/Home.hs | 4 +- website/assets/animate.js | 3 +- website/assets/style.css | 85 +++++++++++++++++++ 4 files changed, 117 insertions(+), 24 deletions(-) diff --git a/website/Shpadoinkle/Website/Component/LiveExample.hs b/website/Shpadoinkle/Website/Component/LiveExample.hs index b3831fb8..c7e23210 100644 --- a/website/Shpadoinkle/Website/Component/LiveExample.hs +++ b/website/Shpadoinkle/Website/Component/LiveExample.hs @@ -40,12 +40,13 @@ import Shpadoinkle (Continuation, Html, kleisli, mapC, pur, readTVarIO, text, writeTVar) -import Shpadoinkle.Html (br'_, className, - div, iframe, +import Shpadoinkle.Html (br'_, class', div, + iframe, mkGlobalMailboxAfforded, onInput, src) import Shpadoinkle.Isreal.Types as Swan import Shpadoinkle.Lens (onRecord) +import Shpadoinkle.Website.Style import Shpadoinkle.Website.Types.Effects.Example (ExampleEffects, Swan (..)) import Shpadoinkle.Website.Types.Example (Example (..)) @@ -57,20 +58,30 @@ default (Text) example :: (MonadJSM m, ExampleEffects m) => ExampleLens -> Example -> Html m Example -example lmutex (Example cc token nonce state') = div "example" - [ div [ className "mirror-wrap" - , onInput $ const $ #state .~ ELoading +example lmutex (Example cc token nonce state') = div [ class' minisandbox ] + [ div [ class' minisandbox__code + , onInput . const $ #state .~ ELoading ] - [ mapC (mappend (compileExample lmutex) . onRecord #inputHaskell) $ mirror cc ] - , case state' of - EReady -> iframe - [ src $ "https://isreal.shpadoinkle.org/" - <> toUrlPiece (Swan.serve token) - <> "/index.html?nonce=" - <> pack (show $ nonce - 1) - ] [] - EError e -> errorMessages e - ELoading -> text "Loading..." + [ div [ class' [ minisandbox__header, minisandbox__code__header ] ] + [ "Live Shpadoinkle Editor" ] + , div [ class' [ minisandbox__code__content, "mirror-wrap" ] ] + [ mapC (mappend (compileExample lmutex) . onRecord #inputHaskell) $ mirror cc ] + ] + , div [ class' minisandbox__output ] + [ div [ class' [ minisandbox__header, minisandbox__output__header ] ] + [ "Result" ] + , div [ class' minisandbox__output__content ] + [ case state' of + EReady -> iframe + [ src $ "https://isreal.shpadoinkle.org/" + <> toUrlPiece (Swan.serve token) + <> "/index.html?nonce=" + <> pack (show $ nonce - 1) + ] [] + EError e -> errorMessages e + ELoading -> text "Loading..." + ] + ] ] @@ -142,10 +153,10 @@ mirrorCfg (Code cc) = do mirror :: Code -> Html m Code mirror cc = baked $ do (notify, stream) <- mkGlobalMailboxAfforded constUpdate - doc <- currentDocumentUnchecked - container <- toJSVal =<< createElement doc "div" + doc' <- currentDocumentUnchecked + container' <- toJSVal =<< createElement doc' "div" cfg <- mirrorCfg cc - cm <- jsg2 "CodeMirror" container cfg + cm <- jsg2 "CodeMirror" container' cfg _ <- cm ^. js2 "on" "change" (fun $ \_ _ _ -> do jsv <- cm ^. js0 "getValue" raw :: Maybe Text <- fromJSVal jsv @@ -153,4 +164,4 @@ mirror cc = baked $ do ) window <- currentWindowUnchecked _ <- setTimeout window (fun $ \_ _ _ -> () <$ cm ^. js0 "refresh") (Just 33) - return (RawNode container, stream) + return (RawNode container', stream) diff --git a/website/Shpadoinkle/Website/Page/Home.hs b/website/Shpadoinkle/Website/Page/Home.hs index 09398377..8133edbc 100644 --- a/website/Shpadoinkle/Website/Page/Home.hs +++ b/website/Shpadoinkle/Website/Page/Home.hs @@ -159,9 +159,7 @@ componentExample l exs cex = onRecord l $ div [ class' component ] , p_ [ text $ cex ^. #description ] ] - , div [ class' component__example ] - [ div [ class' component__example__code ] [ example l (exs ^. l) ] - ] + , example l (exs ^. l) ] diff --git a/website/assets/animate.js b/website/assets/animate.js index 5ac88ddd..497be027 100644 --- a/website/assets/animate.js +++ b/website/assets/animate.js @@ -5,8 +5,7 @@ const animClasses = , ".feature-button" , ".feature__title" , ".footer__wrapper img" - , ".component__info" - , ".component__example" + , ".minisandbox" , ".footer__nav__category" , ".footer__nav__links li" , ".hero-title" diff --git a/website/assets/style.css b/website/assets/style.css index ab056e85..f722160c 100644 --- a/website/assets/style.css +++ b/website/assets/style.css @@ -1691,3 +1691,88 @@ code.doc { background: darkred; transform: translate3d(0,-8px,0); } +.minisandbox { + margin-top: 52px; + border-radius: 31.5px; + overflow: hidden; + display: flex; + flex-direction: column; + width: 100%; + height: 426px; + background-color: #2b2932; +} + +.minisandbox__header { + font-size: 17px; + height: 48px; + padding-top: 20px; + padding-left: 14px; +} + +.minisandbox__code { + background-color: #2b2932; +} + +.minisandbox__code__header { + color: #f58065; + background-color: #161320; +} + +.minisandbox__code__content { + height: 238px; + overflow: scroll; +} + +.minisandbox__output { + background-color: #faf8ec; +} + +.minisandbox__output__header { + color: #21232a; + background-color: #e9e5d3; + width: 100%; +} + +.minisandbox__output__content { + color: #21232a; + height: 91px; + overflow: scroll; +} + +@media screen and (min-width: 640px) { + .minisandbox { + margin-top: 0; + display: flex; + flex-direction: row; + width: 778px; + height: 379px; + background-color: #2b2932; + } + + .minisandbox__header { + padding-left: 46px; + } + + .minisandbox__code { + background-color: #2b2932; + width: 450px; + } + + .minisandbox__code__content { + height: 100%; + } + + .minisandbox__output__content { + height: 100%; + } + + .minisandbox__output { + width: 328px; + background-color: #faf8ec; + } + + .minisandbox__code__header { + color: #f58065; + background-color: #161320; + } +} -- GitLab From 883a473640955fe8c2dfbd172bee44d49f438fc3 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Fri, 14 May 2021 21:10:56 -0600 Subject: [PATCH 105/116] better lookn' and beter workn' --- website/Shpadoinkle/Website/Types/SPA.hs | 2 -- website/assets/style.css | 27 +++++++++++++++++++----- website/default.nix | 1 + 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/website/Shpadoinkle/Website/Types/SPA.hs b/website/Shpadoinkle/Website/Types/SPA.hs index c0e8c4d4..d5e0b46c 100644 --- a/website/Shpadoinkle/Website/Types/SPA.hs +++ b/website/Shpadoinkle/Website/Types/SPA.hs @@ -57,7 +57,6 @@ type SPA m :<|> "sandbox" :> View' m :<|> "404" :> View' m - :<|> View' m routes :: SPA m :>> Route @@ -79,7 +78,6 @@ routes :<|> RSandbox :<|> RFourOhFour - :<|> RFourOhFour instance Routed (SPA m) Route where diff --git a/website/assets/style.css b/website/assets/style.css index f722160c..a0eb9863 100644 --- a/website/assets/style.css +++ b/website/assets/style.css @@ -1676,6 +1676,7 @@ code.doc { } .CodeMirror { height: auto; + font-size: 14px; } .component__example__code { overflow: hidden; @@ -1705,7 +1706,7 @@ code.doc { .minisandbox__header { font-size: 17px; height: 48px; - padding-top: 20px; + padding-top: 15px; padding-left: 14px; } @@ -1720,7 +1721,7 @@ code.doc { .minisandbox__code__content { height: 238px; - overflow: scroll; + overflow: hidden; } .minisandbox__output { @@ -1750,12 +1751,12 @@ code.doc { } .minisandbox__header { - padding-left: 46px; + padding-left: 31px; } .minisandbox__code { background-color: #2b2932; - width: 450px; + width: 524px; } .minisandbox__code__content { @@ -1767,7 +1768,7 @@ code.doc { } .minisandbox__output { - width: 328px; + width: 255px; background-color: #faf8ec; } @@ -1776,3 +1777,19 @@ code.doc { background-color: #161320; } } + +::-webkit-scrollbar { + width: 10px; +} + +::-webkit-scrollbar-track{ + background: #161320; +} + +::-webkit-scrollbar-thumb{ + background: #f58065; +} + +::-webkit-scrollbar-thumb:hover{ + background: #ffb5a4; +} diff --git a/website/default.nix b/website/default.nix index ce293b97..5f017134 100644 --- a/website/default.nix +++ b/website/default.nix @@ -52,5 +52,6 @@ in ln -s ${brand.logo.full.svg { color = "bright_yellow"; }} $out/assets/landing_logo.svg ln -s ${opti (setMay pkgsJS.haskell.packages.${util.compilerjs}.Shpadoinkle-website "run")}/bin/run.jsexe/${file} $out/all.min.js ${setMay pkgs.haskell.packages.${compiler}.Shpadoinkle-website "disembodied"}/bin/disembodied -o $out + cp $out/404/index.html $out/404.html echo ${pkgs.lib.commitIdFromGitRepo ../.git} > $out/version '' -- GitLab From 73946bbde018951d55a533eea9611c737c156101 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Wed, 19 May 2021 19:41:32 -0600 Subject: [PATCH 106/116] I think it's probably ready --- isreal/README.md | 4 +- isreal/swan/swan.cabal | 2 + router/Shpadoinkle/Router/Client.hs | 5 ++ .../Website/Component/LiveExample.hs | 4 +- website/Shpadoinkle/Website/Page/Home.hs | 2 +- website/Shpadoinkle/Website/Run.hs | 9 ++- website/assets/style.css | 62 +++++++++++++++++-- website/todo.example | 2 +- 8 files changed, 78 insertions(+), 12 deletions(-) diff --git a/isreal/README.md b/isreal/README.md index 220a11d4..31432242 100644 --- a/isreal/README.md +++ b/isreal/README.md @@ -37,9 +37,11 @@ You can send this to Isreal Swan with the following `curl` command: ```bash curl -X POST -H "Content-Type:application/octet-stream" --data-binary @Hello.hs \ - https://isreal.shpadoinkle.org/compile/hello-token + https://isreal.shpadoinkle.org/compile/hello-token?nonce=0 ``` +You should increment the nonce for browser cache busting. + Notice **hello-token** in the URL. It's _on you_ to make this a unique token for your work, as the system is open-ended (any arbitrary (uri encoded) string is fine). Reusing this token will result in incremental rebuilds, which diff --git a/isreal/swan/swan.cabal b/isreal/swan/swan.cabal index 4d5a7fb9..91cf4d76 100644 --- a/isreal/swan/swan.cabal +++ b/isreal/swan/swan.cabal @@ -25,6 +25,8 @@ executable swan , ease , these , mtl + , stm + , unliftio , jsaddle , ghcjs-base , ghcjs-dom diff --git a/router/Shpadoinkle/Router/Client.hs b/router/Shpadoinkle/Router/Client.hs index f9799e2a..6a6877c9 100644 --- a/router/Shpadoinkle/Router/Client.hs +++ b/router/Shpadoinkle/Router/Client.hs @@ -12,6 +12,7 @@ module Shpadoinkle.Router.Client ( runXHR , runXHR' + , runXHRe , module Servant.Client.JS ) where @@ -57,3 +58,7 @@ runXHR m = do -- TODO cache the base url or make it optional -- | Run the ClientM from Servant as an XHR request with a customized base URL. runXHR' :: ClientM a -> ClientEnv -> JSM a runXHR' m env = either (liftIO . throwM) pure =<< runClientM m env + + +runXHRe :: ClientM a -> ClientEnv -> JSM (Either ClientError a) +runXHRe = runClientM diff --git a/website/Shpadoinkle/Website/Component/LiveExample.hs b/website/Shpadoinkle/Website/Component/LiveExample.hs index c7e23210..fd40bbc7 100644 --- a/website/Shpadoinkle/Website/Component/LiveExample.hs +++ b/website/Shpadoinkle/Website/Component/LiveExample.hs @@ -41,7 +41,7 @@ import Shpadoinkle (Continuation, Html, readTVarIO, text, writeTVar) import Shpadoinkle.Html (br'_, class', div, - iframe, + div', iframe, mkGlobalMailboxAfforded, onInput, src) import Shpadoinkle.Isreal.Types as Swan @@ -79,7 +79,7 @@ example lmutex (Example cc token nonce state') = div [ class' minisandbox ] <> pack (show $ nonce - 1) ] [] EError e -> errorMessages e - ELoading -> text "Loading..." + ELoading -> div' [ class' lds_dual_ring ] ] ] ] diff --git a/website/Shpadoinkle/Website/Page/Home.hs b/website/Shpadoinkle/Website/Page/Home.hs index 8133edbc..ce703802 100644 --- a/website/Shpadoinkle/Website/Page/Home.hs +++ b/website/Shpadoinkle/Website/Page/Home.hs @@ -125,7 +125,7 @@ featureSection :: forall m a. MonadJSM m => Html m a featureSection = div [ class' feature__section ] [ div [ class' feature__wrapper ] [ p [ class' feature__title ] - [ "A programming model for declarative, high performance user interface." ] + [ "A programming model for declarative user interface." ] , div [ class' feature__cards ] $ featureCard <$> features ] , div [ class' feature_button ] diff --git a/website/Shpadoinkle/Website/Run.hs b/website/Shpadoinkle/Website/Run.hs index 3c077e9a..5956c19c 100644 --- a/website/Shpadoinkle/Website/Run.hs +++ b/website/Shpadoinkle/Website/Run.hs @@ -46,7 +46,8 @@ import Shpadoinkle.Router.Client (BaseUrl (..), ClientEnv (..), ClientM, Scheme (Https), - client, runXHR') + client, runXHR', + runXHRe) import Shpadoinkle.Widgets.Types (Search (..)) #ifndef ghcjs_HOST_OS import Shpadoinkle.Router.Server (serveUI) @@ -94,7 +95,11 @@ hoogleEnv = ClientEnv $ BaseUrl Https "hoogle.shpadoinkle.org" 443 "" instance Swan App where - compile t n c = liftJSM $ runXHR' (compileM t n c) isrealEnv + compile t n c = do + res <- liftJSM $ runXHRe (compileM t n c) isrealEnv + case res of + Left _ -> compile t n c + Right x -> pure x clean t = liftJSM $ runXHR' (cleanM t) isrealEnv diff --git a/website/assets/style.css b/website/assets/style.css index a0eb9863..c9916fbb 100644 --- a/website/assets/style.css +++ b/website/assets/style.css @@ -247,6 +247,9 @@ video { .container { max-width: 1024px; } + .minisandbox{ + max-width: 66%; + } } @media (min-width: 1280px) { .container { @@ -1726,6 +1729,7 @@ code.doc { .minisandbox__output { background-color: #faf8ec; + position: relative; } .minisandbox__output__header { @@ -1737,7 +1741,7 @@ code.doc { .minisandbox__output__content { color: #21232a; height: 91px; - overflow: scroll; + overflow: auto; } @media screen and (min-width: 640px) { @@ -1760,11 +1764,11 @@ code.doc { } .minisandbox__code__content { - height: 100%; + height: calc(100% - 48px); } .minisandbox__output__content { - height: 100%; + height: calc(100% - 48px); } .minisandbox__output { @@ -1779,17 +1783,65 @@ code.doc { } ::-webkit-scrollbar { - width: 10px; + width: 8px; + height: 8px; } -::-webkit-scrollbar-track{ +::-webkit-scrollbar-track, ::-webkit-scrollbar-corner{ background: #161320; } ::-webkit-scrollbar-thumb{ background: #f58065; + border-radius: 1000px; } ::-webkit-scrollbar-thumb:hover{ background: #ffb5a4; } + +.lds-dual-ring { + display: inline-block; + width: 80px; + height: 80px; + position: absolute; + top: 50%; + left: 50%; + margin-top: -40px; + margin-left: -40px; +} +.lds-dual-ring:after { + content: " "; + display: block; + width: 64px; + height: 64px; + margin: 8px; + border-radius: 50%; + border: 6px solid #f58065; + border-color: transparent #f58065 transparent #f58065; + animation: lds-dual-ring 1.2s linear infinite; +} +@keyframes lds-dual-ring { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} +iframe, .errors{ + padding: 1rem; + max-width: 100%; + height: 100%; +} +iframe input { + max-width: 50px; +} +.error { + padding: 1rem; + font-size: 14px; + border: 1px dotted red; + white-space: pre; + font-family: monospace; + overflow: auto; +} diff --git a/website/todo.example b/website/todo.example index 74c0c7e5..ab5233db 100644 --- a/website/todo.example +++ b/website/todo.example @@ -4,7 +4,7 @@ data State = State } deriving (Eq, Generic, NFData) appendItem state = state - & #items %~ (++ [state ^. #input]) + & #items %~ (++ [state ^. #input | state ^. #input /= ""]) & #input .~ "" view state = div_ -- GitLab From 304eba20d49cc1da800e6b1e18fdbeddc9c3f82a Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 20 May 2021 16:54:05 -0600 Subject: [PATCH 107/116] typo --- website/Shpadoinkle/Website/View.hs | 36 +++++++++++++---------------- website/docs/tutorial.adoc | 6 +++-- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/website/Shpadoinkle/Website/View.hs b/website/Shpadoinkle/Website/View.hs index dbf14232..18fc0ec8 100644 --- a/website/Shpadoinkle/Website/View.hs +++ b/website/Shpadoinkle/Website/View.hs @@ -14,9 +14,7 @@ module Shpadoinkle.Website.View where -import Control.Lens ((%~), (&), (.~), - (^.)) -import Control.Monad (void) +import Control.Lens ((%~), (^.), (^?)) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Reader import Data.Generics.Labels () @@ -34,7 +32,6 @@ import Shpadoinkle.Website.Partials.Template import Shpadoinkle.Website.Style (documentation) import Shpadoinkle.Website.Types.CurrentYear import Shpadoinkle.Website.Types.Effects.Example -import Shpadoinkle.Website.Types.Example (Example) import Shpadoinkle.Website.Types.Home import Shpadoinkle.Website.Types.PageModel import Shpadoinkle.Website.Types.Route @@ -66,18 +63,21 @@ view cy vm = div [ class' $ topClass cur ] . wrapper cy (vm ^. #mobileMenu) cur where cur = vm ^. #currentRoute -compileExamples :: forall m. (MonadUnliftIO m, MonadJSM m, ExampleEffects m) => Home -> m Home -compileExamples home' = runConcurrently $ do - hw <- compileWith #helloWorld - c <- compileWith #counter - t <- compileWith #todo - return $ home' & #examples . #helloWorld %~ hw - & #examples . #counter %~ c - & #examples . #todo %~ t +compileExamples :: forall m. (MonadUnliftIO m, MonadJSM m, ExampleEffects m) => TVar ViewModel -> m () +compileExamples model = runConcurrently $ foldl (*>) (pure ()) + [ compileWith #helloWorld + , compileWith #counter + , compileWith #todo + ] where - compileWith :: ExampleLens -> Concurrently m (Example -> Example) - compileWith l = Concurrently $ - runContinuation (compileExample l) $ home' ^. #examples . l + compileWith :: ExampleLens -> Concurrently m () + compileWith l = Concurrently $ do + vm <- readTVarIO model + case vm ^? #pageModel . #_MHome . #examples . l of + Nothing -> pure () + Just x -> do + f <- runContinuation (compileExample l) x + atomically $ modifyTVar model (#pageModel . #_MHome . #examples . l %~ f) genExampleTokens :: MonadIO m => m (Examples SnowToken) @@ -96,9 +96,5 @@ startJS startJS model r = do vm <- start r setTitle $ toSocial r ^. #title - case vm ^. #pageModel of - MHome home' -> void . async $ do - corn <- compileExamples home' - atomically . modifyTVar model $ #pageModel . #_MHome .~ corn - _ -> pure () + async $ compileExamples model return vm diff --git a/website/docs/tutorial.adoc b/website/docs/tutorial.adoc index c77cefe0..2151acc0 100644 --- a/website/docs/tutorial.adoc +++ b/website/docs/tutorial.adoc @@ -13,6 +13,8 @@ xref:getting-started.adoc[Getting Started] section. == HTML +We begin by exploring html generation in Shpadoinkle. + `Shpadoinkle Html` is an optional module for generating HTML. Here's how it works. @@ -32,8 +34,8 @@ will render as ---- Each HTML tag's associated function accepts 2 arguments: first, a list of the -properties (of type `[(Text, Prop a)]`); and second, a list of the children -HTML tags (of type `[Html a]`). +properties (of type `[(Text, Prop m a)]`); and second, a list of the children +HTML tags (of type `[Html m a]`). Each such named function has 2 additonal versions. Appending an underscore to the named function gives a version of the function that takes no properties. -- GitLab From 11f2b9d91bc55175ec84d128c2554d614dd66eb0 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 20 May 2021 16:57:26 -0600 Subject: [PATCH 108/116] bad nonce --- website/Shpadoinkle/Website/Partials/Template.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/Shpadoinkle/Website/Partials/Template.hs b/website/Shpadoinkle/Website/Partials/Template.hs index 180ec568..f10dbf72 100644 --- a/website/Shpadoinkle/Website/Partials/Template.hs +++ b/website/Shpadoinkle/Website/Partials/Template.hs @@ -62,7 +62,7 @@ headView ev vm = head_ $ , stylesheet $ highlightCDN "styles/atom-one-light.min.css" , stylesheet $(assetLink "/assets/style.css") , stylesheet "https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" - , javascript $ entrypoint ev + , javascript $ entrypoint ev <> "?_=1" , javascript $ codemirrorCDN "codemirror.min.js" , javascript $ codemirrorCDN "mode/haskell/haskell.min.js" , javascript $ highlightCDN "highlight.min.js" -- GitLab From d569a1a569ef179b10f9f0baa58ea3c7c12554dc Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 20 May 2021 17:11:20 -0600 Subject: [PATCH 109/116] restore shell --- nix/base.nix | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/nix/base.nix b/nix/base.nix index 078f0d16..0f711263 100644 --- a/nix/base.nix +++ b/nix/base.nix @@ -27,19 +27,19 @@ let docker = import ../examples/servant-crud/docker.nix { inherit compiler chan; }; - # eventlog2html = (import (pkgs.fetchFromGitHub { - # owner = "mpickering"; - # repo = "eventlog2html"; - # rev = "a18ec810328c71122ccc630fccfcea5b48c0e937"; - # sha256 = "1y24md2x4zs1vjahfljgfyvxhpk097s1mjddha4rpcf99f0djd93"; - # }) {}).eventlog2html; + eventlog2html = (import (pkgs.fetchFromGitHub { + owner = "mpickering"; + repo = "eventlog2html"; + rev = "a18ec810328c71122ccc630fccfcea5b48c0e937"; + sha256 = "064x0y3ix5h22ibl9sn3znhkan6g1prkniv2hgrrssr0c4sgjvhb"; + }) {}).eventlog2html; - eventlog2html = (import ../../eventlog2html {}).eventlog2html; + # eventlog2html = (import ../../eventlog2html {}).eventlog2html; ghcTools = with haskell.packages.${compiler}; with haskell.lib; [ easy-hls pkgs.stylish-haskell pkgs.hlint] - ++ (if enableLibraryProfiling then [ eventlog2html ] else []) + # ++ (if enableLibraryProfiling then [ eventlog2html ] else []) ++ map disableLibraryProfiling ([ cabal-install ghcid ] ++ (if enableLibraryProfiling then [ hp2pretty ghc-prof-flamegraph ] else [])); -- GitLab From e7b802f06eb7e5a153a83e274478babb78ce5332 Mon Sep 17 00:00:00 2001 From: Morgan Thomas Date: Thu, 20 May 2021 19:53:26 -0400 Subject: [PATCH 110/116] upgrade servant-client-js to support network error handling --- nix/overlay-shpadoinkle.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index 6321b3de..6571a8ab 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -52,8 +52,8 @@ servant-client-js-src = super.fetchFromGitHub { owner = "morganthomas"; repo = "servant-client-js"; - rev = "c61e99ef6688eae463eea322ad43cb5fe6aa7fed"; - sha256 = "1al0wlvy44s13zd7jlxiw2939kpr44r05zgd5kz6gcv1ppw7gz2d"; + rev = "fc440f61656641a18e3f469a13db29840c586173"; + sha256 = "0mjwnfpkajajq55vf3pxzawhmsb1azldzakclsb4yn23dlpvl23h"; }; -- GitLab From 32c271037e7c364613318364ee9083a33f36ba80 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 20 May 2021 18:22:29 -0600 Subject: [PATCH 111/116] just don't profile for now --- .gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2aa647b4..2d0d770f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -86,28 +86,28 @@ Dev Shell GHC 8.6.5 on Linux: needs: - Packages GHC 8.6.5 on Linux script: - - nix-shell --run "echo works" --arg enableLibraryProfiling true --fallback + - nix-shell --run "echo works" --arg enableLibraryProfiling false --fallback Dev Shell GHCJS 8.6 on Linux: stage: Linux Shell needs: - Packages GHCJS 8.6 on Linux script: - - nix-shell --run "echo works" --arg enableLibraryProfiling true --arg isJS true --fallback + - nix-shell --run "echo works" --arg enableLibraryProfiling false --arg isJS true --fallback Dev Shell GHC 8.6.5 on Darwin: stage: Darwin Shell needs: - Packages GHC 8.6.5 on Darwin script: - - nix-build --run "echo works" --arg enableLibraryProfiling true --argstr system x86_64-darwin --fallback + - nix-build --run "echo works" --arg enableLibraryProfiling false --argstr system x86_64-darwin --fallback Dev Shell GHCJS 8.6 on Darwin: stage: Darwin Shell needs: - Packages GHCJS 8.6 on Darwin script: - - nix-shell --run "echo works" --arg enableLibraryProfiling true --argstr system x86_64-darwin --arg isJS true --fallback + - nix-shell --run "echo works" --arg enableLibraryProfiling false --argstr system x86_64-darwin --arg isJS true --fallback Website: stage: Documentation -- GitLab From b74781e3eec8c55c8cf64c3a5d84c700ba76b8db Mon Sep 17 00:00:00 2001 From: Morgan Thomas Date: Thu, 20 May 2021 20:24:08 -0400 Subject: [PATCH 112/116] handle network errors by throwing an exception in streaming requests --- nix/overlay-shpadoinkle.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index 6571a8ab..0596aff2 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -52,8 +52,8 @@ servant-client-js-src = super.fetchFromGitHub { owner = "morganthomas"; repo = "servant-client-js"; - rev = "fc440f61656641a18e3f469a13db29840c586173"; - sha256 = "0mjwnfpkajajq55vf3pxzawhmsb1azldzakclsb4yn23dlpvl23h"; + rev = "07a351f0e2d45b3843bf7fdd8c91f82fe5f3599b"; + sha256 = "1cg4nbmm4p7h52hbwg12hrz9j25jspkv80dqpmny5ndbjgr47smi"; }; -- GitLab From d0ca8afb45055a688bbdbe06127e36ea713c19b8 Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Thu, 20 May 2021 18:38:16 -0600 Subject: [PATCH 113/116] no parcel --- nix/base.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/base.nix b/nix/base.nix index 0f711263..78ea2663 100644 --- a/nix/base.nix +++ b/nix/base.nix @@ -106,7 +106,7 @@ let inherit withHoogle; packages = _: if pack == "all" then attrValues packages else [ packages.${pack} ]; COMPILER = util.compilerjs; - buildInputs = ghcTools ++ [ asciidoctor ack util.cannibalize nixops nodePackages.parcel-bundler ]; + buildInputs = ghcTools ++ [ asciidoctor ack util.cannibalize nixops ]; shellHook = '' cat ${../etc/figlet} ''; -- GitLab From 3822a1b00ece3308839dbbcff0c70bd0a6b41b1e Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Fri, 21 May 2021 10:56:16 -0600 Subject: [PATCH 114/116] profile --- .gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2d0d770f..2aa647b4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -86,28 +86,28 @@ Dev Shell GHC 8.6.5 on Linux: needs: - Packages GHC 8.6.5 on Linux script: - - nix-shell --run "echo works" --arg enableLibraryProfiling false --fallback + - nix-shell --run "echo works" --arg enableLibraryProfiling true --fallback Dev Shell GHCJS 8.6 on Linux: stage: Linux Shell needs: - Packages GHCJS 8.6 on Linux script: - - nix-shell --run "echo works" --arg enableLibraryProfiling false --arg isJS true --fallback + - nix-shell --run "echo works" --arg enableLibraryProfiling true --arg isJS true --fallback Dev Shell GHC 8.6.5 on Darwin: stage: Darwin Shell needs: - Packages GHC 8.6.5 on Darwin script: - - nix-build --run "echo works" --arg enableLibraryProfiling false --argstr system x86_64-darwin --fallback + - nix-build --run "echo works" --arg enableLibraryProfiling true --argstr system x86_64-darwin --fallback Dev Shell GHCJS 8.6 on Darwin: stage: Darwin Shell needs: - Packages GHCJS 8.6 on Darwin script: - - nix-shell --run "echo works" --arg enableLibraryProfiling false --argstr system x86_64-darwin --arg isJS true --fallback + - nix-shell --run "echo works" --arg enableLibraryProfiling true --argstr system x86_64-darwin --arg isJS true --fallback Website: stage: Documentation -- GitLab From 80fb7fd706319b7914e1ca54f8bc780ad82fb421 Mon Sep 17 00:00:00 2001 From: Morgan Thomas Date: Wed, 26 May 2021 13:49:39 -0400 Subject: [PATCH 115/116] also catch exceptions thrown by fetch --- nix/overlay-shpadoinkle.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index 0596aff2..bd8cac86 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -52,8 +52,8 @@ servant-client-js-src = super.fetchFromGitHub { owner = "morganthomas"; repo = "servant-client-js"; - rev = "07a351f0e2d45b3843bf7fdd8c91f82fe5f3599b"; - sha256 = "1cg4nbmm4p7h52hbwg12hrz9j25jspkv80dqpmny5ndbjgr47smi"; + rev = "c968734f9f03038e3c5a0ed198c9bc752b86aac9"; + sha256 = "0s1sf930r8qsq7sl3614pd974mmnqwy6w81s9x8qkpjiwgwc18j9"; }; -- GitLab From dbf63bee98a8944e1c6b3c6de832307f87c70af0 Mon Sep 17 00:00:00 2001 From: Morgan Thomas Date: Wed, 26 May 2021 14:21:27 -0400 Subject: [PATCH 116/116] firm up servant-client-js exception handling --- nix/overlay-shpadoinkle.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nix/overlay-shpadoinkle.nix b/nix/overlay-shpadoinkle.nix index bd8cac86..1c0fef0b 100644 --- a/nix/overlay-shpadoinkle.nix +++ b/nix/overlay-shpadoinkle.nix @@ -52,8 +52,8 @@ servant-client-js-src = super.fetchFromGitHub { owner = "morganthomas"; repo = "servant-client-js"; - rev = "c968734f9f03038e3c5a0ed198c9bc752b86aac9"; - sha256 = "0s1sf930r8qsq7sl3614pd974mmnqwy6w81s9x8qkpjiwgwc18j9"; + rev = "56cf74cd71f7164e0c45d6caab4e53535932ad7d"; + sha256 = "06i0dmfqq3c2wdxqb1p3vqkiw8094k1q930g7hrfggv2ps36jhf1"; }; -- GitLab