3 import Control.Monad.Trans.Class qualified as MT
4 import Control.Monad.Trans.State.Strict qualified as MT
5 import Data.ByteString.Builder qualified as Builder
6 import Data.List qualified as List
7 import Data.Map.Strict qualified as Map
8 import Data.Text qualified as Text
9 import Data.Text.Short qualified as ShortText
10 import Graph.DOT qualified as DOT
12 import Text.Blaze.Renderer.Utf8 qualified as Blaze
13 import Text.Blaze.XHtml1.FrameSet qualified as H
14 import Text.Blaze.XHtml1.FrameSet.Attributes qualified as H
15 import Worksheets.Utils.HTML qualified as HTML
16 import Worksheets.Utils.Prelude
20 type Quantity = ShortText
23 , recipeFunctions :: [(Name, Function)]
26 = TermDescr {termQuantity :: Quantity}
29 , termRefPort :: Maybe Name
30 , termRefQuantity :: Maybe Quantity
33 termToShortText :: Term -> ShortText
34 termToShortText = \case
36 TermRef{..} -> termRefName <> (termRefPort & maybe "" (\p -> ":" <> p))
38 instance IsString Term where
39 fromString = TermDescr . ShortText.pack
40 data Function = Function
41 { functionInputs :: Map Name Term
42 , functionSteps :: [ShortText]
43 , functionOutputs :: Map Name Type
46 data RecipeState = RecipeState
47 { recipeStateStep :: Natural
49 recipeDOT :: Recipe -> Builder.Builder
50 recipeDOT Recipe{..} = (`MT.evalState` RecipeState{recipeStateStep = 1}) $ DOT.runDOT $ do
51 DOT.dotLine "digraph g"
53 DOT.dotLine "size=\"8.267,11.7!\""
54 DOT.dotLine "margin=0"
55 DOT.dotLine "nodesep=10"
56 DOT.dotLine "ranksep=10"
57 DOT.dotLine "rankdir=TD"
58 DOT.dotLine "//fontnames=\"DejaVu Sans Mono,\""
59 DOT.dotLine "splines=true"
60 DOT.dotLine "overlap=false"
61 DOT.dotLine "stylesheet=\"recipe.css\""
62 DOT.dotLine "ratio=\"fill\""
63 DOT.dotLine "//graph [layout=\"dot\" fontname=\"helvetica\"];"
64 DOT.dotLine "node [fontsize=400 shape=\"box\" style=\"plaintext\" fontname=\"Linux Libertine Capitals\"];"
65 DOT.dotLine "edge [color=\"#aaaaaa\" fontcolor=blue penwidth=30 arrowhead=open arrowsize=20];"
66 forM_ (recipeFunctions & ol1) \(functionNum :: Natural, (functionName, Recipes.Function{..})) -> do
67 -- step <- MT.lift $ MT.lift $ MT.state \st ->
68 -- ( recipeStateStep st
69 -- , st{recipeStateStep = recipeStateStep st + fromIntegral (List.length functionSteps) }
72 (functionName & DOT.builderQuotedShortText)
73 [ "label" := DOT.ValueHTML $ Blaze.renderMarkupBuilder $ do
79 ! H.customAttribute "cellborder" "0"
80 ! H.customAttribute "rows" "*"
81 -- ! H.customAttribute "columns" "*"
88 ! H.customAttribute "cellborder" "0"
91 xhtml1_text functionNum
92 ! H.customAttribute "color" "blue"
93 xhtml1_text (". " :: ShortText)
94 xhtml1_text functionName
95 ! H.customAttribute "color" "black"
96 forM_ (functionInputs & Map.toList) \(inputName, inputValue) -> do
100 ! H.customAttribute "port" (inputName & ShortText.unpack & ("input-" <>) & toValue)
108 -- ! H.customAttribute "fixedsize" "true"
111 TermDescr{termQuantity} | termQuantity /= "" -> do
112 -- !? ( case inputValue of
113 -- TermDescr{} -> (False, H.customAttribute "port" "")
114 -- TermRef{..} -> (True, termRefPort & maybe (H.customAttribute "port" "") (H.customAttribute "port" . toValue . ShortText.unpack))
115 -- -- " PORT="<>ShortText.unpack termRefPort
116 -- --(isJust termRefPort, H.customAttribute "port" (termRefPort & fromMaybe ""))
118 xhtml1_quantity termQuantity
119 TermRef{..} | termRefQuantity & isJust -> do
120 termRefQuantity & maybe "" \q -> do
122 _ -> xhtml1_quantity "1×"
131 xhtml1_text inputName
134 forM_ (functionSteps & ol1) \(stepNum :: Natural, stepName) -> do
142 ! H.bgcolor "lightyellow"
145 xhtml1_text (" " :: ShortText)
146 xhtml1_text (functionNum & show & ShortText.pack)
147 ! H.customAttribute "color" "blue"
148 xhtml1_text ("." :: ShortText)
149 xhtml1_text (stepNum & show & ShortText.pack)
150 xhtml1_text (". " :: ShortText)
155 & List.intersperse H.br
157 xhtml1_text @ShortText " "
158 forM_ (functionOutputs & Map.toList) \(outputName, outputValue) -> do
171 xhtml1_quantity outputValue
179 xhtml1_text outputName
182 ! H.customAttribute "port" (outputName & ShortText.unpack & ("output-" <>) & toValue)
184 forM_ recipeFunctions \(functionName, Recipes.Function{..}) -> do
185 forM_ (functionInputs & Map.toList) \(inputName, inputTerm) -> do
187 TermDescr{} -> return ()
190 [ [ [termRefName & DOT.builderQuotedShortText]
191 , termRefPort & maybeToList <&> (("output-" <>) >>> DOT.builderQuotedShortText)
195 & List.intersperse (Builder.charUtf8 ':')
197 , [ [functionName & DOT.builderQuotedShortText]
198 , [inputName & ("input-" <>) & DOT.builderQuotedShortText]
202 & List.intersperse (Builder.charUtf8 ':')
212 -- ! H.customAttribute "fixedsize" "true"
215 ! H.customAttribute "face" "Liberation Sans"
216 ! H.customAttribute "point-size" "200"
220 xhtml1_text :: HTML.ToMarkup s => s -> _
223 ! H.customAttribute "face" "Linux Libertine Capitals"
224 ! H.customAttribute "point-size" "200"
228 xhtml1_quantity :: ShortText -> _
229 xhtml1_quantity "" = return ()
232 ! H.customAttribute "face" "Courier"
233 ! H.customAttribute "point-size" "200"
239 -- <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
240 -- <TR><TD ALIGN="LEFT" BGCOLOR="white">☐ 70g SucreSemoule</TD></TR>