From c1c4996e632cd950f24c0bfc2a67dc636a35435c Mon Sep 17 00:00:00 2001 From: David Himmelstrup Date: Sat, 22 Aug 2020 10:35:25 +0800 Subject: [PATCH] Simplify playground code and add snippets. (#136) * Add instructions for how to run the playground locally. * Make it easier to select backend in the elm code. * Use scrolling for long scripts. * Fix bug, send code to backend on connect, don't auto play on code changes. * Compile and cache snippets. --- .github/workflows/gh-pages.yml | 6 +- reanimate-playground/README.md | 19 ++ reanimate-playground/hie.yaml | 3 +- reanimate-playground/playground.cabal | 1 + reanimate-playground/src/Main.hs | 59 +++++- .../viewer-elm/dist/index.html | 86 ++++++-- .../viewer-elm/dist/playground.js | 25 ++- .../viewer-elm/dist/snippets.js | 1 + .../viewer-elm/dist/style.css | 22 ++ reanimate-playground/viewer-elm/src/Main.elm | 200 +++--------------- 10 files changed, 219 insertions(+), 203 deletions(-) create mode 100644 reanimate-playground/viewer-elm/dist/snippets.js diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index c9c1638..deb525b 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -82,7 +82,11 @@ jobs: - name: Playground run: | CWD=`pwd` - cd reanimate-playground/viewer-elm + stack build + cd reanimate-playground + stack build + stack exec --cwd ../ playground snippets reanimate-playground/snippets > viewer-elm/dist/snippets.js + cd viewer-elm npm install npm run build mkdir $CWD/hpc/playground diff --git a/reanimate-playground/README.md b/reanimate-playground/README.md index ade555a..b962e23 100644 --- a/reanimate-playground/README.md +++ b/reanimate-playground/README.md @@ -18,3 +18,22 @@ Test against: +# How to run locally: + +## Backend + +``` +stack build +stack exec --cwd ../ playground +``` + +## Frontend + +By default the frontend will use the backend running at reanimate.clozecards.com. +To switch to a local backend, change 'backend' in Main.elm to 'Local'. + +``` +cd viewer-elm +npm install +npm run dev-server +``` diff --git a/reanimate-playground/hie.yaml b/reanimate-playground/hie.yaml index 7e844f0..8892596 100644 --- a/reanimate-playground/hie.yaml +++ b/reanimate-playground/hie.yaml @@ -1,3 +1,2 @@ cradle: - stack: - component: "playground" + stack: {component: "playground"} diff --git a/reanimate-playground/playground.cabal b/reanimate-playground/playground.cabal index bf9d5d7..e140602 100644 --- a/reanimate-playground/playground.cabal +++ b/reanimate-playground/playground.cabal @@ -23,6 +23,7 @@ executable playground githash -any, time -any, haskell-src-exts -any, + temporary, websockets -any, warp, wai-app-static diff --git a/reanimate-playground/src/Main.hs b/reanimate-playground/src/Main.hs index d351669..66e6b7e 100644 --- a/reanimate-playground/src/Main.hs +++ b/reanimate-playground/src/Main.hs @@ -34,6 +34,7 @@ import System.Directory import System.Environment import System.Exit import System.FilePath +import System.IO.Temp import System.IO import System.Process import System.Timeout @@ -83,6 +84,22 @@ main = do args <- getArgs case args of ["test"] -> putStrLn "Test OK" + ["snippets", folder] -> do + files <- sort <$> getDirectoryContents folder + ghci <- takeMVar (backendGhci backend) + snippets <- mapM (genSnippet ghci) + [ folder file + | file <- files, takeExtension file == ".hs" ] + putStr "const snippets = " + putStrLn $ + "[" ++ intercalate "," + [ "{" ++ + "\"title\": " ++ show title ++ "," ++ + "\"url\": " ++ show url ++ "," ++ + "\"code\": " ++ show inp ++ + "}" + | (title, url, inp) <- snippets ] ++ + "];" [] -> do root <- cacheDir tid <- forkIO $ run 10162 (staticApp $ defaultWebAppSettings root) @@ -91,6 +108,23 @@ main = do hPutStrLn stderr "Invalid arguments" exitWith (ExitFailure 1) +genSnippet :: Ghci -> FilePath -> IO (String, String, Text) +genSnippet ghci path = do + inp <- T.readFile path + let ParseOk m = parseHaskell inp + h = sourceHash m + withHaskellFile m $ \hsFile -> do + _ <- reqGhcOutput ghci $ ":load " ++ hsFile + out <- reqGhcOutput ghci "Reanimate.duration animation" + let dur = read (unlines out) :: Double + frames = round (dur * fromIntegral frameRate) :: Int + url = "https://reanimate.clozecards.com/" ++ h ++ "/" ++ show (frames `div` 2) ++ ".svg" + title = takeWhileEnd (/= '_') (takeBaseName path) + return (title, url, inp) + where + takeWhileEnd f = reverse . takeWhile f . reverse + + opts :: ConnectionOptions opts = defaultConnectionOptions { connectionCompressionOptions = PermessageDeflateCompression defaultPermessageDeflate } @@ -189,10 +223,10 @@ requestRender backend render = do newGhci :: IO Ghci newGhci = do let fastProc = proc "stack" ["exec", "ghci", "--rts-options="++memoryLimit] - (fastGhci, _loads) <- startGhciProcess fastProc (\_stream msg -> putStrLn msg) + (fastGhci, _loads) <- startGhciProcess fastProc (\_stream msg -> hPutStrLn stderr msg) - void $ exec fastGhci "import qualified Reanimate" - void $ exec fastGhci "import qualified Reanimate.Render as Reanimate" + void $ reqGhcOutput fastGhci "import qualified Reanimate" + void $ reqGhcOutput fastGhci "import qualified Reanimate.Render as Reanimate" return fastGhci newBackend :: IO Backend @@ -201,8 +235,7 @@ newBackend = do queue <- newEmptyMVar tid <- forkIO $ forever $ do req <- takeMVar queue - guardWanted req $ do - hs <- writeHaskellFile (renderCode req) + guardWanted req $ withHaskellFile (renderCode req) $ \hs -> do ghci <- readMVar ghciRef guardGhci req ghci (":load " ++ hs) $ \_ -> guardWanted req $ guardGhci req ghci "Reanimate.duration animation" $ \out -> do @@ -247,8 +280,9 @@ cacheDir = do createDirectoryIfMissing True root return root -writeHaskellFile :: Module SrcSpanInfo -> IO FilePath -writeHaskellFile m = do +withHaskellFile :: Module SrcSpanInfo -> (FilePath -> IO a) -> IO a +withHaskellFile m action = withSystemTempFile "playground.hs" $ \target h -> do + hClose h T.writeFile target "{-# LANGUAGE OverloadedStrings #-}\n" T.appendFile target "module Animation where\n" T.appendFile target "import Reanimate\n" @@ -262,9 +296,7 @@ writeHaskellFile m = do T.appendFile target "import Control.Lens\n" T.appendFile target "import Codec.Picture.Types\n" T.appendFile target $ T.pack $ prettyPrint m - return target - where - target = "playground.hs" + action target splitGhciOutput :: Ghci -> String -> IO ([String], [String]) splitGhciOutput ghci cmd = do @@ -276,6 +308,13 @@ splitGhciOutput ghci cmd = do Stderr -> modifyIORef err (++[msg]) (,) <$> readIORef err <*> readIORef out +reqGhcOutput :: Ghci -> String -> IO [String] +reqGhcOutput ghci cmd = do + (err, out) <- splitGhciOutput ghci cmd + unless (null err) $ + error (unlines err) + return out + logMsg :: String -> IO () logMsg msg = do now <- getCurrentTime diff --git a/reanimate-playground/viewer-elm/dist/index.html b/reanimate-playground/viewer-elm/dist/index.html index 15c539b..8df8341 100644 --- a/reanimate-playground/viewer-elm/dist/index.html +++ b/reanimate-playground/viewer-elm/dist/index.html @@ -22,6 +22,7 @@ + @@ -41,10 +42,6 @@
-
@@ -60,7 +57,10 @@
- +
+ + +
@@ -85,6 +85,45 @@ + +