Week 03 of 2019

Development log of Elm Tree Workshop

30 items
  1. Correction
  2. Move "We are still working ..." note to the top
  3. Draft section about dot function on day 3
  4. Draft section about line function on day 3
  5. Remove SVG gradients from curriculum for day 3
  6. Correct typos
  7. Merge branch 'content' into content-fix
  8. Correct typos and grammatical error
  9. Style
  10. Fix typo
  11. Control oprhans in paragraphs
  12. Write first section of day 4 - manual tree
  13. Start writing Rule Based Tree
  14. Upgrade mdgriffith/elm-markup to 2.0.6
  15. Make the Dot example work, add fill property to Container
  16. Merge branch 'content' into wip-dots-example
  17. Add a little spacing between lines in the Terminal block
  18. Write the section about rules based tree on day 4
  19. Update day-2 to use new Dots block.
  20. Merge remote-tracking branch 'origin/wip-dots-example' into content
  21. Write section about halting on day 4
  22. Write section about segments growing on day 4
  23. Add congratulations block to day 4
  24. Fix mistakes on day 5 header
  25. Fix bug in halting condition of segment (day 4)
  26. Correct link from day 4 to day 5
  27. Draft day 5
  28. Improve Fana's bio.
  29. Replaced old tree example with new tree
  30. Replace Code block with a new Edito block supporting highlits

Correction

On by Tadeusz Łazurski

index 67e6def..ae30437 100644
--- a/content/day-3.txt
+++ b/content/day-3.txt
@@ -321,7 +321,7 @@ Take your time. Here are some hints to help you:
=
=    #. Names are given to values wherever you see a {Code|=} sign. The name is on the left and the value is below and slightly to the right. In our code we are giving only one name and it's value is quite complex. In fact the whole program is about computing this value. Try to find it.
=
-    #. Functions are called wherever you see a name that have any expression to the right (or below and right) of it (like: {Code|fun "learning Elm together"}), or that stand to the left of the {Code|\|>} (pipe operator; like: {Code|"learning Elm together" \|> fun}). The expression to the right of the function (or left of pipe) is called /the argument/. Sometimes an argument is a complex expression. Some functions take more than one argument (e.g. {Code|Svg.svg} takes 2 and the second one is very complex), and sometimes one is on the right and the other comes through the pipe 🤕. There is a lot of functions called here (36 to be precise) and most are prefixed with a name of the module that exposed them to us.
+    #. Functions are called wherever you see a name that have any expression to the right (or below and right) of it (like: {Code|fun "learning Elm together"}), or that stand to the left of the {Code|\|>} (pipe operator; like: {Code|"learning Elm together" \|> fun}). The expression to the right of the function (or left of pipe) is called /the argument/. Sometimes an argument is a complex expression. Some functions take more than one argument (e.g. {Code|Svg.svg} takes 2 and the second one is very complex), and sometimes one is on the right and the other comes through the pipe 🤕. There is a lot of functions called here (36 to be precise) and all of them are prefixed with a name of the module that exposed them to us.
=
=    #. Interestingly there are only two names exposed by a module that do not refer to a function. Try to find them!
=| Note

Move "We are still working ..." note to the top

On by Tadeusz Łazurski

index ae30437..ab7167f 100644
--- a/content/day-3.txt
+++ b/content/day-3.txt
@@ -4,6 +4,11 @@
=| Emphasize
=    Connecting the Dots
=
+| Note
+    *We are still working on this content.*
+
+    Stay tuned {Icon|name=radio}
+
=| Note
=    So far we have been writing the code of our program without thinking too much about what it means. Today we are going to learn about
=
@@ -240,13 +245,10 @@ We don't have to create all the functions that we need. There are thousands of t
=
=And then use any name that it exposes, like this
=
-| Note
-    *We are still working on this content.*
=| Terminal
=    > String.fromInt 10
=    "10" : String
=
-    Stay tuned {Icon|name=radio}
=If the name comes from an imported module, you need to prefix it with the name of the module, like we did above.
=
=Let's look at our code and try to recognize some literal values, lists, names and functions. Here is how it should look after day 2:
@@ -324,10 +326,7 @@ Take your time. Here are some hints to help you:
=    #. Functions are called wherever you see a name that have any expression to the right (or below and right) of it (like: {Code|fun "learning Elm together"}), or that stand to the left of the {Code|\|>} (pipe operator; like: {Code|"learning Elm together" \|> fun}). The expression to the right of the function (or left of pipe) is called /the argument/. Sometimes an argument is a complex expression. Some functions take more than one argument (e.g. {Code|Svg.svg} takes 2 and the second one is very complex), and sometimes one is on the right and the other comes through the pipe 🤕. There is a lot of functions called here (36 to be precise) and all of them are prefixed with a name of the module that exposed them to us.
=
=    #. Interestingly there are only two names exposed by a module that do not refer to a function. Try to find them!
-| Note
-    *We are still working on this content.*
=
-    Stay tuned {Icon|name=radio}
=
=
=Let's take a deeper look at how we give attributes to an SVG element.

Draft section about dot function on day 3

On by Tadeusz Łazurski

index ab7167f..0898a8a 100644
--- a/content/day-3.txt
+++ b/content/day-3.txt
@@ -121,6 +121,8 @@ Above we applied the function (by calling it's name: {Code|fun}) to a literal va
=
=    Alternatively you can think of functions as parametrized values. The value you get depends on what value you provide to it. Eggs can have different sizes . An egg painting machine would give you different painted eggs depending on what egg you put in it.
=
+    Don't worry if the concept of a function is not very clear yet. Most people have difficulty here. You will see some practical examples of functions soon.
+
=Now we see that we can get a value in three ways: (1) by literally expressing it, (2) by calling it's name and (3) by applying a function to another value.
=
=Not every function can be applied to any value. You can add two numbers (like {Code|2} and {Code|5} in {Code|2 + 5}), but you can't add a number to a string ({Code|2 + "Hello!"} - try it and see an error!). The system that governs what function can be applied to what value is called a *type system* and it plays important role in Elm programming.
@@ -327,7 +329,189 @@ Take your time. Here are some hints to help you:
=
=    #. Interestingly there are only two names exposed by a module that do not refer to a function. Try to find them!
=
+| Header
+    Let's make a function
+
+Now we know what is what. Let's consider where we have a repetitive code that we could turn into a named function. If you look at the code from a distance, you can see that there is one block that is repeated 5 times almost without a difference. I'm talking about this:
+
+| Monospace
+    Svg.circle
+        [ Svg.Attributes.r "10"
+        , Svg.Attributes.cx "0"
+        , Svg.Attributes.cy "0"
+        , Svg.Attributes.fill "..."
+        , Svg.Attributes.transform "rotate(...) translate(80)"
+        ]
+        []
+
+This block of code is repeated five times in our code - once for each dot. Now I'm going to show you how to eliminate the repetition in few steps.
+
+First let's make them even more uniform by adding {Code|rotate(0)} to the first block. It will help us later. The code should look like this:
+
+| Code
+    TODO
+
+
+
+Then let's give the value produced by this block a name. We can call it the {Code|dot}. Select the first block that you have just modified, everything between {Code|[} and the {Code|,} on line 20 and cut it with {Key|command} + {Key|x}. Then in the empty space left type {Code|dot}.
+
+Move your cursor to the end of the file and type {Code|dot =}, press {Key|enter} and paste the code you cut before with {Key|command} + {Key|v}. The code should now look like this:
+
+| Code
+    TODO
+
+Reload the browser. Everything should work exactly the same as before. One of the dots is now a named value, but for our program it makes no difference. What is important is it's value, not where it comes from.
+
+Of course the goal is to have our list of dots looking like this:
+
+| Monospace
+    [ dot
+    , dot
+    , dot
+    , dot
+    , dot
+    ]
+
+Let's try it. The effect will be that all the dots will be in the same place, one on top of another. Also they will all have the same color. So on the screen you should see only one dot, somewhat to the right of center.
+
+The dots need to have some parameters so they can be different. We already identified them: it's color and rotation. Above I said that a function is a parametrized value. Let's turn our dot definition on line {Code|TODO} into a function with two parameters like this:
+
+| Code
+    TODO: Rest of the code
+
+    dot color rotation =
+        Svg.circle
+            [ Svg.Attributes.r "10"
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill "skyblue"
+            , Svg.Attributes.transform "rotate(0) translate(80)"
+            ]
+            []
+
+This way we declared that when calling a {Code|dot} name you will provide two values: first one for {Code|color} and second for {Code|rotation}. Let's stay true to our word and provide it. Change the list on lines {Code|todo} to look like this:
+
+| Code
+    TODO: Rest of the code
+
+    [ dot "skyblue" 0
+    , dot "orange" 72
+    , dot "red" 144
+    , dot "lime" 216
+    , dot "maroon" 288
+    ]
+
+Let's reload again and see that there is still only one, skyblue dot visible. That's because we did not tell Elm what to do with the values of the two parameters we provide for the {Code|dot} function. We gave them names ({Code|color} and {Code|rotation}), but never called them.
+
+First, let's use the {Code|color} parameter. If you want to change the color of the dot, you need to pass a different value to the {Code|Svg.Attributes.fill} function on line {Code|TODO}. Currently the value is a literal string {Code|"skyblue"}. Instead we want to give it whatever value was given for the {Code|color} parameter. We do it by simply calling the name of the parameter in place of a literal value, like this:
+
+
+| Code
+    TODO: Rest of the code
+
+    dot color rotation =
+        Svg.circle
+            [ Svg.Attributes.r "10"
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform "rotate(0) translate(80)"
+            ]
+            []
+
+Now let's do the same for rotation. This one is more complex, because we cannot just pass {Code|rotation} value to the {Code|Svg.Attributes.transform} function. The {Code|rotation} is just a number, while the function expects a string formatted like this: {Code|"rotate(...) translate(80)"}.
+
+We can use the {Code|++} operator to glue the strings together. First part is constant, so we can just give it a literal value of {Code|"rotate("}. After that we put the {Code|++} and our variable part: {Code|rotation}. Finally the rest of the string is also constant: {Code|" translate(80)"} (notice the space before {Code|translate}!). All together we have:
+
+| Code
+    TODO: Rest of the code
+
+    dot color rotation =
+        Svg.circle
+            [ Svg.Attributes.r "10"
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform "rotate(" ++ rotation ++ ") translate(80)"
+            ]
+            []
+
+But Elm will complain about this. First it looks like we gave 5 arguments to the {Code|Svg.Attributes.transform} function, and it only takes one. We can fix it by putting a parentheses around the {Code|"rotate(" ++ rotation ++ ") translate(80)" }, basically telling Elm that this is a single expression and it should evaluate it's value before passing it to the {Code|Svg.Attributes.transform} function. Now it should look like this:
+
+| Code
+    TODO: Rest of the code
+
+    dot color rotation =
+        Svg.circle
+            [ Svg.Attributes.r "10"
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform ("rotate(" ++ rotation ++ ") translate(80)")
+            ]
+            []
+
+Here we face another problem. Elm complains with the following:
+
+| Monospace
+    TODO: Provide error message. Make sure the line and column numbers are correct.
+
+It's the type system in action. We can only use {Code|++} function with both arguments being the same type. Here one argument is:
+
+| Monospace
+    "rotate(" : String
+
+and another is:
+
+| Monospace
+    rotation : Float
+
+Elm won't have that. Fortunately there is an easy way to satisfy the type system. The {Code|String.fromFloat} takes a {Code|Float} and gives a {Code|String}. Try it in the REPL:
+
+| Terminal
+    > String.fromFloat
+    <function> : Float -> String
+
+    > String.fromFloat 10
+    "10" : String
+
+    > String.fromFloat 10.2
+    "10.2" : String
+
+That's what we need! Let's pass the value of {Code|rotation} through this function like this:
+
+| Code
+    TODO: Rest of the code
+
+    dot color rotation =
+        Svg.circle
+            [ Svg.Attributes.r "10"
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform ("rotate(" ++ (String.fromFloat rotation) ++ ") translate(80)")
+            ]
+            []
+
+| Code
+    IDEA: What if we use String.concat instead?
+
+    dot color rotation =
+        Svg.circle
+            [ Svg.Attributes.r "10"
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform (String.concat
+                [ "rotate("
+                , String.fromFloat rotation
+                , ") translate(80)"
+                ]
+              )
+            ]
+            []
=
+This should work! The browser should now present the dots as intended and I would argue that the code is more readable now.
=
=Let's take a deeper look at how we give attributes to an SVG element.
=

Draft section about line function on day 3

On by Tadeusz Łazurski

index 0898a8a..80cf295 100644
--- a/content/day-3.txt
+++ b/content/day-3.txt
@@ -513,6 +513,111 @@ That's what we need! Let's pass the value of {Code|rotation} through this functi
=
=This should work! The browser should now present the dots as intended and I would argue that the code is more readable now.
=
+| Header
+    Lines to Connect the Dots
+
+Armed with our functional superpowers, let's make another function that draws a line. Let me fist show you a complete code and then we can discuss it.
+
+| Code
+    line color rotation =
+        Svg.line
+            [ Svg.Attributes.strokeWidth "1"
+            , Svg.Attributes.x1 "0"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.x1 "80"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.stroke color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ")"
+                    ]
+                )
+            ]
+            []
+
+It works similar to the {Code|dot} function so you can just copy and past the it. Then apply the following changes:
+
+| List
+    #. The name needs to be different. {Code|line} seems appropriate.
+
+    #. Instead of a {Code|Svg.circle} we will call the {Code|Svg.line} function
+
+    #. The line doesn't have a radius, so remove the first attribute, {Code|Svg.Attributes.r}.
+
+    #. The line goes from one point to another, so it has four coordinates {Code|x1} and {Code|y1} for the beggening, {Code|x2} and {Code|y2} for the end. Beggening should be in the middle of the circle, which is {Code|x=0, y=0} (/the origin/). We will make the end reach to the big circle by giving the {Code|x2} value of {Code|80} (same as a radius of the circle).
+
+    #. Line can't be filled, as it has no area. Instead we give it a color using {Code|Svg.Attributes.stroke} function.
+
+    #. We do not need to translate the line. It's enough to rotate it.
+
+Once you have the function defined, let's use it. For every dot, create one line, like so:
+
+| Code
+    module Main exposing (main)
+
+    import Element
+    import Svg
+    import Svg.Attributes
+
+
+    main =
+        [ dot "skyblue" 0
+        , line "skyblue" 0
+        , dot "orange" 72
+        , line "orange" 72
+        , dot "red" 144
+        , dot "lime" 216
+        , dot "maroon" 288
+        ]
+            |> Svg.svg
+                [ Svg.Attributes.height "100%"
+                , Svg.Attributes.width "100%"
+                , Svg.Attributes.style "background: none"
+                , Svg.Attributes.viewBox "-100 -100 200 200"
+                ]
+            |> Element.html
+            |> Element.layout
+                [ Element.width Element.fill
+                , Element.height Element.fill
+                ]
+
+
+    dot color rotation =
+        Svg.circle
+            [ Svg.Attributes.r "10"
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ") translate(80)"
+                    ]
+                )
+            ]
+            []
+
+
+    line color rotation =
+        Svg.line
+            [ Svg.Attributes.strokeWidth "1"
+            , Svg.Attributes.x1 "0"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.x1 "80"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.stroke color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ")"
+                    ]
+                )
+            ]
+            []
=Let's take a deeper look at how we give attributes to an SVG element.
=
=We've already seen a few example of SVG attributes. The radius of our dot ({Code|circle}) is an attribute.

Remove SVG gradients from curriculum for day 3

On by Tadeusz Łazurski

Remove the content pieces from the bottom.

index 80cf295..15d5af6 100644
--- a/content/day-3.txt
+++ b/content/day-3.txt
@@ -17,7 +17,7 @@
=        - Functions
=        - Modules
=        - Types
-        - SVG gradients
+        - SVG lines
=
=Before we begin, let's reflect on the following.
=
@@ -618,175 +618,17 @@ Once you have the function defined, let's use it. For every dot, create one line
=                )
=            ]
=            []
-Let's take a deeper look at how we give attributes to an SVG element.
=
-We've already seen a few example of SVG attributes. The radius of our dot ({Code|circle}) is an attribute.
+You should see something like this in the browser:
=
-| Code
-     Svg.circle [ Svg.Attributes.r "10" ] []
-
-Try giving it a new value to see your dot grow or shrink.
-
-Our viewBox is also an attribute. It's an attribute of the {Code|svg} element.
-
-| Code
-    Svg.svg
-        [ Svg.Attributes.viewBox "-300 -300 600 600" ]
-        [ Svg.circle [ Svg.Attributes.r "10" ] [] ]
-
-Again, try giving it new values. As we saw earlier, the first two values (which represent the left and top of the viewBox) must be equal to the negative of the half of the last two values (which correspond to width and height). Try other combinations of values to adjust the placement of the dot. If the behavior of the viewBox is difficult to understand, refer again to the interactive tutorial above.
-
-To color our dot, we'll have to give it a second attribute. Many SVG elements take a {Code|List} of attributes. A {Code|List} is a very important concept in software development. {Code|Lists} allow us to group an arbitary number of similar values. In this case, we'll be using a {Code|List} to group {Code|Attribute a} values. Whenever you see {Code|[...]} in an Elm source code file, you are dealing with a list.
-
-Before we give a color to our dot, try to identify all the lists in the code we're writen already. There are 5. Hint: It is possible for a list to have only 1 or 0 items ({Code|[]}).
-
-| Code
-    Svg.circle [ Svg.Attributes.r "10" ] []
-
-When we take a look at the code responsible for creating our dot, we see that it is followed by two lists. We'll only need to worry about the first one at the moment. As we've discussed, the first list already contains the {Code|r} attribute of the circle, representing the radius. We'll need to add a second attribute to the circle. We use a comma ({Code|,}) to separate elements in a list.
-
-| Code
-    Svg.circle [ Svg.Attributes.r "10", ... ] []
-
-To color our circle, we'll use the {Code|fill} attribute.
-
-| Code
-    Svg.circle
-        [ Svg.Attributes.r "10"
-        , Svg.Attributes.fill "blue"
-        ]
-        []
-
-You should see this result:
-
-| Window
-    | DotWithViewBox
-        background=none
-        fill=blue
-        viewBox=-300 -300 600 600
-
-Let's use our new understanding of lists to do something a bit more interesting. Let's draw multiple dots!
-
-We can start by taking a look at our {Code|main} value:
-
-| Code
-    main =
-        Element.layout
-            [ Element.width Element.fill
-            , Element.height Element.fill
-            ]
-            (Element.html
-                (Svg.svg
-                    [ Svg.Attributes.viewBox "-500 -500 1000 1000"
-                    ]
-                    [ Svg.circle
-                        [ Svg.Attributes.r "10"
-                        , Svg.Attributes.fill "blue"
-                        ]
-                        []
-                    ]
-                )
-            )
-
-Where is our list of circles? To find it, first find the circle.
-
-| Code
-    Svg.circle
-      [ Svg.Attributes.r "10"
-      , Svg.Attributes.fill "blue"
-      ]
-      []
-
-Next, identify the first open square bracket {Code|[} before the circle.
-
-| Code
-    [ Svg.circle
-        [ Svg.Attributes.r "10"
-        , Svg.Attributes.fill "blue"
-        ]
-        []
-    ...
-
-This is where the list begins.
-
-Finally, identify the matching closing brakcet {Code|]}. This is a bit tricky because there are two lists within this list. The first is the list of attributes to the circle we saw earlier. The second is an empty list. We'll see exactly what they do in a moment. Ignore both of these for now. The matching closing bracket should be on the same indentation level as the opening bracket. Your text editor should be able to help you here. When you move your cursor before the opening bracket, the closing bracket should be highlighted.
-
-| Code
-    [ Svg.circle
-        [ Svg.Attributes.r "10"
-        , Svg.Attributes.fill "blue"
-        ]
-        []
-    ]
-
-Right now, our list of circles has one value. We want to give it a second value. The second value will look identical to the first. What is the first value?
-
-| Code
-    Svg.circle
-        [ Svg.Attributes.r "10"
-        , Svg.Attributes.fill "blue"
-        ]
-        []
-
-It might not seem obvious at first that this is all one value. But it is. {Code|Svg.circle} takes two lists as arguments. We'll see what an argument is later. For now, we will need to understand the difference between an attribute and a child.
-
-Imagine a box of colored easter eggs. One egg is painted yellow, another red, another blue. Moreover, they don't all come from the same bird. One is a small robin's egg. Another is a medium chicken egg. Another is a huge ostrich egg.
-
-Some of the elements in our image are 'things'. A box is a thing. Each egg is also a thing. Some of the elements in our image are not things. Blueness is not a thing. Medium-sizeness is not a thing. You can see blueness. You can see medium-sizeness. But they are not things. They are attributes of a thing. A thing can be blue. It can be medium-sized.
-
-It is a convention in many programming languages, including Elm and SVG, to distinguish between the attributes and children of an element. If we wanted to represent our box in SVG, we would give our box several attributes. It would have a color (maybe brown). It would have a width, length, depth. We would also give it several children. Each of the eggs within the box would be a child of the box. A box with 6 eggs would have 6 children. Each of these children (the eggs) would have different attributes: blue, red, medium-sized, etc. The children of an element are the 'things' it contains. The attributes of a thing are the elements that describe it.
-
-The first list following {Code|Svg.circle} is:
-
-| Code
-    [ Svg.Attributes.r "10"
-    , Svg.Attributes.fill "blue"
-    ]
-
-This is a list of attributes. A radius is not a 'thing'. A fill color is not a 'thing'. They are elements that describe a thing. They describe our circle.
-
-The second list is:
-
-| Code
-    []
-
-It is an empty list. Nevertheless, it is a list of children. Our circles don't have any children.
-
-Is the circle itself an attribute or a thing?
-
-From Elm's perspective, this entire code block is a circle:
-
-| Code
-    Svg.circle
-        [ Svg.Attributes.r "10"
-        , Svg.Attributes.fill "blue"
-        ]
-        []
-
-This is not a circle:
-
-| Code
-    Svg.circle
-        [ Svg.Attributes.r "10"
-        , Svg.Attributes.fill "blue"
-        ]
-
-Nor is this:
+| Monospace
+    TODO: Example showing the connected dots
=
-| Code
-    Svg.circle
+| Emphasize
+    Congratulations!
=
-To create a list with two circles, we'll want to duplicate the properly formed circle value. You can give the second dot a different color from the first.
+| Emphasize
+    {Icon|name=award}
=
-| Code
-    [ Svg.circle
-        [ Svg.Attributes.r "10"
-        , Svg.Attributes.fill "blue"
-        ]
-        []
-    , Svg.circle
-        [ Svg.Attributes.r "10"
-        , Svg.Attributes.fill "red"
-        ]
-        []
-    ]
+| Emphasize
+    You are ready for {Link|Day 4|url=/day-4.html}!

Correct typos

On by Fana

index d1fc7a3..7a9f8a4 100644
--- a/content/day-1.txt
+++ b/content/day-1.txt
@@ -289,7 +289,7 @@ One method is to use the *cartesian coordinates system*. To get some intuition a
=| Note
=    TODO: Image of the table, the flower pot and the villain
=
-However, if you had a ruler, you could  say: "it's 63.3cm from the top edge and 27.1cm from the left edge". To do that you would have previously agree which edge is left and which is top - not so obvious in case of a table. Fortunately in SVG we have this sort of agreement made for us. We alsways measure from the top and left edge of the viewport.
+However, if you had a ruler, you could  say: "it's 63.3cm from the top edge and 27.1cm from the left edge". To do that you would have previously agree which edge is left and which is top - not so obvious in case of a table. Fortunately in SVG we have this sort of agreement made for us. We always measure from the top and left edge of the viewport.
=
=The distance from the left edge is called {Code|x} and the distance from the top is called {Code|y}. The bigger the {Code|x} the more to the right goes the element. The bigger the {Code|y} the more it goes  down. Try it out:
=
@@ -302,7 +302,7 @@ The distance from the left edge is called {Code|x} and the distance from the top
=
=Adjust the sliders below to change the {Code|x} and {Code|y} coordinates of the dot. It's like moving the flower pot on the table by describing how far left, right, up or down it should be.
=
-Initially the center of the dot is at a point called /the origin/ or {Code|\{ x = 0,  y = 0\}}. The origin is in the top left corner of the viewport. That's why only quarter of the dot is visible. If we want to help our dot get out of hiding, we'll have to place the center of the dot at a point with positive {Code|x} and {Code|y} values.
+Initially the center of the dot is at a point called /the origin/ or {Code|\{ x = 0,  y = 0\}}. The origin is in the top left corner of the viewport. That's why only a quarter of the dot is visible. If we want to help our dot get out of hiding, we'll have to place the center of the dot at a point with positive {Code|x} and {Code|y} values.
=
=To change the position of the dot, we can use the {Code|cx} and {Code|cy} attributes of the {Code|circle} element, like this:
=
@@ -412,7 +412,7 @@ In the code we set the viewbox as an attribute of the {Code|svg} element, like t
=            , Element.height Element.fill
=            ]
=
-There are only two changes here. First we reset the {Code|cx} and {Code|cy} attributes of the {Code|circle} back to {Code|"0"} (on lines 11 and 12). We want the dot to be back at origin. Then we set up the position of the viewbox on line 20. Try experimenting with different values. If you set them right and refresh the broswer the dot should be in the center of the screen.
+There are only two changes here. First we reset the {Code|cx} and {Code|cy} attributes of the {Code|circle} back to {Code|"0"} (on lines 11 and 12). We want the dot to be back at origin. Then we set up the position of the viewbox on line 20. Try experimenting with different values. If you set them right and refresh the browser the dot should be in the center of the screen.
=
=| Window
=    | DotWithViewBox
@@ -425,7 +425,7 @@ You can also remove line 19 to remove the pink background, or set it to some oth
=| Header
=    Color
=
-Speaking of colors, now that we've centered our dot, it's time to give it a color! We do it by adding an {Code|fill} attribute to the {Code|circle} element, like that:
+Speaking of colors, now that we've centered our dot, it's time to give it a color! We do it by adding a {Code|fill} attribute to the {Code|circle} element, like that:
=
=| Code
=    module Main exposing (main)
index af026fb..8161521 100644
--- a/content/day-2.txt
+++ b/content/day-2.txt
@@ -5,7 +5,7 @@
=    Let's Place the Dots in a Circle
=
=| Note
-    Today we are going to lear about
+    Today we are going to learn about
=
=    | List
=        - SVG transformations
@@ -324,7 +324,7 @@ Now, notice that the dots displayed by the program we are creating are not layin
=        radius = 80
=        scatter = False
=
-On the right, you immediately see a patern. They form a circle. But in fact both pictures are showing 5 dots laying on a circle. Look:
+On the right, you immediately see a pattern. They form a circle. But in fact both pictures are showing 5 dots laying on a circle. Look:
=
=| Row
=    | Circle
@@ -451,7 +451,7 @@ We will start from where we left off yesterday. Open {Code|src//Main.elm} in you
=                , Element.height Element.fill
=                ]
=
-It's a good starting point. We already have a single dot in the middle. Let's say this is will be the center of our circle.
+It's a good starting point. We already have a single dot in the middle. Let's say this will be the center of our circle.
=
=Now it's time to introduce our protractor and ruler. These is a property of an SVG element called `transform`. You use it like that:
=
@@ -537,7 +537,7 @@ For that we need to add another transformation before the move. First we will ro
=        []
=    ]
=
-This will rotate the element by 72 degrees. On it's own it wouldn't make any difference, because the dot is round - it doesn't matter how you rotate it, it always looks the same. But It also rotates it's internal coordinates system. So the translation will move it in a different direction then the first dot.
+This will rotate the element by 72 degrees. On its own it wouldn't make any difference, because the dot is round - it doesn't matter how you rotate it, it always looks the same. But It also rotates its internal coordinates system. So the translation will move it in a different direction then the first dot.
=
=Now it's time to add the third, red dot. Duplicate the second one, change fill to `"red"` and put `144` for rotate function, like this:
=
index 5780836..4125171 100644
--- a/content/day-3.txt
+++ b/content/day-3.txt
@@ -5,7 +5,7 @@
=    Connecting the Dots
=
=| Note
-    So far we have been writing the code of our program without thinking too much about about what it means. Today we are going to learn about
+    So far we have been writing the code of our program without thinking too much about what it means. Today we are going to learn about
=
=    | List
=        - Values and names
@@ -83,7 +83,7 @@ Now let's focus our attention to *names*. You can give any value a name.
=    "Hello!" : String
=    >
=
-From now on you can refer to this value either literally or by it's name:
+From now on you can refer to this value either literally or by its name:
=
=| Terminal
=    > kittens
@@ -109,19 +109,19 @@ Here we gave a name {Code|fun} to a function, that given {Code|something} will p
=    > fun "Learning Elm"
=    "Learning Elm is fun!" : String
=
-Above we applied the function (by calling it's name: {Code|fun}) to a literal value: {Code|"Learning Elm"}. In return the function produced another value: {Code|"Learning Elm is fun!"}. So a function takes a value and gives a value. But function itself is also a value.
+Above we applied the function (by calling its name: {Code|fun}) to a literal value: {Code|"Learning Elm"}. In return the function produced another value: {Code|"Learning Elm is fun!"}. So a function takes a value and gives a value. But function itself is also a value.
=
=| Note
=    Imagine a machine that paints eggs. You put an egg into the machine and on the other side you get a painted egg. The egg is a thing and the painted egg is also a thing. But so is the machine!
=
-Now we see that we can get a value in three ways: (1) by literally expressing it, (2) by calling it's name and (3) by applying a function to another value.
+Now we see that we can get a value in three ways: (1) by literally expressing it, (2) by calling its name and (3) by applying a function to another value.
=
-Not every function can be applied to any value. You can add two numbers (like {Code|2} and {Code|5} in {Code|2 + 5}), but you can't a number to a string ({Code|2 + "Hello!"} - try it and see an error!). The system that governs what function can be applied to what value is called a *type system* and it plays important role in Elm programming.
+Not every function can be applied to any value. You can add two numbers (like {Code|2} and {Code|5} in {Code|2 + 5}), but you can't add a number to a string ({Code|2 + "Hello!"} - try it and see an error!). The system that governs what function can be applied to what value is called a *type system* and it plays important role in Elm programming.
=
=| Note
=    Again, imagine a machine that paints eggs. If you try to put a carrot in that machine, the dumb machine would make a mess of your carrot and probably jam. But a smart machine would sound a low pitch buzz and simply refuse to take the carrot. That way a smart machine avoids making a mess and possibly damaging itself. The Elm's type system is what makes your functions smart. It stops you from putting a carrot into the egg painting machine!
=
-Every value in Elm has a type. You can see the type in the REPL - after evaluating the expression it shows it's value and the type, like here:
+Every value in Elm has a type. You can see the type in the REPL - after evaluating the expression it shows its value and the type, like here:
=
=| Terminal
=    > kittens
@@ -141,7 +141,7 @@ Composite values like lists have composite types. For example here the type is {
=    > [ "Hello", "Good bye" ]
=    ["Hello","Good bye"] : List String
=
-This means that it's a list of strings. That means that all the elements of this list are of type {Code|String}. When talking about lists you have to tell that it's a list and what is the type of it's element. Only this can be considered a fully specified type. Compare:
+This means that it's a list of strings. That means that all the elements of this list are of type {Code|String}. When talking about lists you have to tell that it's a list and what is the type of its element. Only this can be considered a fully specified type. Compare:
=
=| Terminal
=    > [ 1, 2, 3 ]
@@ -177,12 +177,12 @@ What's the type of an empty list? Let's see:
=    > []
=    [] : List a
=
-A list of {Code|a}. Obviously {Code|a} is not a type. This means that the type of the elements cannot be determine yet. This is signaled by the lowercase term where the type should be: {Code|a} instead of the capitalized {Code|String}.
+A list of {Code|a}. Obviously {Code|a} is not a type. This means that the type of the elements cannot be determined yet. This is signaled by the lowercase term where the type should be: {Code|a} instead of the capitalized {Code|String}.
=
=| Note
=    Notice that the {Code|number} (type of value {Code|15} for example} is also lowercase. That's because it's also not a concrete type! Elm has two concrete types for numbers: {Code|Int} that can represent an integer number (1, 2, 3, 0, -122 etc.) and {Code|Float} that can represent a number with a fraction, (like 1.2, -3.5, 44.2). Because fraction part can be 0 (e.g. 1.0, 2.0, 0.0, -122.0 - so called whole numbers), it's not possible to tell if {Code|2} is an {Code|Int} or a {Code|Float}. We will see some of the implications of this later.
=
-Above I said that every value has a type and also that a function is a value on it's own. Let's try the following in REPL:
+Above I said that every value has a type and also that a function is a value on its own. Let's try the following in REPL:
=
=| Terminal
=    > fun
@@ -198,7 +198,7 @@ The part expressed as {Code|"Learning Elm"} is a {Code|String} (we recognize it
=
=The part after the arrow symbol is the type that the function returns. It is also {Code|String}. That also should not surprise us, since {Code|"Learning Elm is fun!"} is also a {Code|String}.
=
-Not every function returns the same type as it takes. For example function {Code|String.fromInt} takes a value of type {Code|Int} and returns a value of type {Code|String}. It's type is therefor {Code|Int -> String}. We read it as: /type of String.fromInt is a function from Int to String/. Let's see it in action:
+Not every function returns the same type as it takes. For example function {Code|String.fromInt} takes a value of type {Code|Int} and returns a value of type {Code|String}. Its type is therefor {Code|Int -> String}. We read it as: /type of String.fromInt is a function from Int to String/. Let's see it in action:
=
=| Terminal
=    > String.fromInt 10
@@ -257,9 +257,9 @@ Our viewBox is also an attribute. It's an attribute of the {Code|svg} element.
=
=Again, try giving it new values. As we saw earlier, the first two values (which represent the left and top of the viewBox) must be equal to the negative of the half of the last two values (which correspond to width and height). Try other combinations of values to adjust the placement of the dot. If the behavior of the viewBox is difficult to understand, refer again to the interactive tutorial above.
=
-To color our dot, we'll have to give it a second attribute. Many SVG elements take a {Code|List} of attributes. A {Code|List} is a very important concept in software development. {Code|Lists} allow us to group an arbitary number of similar values. In this case, we'll be using a {Code|List} to group {Code|Attribute a} values. Whenever you see {Code|[...]} in an Elm source code file, you are dealing with a list.
+To color our dot, we'll have to give it a second attribute. Many SVG elements take a {Code|List} of attributes. A {Code|List} is a very important concept in software development. {Code|Lists} allow us to group an arbitrary number of similar values. In this case, we'll be using a {Code|List} to group {Code|Attribute a} values. Whenever you see {Code|[...]} in an Elm source code file, you are dealing with a list.
=
-Before we give a color to our dot, try to identify all the lists in the code we're writen already. There are 5. Hint: It is possible for a list to have only 1 or 0 items ({Code|[]}).
+Before we give a color to our dot, try to identify all the lists in the code we have written already. There are 5. Hint: It is possible for a list to have only 1 or 0 items ({Code|[]}).
=
=| Code
=    Svg.circle [ Svg.Attributes.r "10" ] []
@@ -330,7 +330,7 @@ Next, identify the first open square bracket {Code|[} before the circle.
=
=This is where the list begins.
=
-Finally, identify the matching closing brakcet {Code|]}. This is a bit tricky because there are two lists within this list. The first is the list of attributes to the circle we saw earlier. The second is an empty list. We'll see exactly what they do in a moment. Ignore both of these for now. The matching closing bracket should be on the same indentation level as the opening bracket. Your text editor should be able to help you here. When you move your cursor before the opening bracket, the closing bracket should be highlighted.
+Finally, identify the matching closing bracket {Code|]}. This is a bit tricky because there are two lists within this list. The first is the list of attributes to the circle we saw earlier. The second is an empty list. We'll see exactly what they do in a moment. Ignore both of these for now. The matching closing bracket should be on the same indentation level as the opening bracket. Your text editor should be able to help you here. When you move your cursor before the opening bracket, the closing bracket should be highlighted.
=
=| Code
=    [ Svg.circle

Merge branch 'content' into content-fix

On by Fana

Correct typos and grammatical error

On by Fana

index f70e581..03a2ca8 100644
--- a/content/day-3.txt
+++ b/content/day-3.txt
@@ -59,7 +59,7 @@ Some value literals are simple, like numbers and strings. Some are complex. Prev
=    > [ 1, 2, 5, 77, 2 ]
=    [1,2,5,77.6,2] : List number
=
-First notice that a list literal starts with {Code|[} and ends with {Code|]}. Inside there are values called /elements of the list/. Each element is separated from others with a {Code|,} (comma). Then notice that each element is a value on it's own, but the whole list is also a value.
+First notice that a list literal starts with {Code|[} and ends with {Code|]}. Inside there are values called /elements of the list/. Each element is separated from others with a {Code|,} (comma). Then notice that each element is a value on its own, but the whole list is also a value.
=
=| Note
=    It can be called a /composite value/. It's like the box of eggs. Each egg is a thing, but the box of eggs is also a thing (composed of the box and the eggs).
@@ -119,7 +119,7 @@ Above we applied the function (by calling its name: {Code|fun}) to a literal val
=| Note
=    Imagine a machine that paints eggs. You put an egg into the machine and on the other side you get a painted egg. The egg is a thing and the painted egg is also a thing. But so is the machine!
=
-    Alternatively you can think of functions as parametrized values. The value you get depends on what value you provide to it. Eggs can have different sizes . An egg painting machine would give you different painted eggs depending on what egg you put in it.
+    Alternatively you can think of functions as parameterized values. The value you get depends on what value you provide to it. Eggs can have different sizes . An egg painting machine would give you different painted eggs depending on what egg you put in it.
=
=    Don't worry if the concept of a function is not very clear yet. Most people have difficulty here. You will see some practical examples of functions soon.
=
@@ -189,7 +189,7 @@ What's the type of an empty list? Let's see:
=A list of {Code|a}. Obviously {Code|a} is not a type. This means that the type of the elements cannot be determined yet. This is signaled by the lowercase term where the type should be: {Code|a} instead of the capitalized {Code|String}.
=
=| Note
-    Notice that the {Code|number} (type of value {Code|15} for example) is also lowercase. That's because it's also not a concrete type! Elm has two concrete types for numbers: {Code|Int} that can represent an integer number (1, 2, 3, 0, -122 etc.) and {Code|Float} that can represent a number with a fraction, (like 1.2, -3.5, 44.2). Because fraction part can be 0 (e.g. 1.0, 2.0, 0.0, -122.0 - so called whole numbers), it's not possible to tell if {Code|2} is an {Code|Int} or a {Code|Float}. We will see some of the implications of this later.
+    Notice that the {Code|number} (type of value {Code|15} for example) is also lowercase. That's because it's not a concrete type! Elm has two concrete types for numbers: {Code|Int} that can represent an integer number (1, 2, 3, 0, -122 etc.) and {Code|Float} that can represent a number with a fraction, (like 1.2, -3.5, 44.2). Because fraction part can be 0 (e.g. 1.0, 2.0, 0.0, -122.0 - so called whole numbers), it's not possible to tell if {Code|2} is an {Code|Int} or a {Code|Float}. We will see some of the implications of this later.
=
=Above I said that every value has a type and also that a function is a value on its own. Let's try the following in REPL:
=
@@ -327,7 +327,7 @@ Take your time. Here are some hints to help you:
=
=    #. Functions are called wherever you see a name that have any expression to the right (or below and right) of it (like: {Code|fun "learning Elm together"}), or that stand to the left of the {Code|\|>} (pipe operator; like: {Code|"learning Elm together" \|> fun}). The expression to the right of the function (or left of pipe) is called /the argument/. Sometimes an argument is a complex expression. Some functions take more than one argument (e.g. {Code|Svg.svg} takes 2 and the second one is very complex), and sometimes one is on the right and the other comes through the pipe 🤕. There is a lot of functions called here (36 to be precise) and all of them are prefixed with a name of the module that exposed them to us.
=
-    #. Interestingly there are only two names exposed by a module that do not refer to a function. Try to find them!
+    #. Interestingly there is only one name exposed by a module that do not refer to a function. Try to find it!
=
=| Header
=    Let's make a function
@@ -360,7 +360,7 @@ Move your cursor to the end of the file and type {Code|dot =}, press {Key|enter}
=| Code
=    TODO
=
-Reload the browser. Everything should work exactly the same as before. One of the dots is now a named value, but for our program it makes no difference. What is important is it's value, not where it comes from.
+Reload the browser. Everything should work exactly the same as before. One of the dots is now a named value, but for our program it makes no difference. What is important is its value, not where it comes from.
=
=Of course the goal is to have our list of dots looking like this:
=
@@ -374,7 +374,7 @@ Of course the goal is to have our list of dots looking like this:
=
=Let's try it. The effect will be that all the dots will be in the same place, one on top of another. Also they will all have the same color. So on the screen you should see only one dot, somewhat to the right of center.
=
-The dots need to have some parameters so they can be different. We already identified them: it's color and rotation. Above I said that a function is a parametrized value. Let's turn our dot definition on line {Code|TODO} into a function with two parameters like this:
+The dots need to have some parameters so they can be different. We already identified them: color and rotation. Above I said that a function is a parametrized value. Let's turn our dot definition on line {Code|TODO} into a function with two parameters like this:
=
=| Code
=    TODO: Rest of the code
@@ -436,7 +436,7 @@ We can use the {Code|++} operator to glue the strings together. First part is co
=            ]
=            []
=
-But Elm will complain about this. First it looks like we gave 5 arguments to the {Code|Svg.Attributes.transform} function, and it only takes one. We can fix it by putting a parentheses around the {Code|"rotate(" ++ rotation ++ ") translate(80)" }, basically telling Elm that this is a single expression and it should evaluate it's value before passing it to the {Code|Svg.Attributes.transform} function. Now it should look like this:
+But Elm will complain about this. First it looks like we gave 5 arguments to the {Code|Svg.Attributes.transform} function, and it only takes one. We can fix it by putting a parentheses around the {Code|"rotate(" ++ rotation ++ ") translate(80)" }, basically telling Elm that this is a single expression and it should evaluate its value before passing it to the {Code|Svg.Attributes.transform} function. Now it should look like this:
=
=| Code
=    TODO: Rest of the code
@@ -511,12 +511,12 @@ That's what we need! Let's pass the value of {Code|rotation} through this functi
=            ]
=            []
=
-This should work! The browser should now present the dots as intended and I would argue that the code is more readable now.
+This should work! The browser should now present the dots as intended and I would agree that the code is more readable now.
=
=| Header
=    Lines to Connect the Dots
=
-Armed with our functional superpowers, let's make another function that draws a line. Let me fist show you a complete code and then we can discuss it.
+Armed with our functional superpowers, let's make another function that draws a line. Let me first show you a complete code and then we can discuss it.
=
=| Code
=    line color rotation =
@@ -537,7 +537,7 @@ Armed with our functional superpowers, let's make another function that draws a
=            ]
=            []
=
-It works similar to the {Code|dot} function so you can just copy and past the it. Then apply the following changes:
+It works similar to the {Code|dot} function so you can just copy and paste it. Then apply the following changes:
=
=| List
=    #. The name needs to be different. {Code|line} seems appropriate.
@@ -546,7 +546,7 @@ It works similar to the {Code|dot} function so you can just copy and past the it
=
=    #. The line doesn't have a radius, so remove the first attribute, {Code|Svg.Attributes.r}.
=
-    #. The line goes from one point to another, so it has four coordinates {Code|x1} and {Code|y1} for the beggening, {Code|x2} and {Code|y2} for the end. Beggening should be in the middle of the circle, which is {Code|x=0, y=0} (/the origin/). We will make the end reach to the big circle by giving the {Code|x2} value of {Code|80} (same as a radius of the circle).
+    #. The line goes from one point to another, so it has four coordinates {Code|x1} and {Code|y1} for the beginning, {Code|x2} and {Code|y2} for the end. Beginning should be in the middle of the circle, which is {Code|x=0, y=0} (/the origin/). We will make the end reach to the big circle by giving the {Code|x2} value of {Code|80} (same as a radius of the circle).
=
=    #. Line can't be filled, as it has no area. Instead we give it a color using {Code|Svg.Attributes.stroke} function.
=

Style

On by Tadeusz Łazurski

index 03a2ca8..155a00b 100644
--- a/content/day-3.txt
+++ b/content/day-3.txt
@@ -511,7 +511,7 @@ That's what we need! Let's pass the value of {Code|rotation} through this functi
=            ]
=            []
=
-This should work! The browser should now present the dots as intended and I would agree that the code is more readable now.
+This should work! The browser should now present the dots as intended and I hope you agree that the code is more readable now.
=
=| Header
=    Lines to Connect the Dots

Fix typo

On by Tadeusz Łazurski

index 32fc805..e54b4b9 100644
--- a/content/day-5.txt
+++ b/content/day-5.txt
@@ -6,7 +6,7 @@
=
=
=| Note
-    Today we are going to lear about
+    Today we are going to learn about
=
=    | List
=        - The Elm Architecture

Control oprhans in paragraphs

On by Tadeusz Łazurski

See https://css-tricks.com/almanac/properties/o/orphans/

index b49f7d3..8430329 100644
--- a/src/Mark/Custom.elm
+++ b/src/Mark/Custom.elm
@@ -55,6 +55,7 @@ paragraph =
=                [ Element.paddingXY 0 10
=                , Element.spacing 12
=                , css "hyphens" "auto"
+                , css "orphans" "3"
=                , Font.justify
=                ]
=                (content model)

Write first section of day 4 - manual tree

On by Tadeusz Łazurski

index ea07e93..80d31eb 100644
--- a/content/day-4.txt
+++ b/content/day-4.txt
@@ -1,20 +1,372 @@
=| Title
-    Day 3
+    Day 4
=
=| Emphasize
=    Let's Make a Tree
=
+| Note
+    *We are still working on this content.*
+
+    Stay tuned {Icon|name=radio}
=
=| Note
-    Today we are going to lear about
+    Last day we have learned about basic building blocks of an Elm program: *value*, *name* and *type*.  Today we are going to use this blocks to compose more complex structures. On the way we are going to learn about:
=
=    | List
-        - Recursion
+        -> Recursion
=
=| Header
=    The Problem
=
+| Monospace
+    TODO: Static tree example
+
+The tree is built from segments: a line and a dot. In this respect it is similar to the connected dots we created yesterday. If we group the dot and a line, we will have the building block for our tree. We can do it with {Code|Svg.g} function ({Code|g} is an abbreviation of "group"). Just like {Code|Svg.svg}, it takes list of attributes and list of children. We can use it like that:
+
+| Code
+    main =
+        [ Svg.g []
+            [ dot "skyblue" 0
+            , line "skyblue" 0
+            ]
+        , Svg.g []
+            [ dot "orange" 72
+            , line "orange" 72
+            ]
+        , Svg.g []
+            [ dot "red" 144
+            , line "red" 144
+            ]
+        , Svg.g []
+            [ dot "lime" 216
+            , line "lime" 216
+            ]
+        , Svg.g []
+            [ dot "maroon" 288
+            , line "maroon" 288
+            ]
+        ]
+            |> Svg.svg
+                [ Svg.Attributes.height "100%"
+                , Svg.Attributes.width "100%"
+                , Svg.Attributes.style "background: none"
+                , Svg.Attributes.viewBox "-100 -100 200 200"
+                ]
+            |> Element.html
+            |> Element.layout
+                [ Element.width Element.fill
+                , Element.height Element.fill
+                ]
+
+Reload the browser there should be no difference at this point. Looking back at code I notice that each segment looks the same: it is a group with a dot and a line, where the {Code|group} and {Code|line} functions have the same arguments passed to them. This kind of repetition begs for a function. Let's call it {Code|segment} and define like this:
+
+| Code
+    segment color rotation =
+        Svg.g []
+            [ dot color rotation
+            , line color rotation
+            ]
+
+Type this code at the bottom of the file and then replace main with the following:
+
+| Code
+    main =
+        [ segment "skyblue" 0
+        , segment "orange" 72
+        , segment "red" 144
+        , segment "lime" 216
+        , segment "maroon" 288
+        ]
+            |> Svg.svg
+                [ Svg.Attributes.height "100%"
+                , Svg.Attributes.width "100%"
+                , Svg.Attributes.style "background: none"
+                , Svg.Attributes.viewBox "-100 -100 200 200"
+                ]
+            |> Element.html
+            |> Element.layout
+                [ Element.width Element.fill
+                , Element.height Element.fill
+                ]
+
=| Note
-    *We are still working on this content.*
+    I hope you can see the pattern in what we are doing. We are taking repetitive blocks of code and turning them into named functions. Parameters help us deal with variability in the repeated code (like color and rotation that is different for each segment).
=
-    Stay tuned {Icon|name=radio}
+Reload the browser. There should still be no visible difference in the behavior of the program, but our source code is getting more readable. That's good.
+
+The big difference between our program and the one we are trying to build is that our have only one level of segments, whereas in the example segments grow from the tip of other segments. You can think of it as segments having child segments: the green segment has two child brown segments. Each brown segment has three red child segments
+
+| Note
+    TODO: Make sure the description matches the example
+
+It's similar to how the way an SVG group has child elements. But group itself (together with it's children) is an element, so we can put a group within a group. Let's do that! Change the definition of {Code|segment} function as follows:
+
+| Code
+    segment color rotation =
+        Svg.g []
+            [ dot color rotation
+            , line color rotation
+            , Svg.g
+                [ Svg.Attributes.transform
+                    (String.concat
+                        [ "rotate("
+                        , String.fromFloat rotation
+                        , ") translate(80)"
+                        ]
+                    )
+                ]
+                [ dot "red" 15
+                , line "red" 15
+                , dot "blue" -15
+                , line "blue" -15
+                ]
+            ]
+
+Now the program should display something like this:
+
+| Note
+    TODO: Example
+
+It's already starts to look interesting, but there are two problems with it. First it doesn't fit on screen. This is easy to fix using {Code|viewBox}. We need to zoom out to see more of a picture. Change the value passed to {Code|viewBox} on line 19 to {Code|"-500 -500 1000 1000"}, effectively zooming out 5x but preserving keeping the origin in the center of our viewbox.
+
+Then the lines look ugly displayed on top of the dots of different color. This is also easy to fix. In SVG several siblings (children of the same parent element) lay one on top of another. The "younger" sibling is laying on top of the older, so in our case the group containing the blue and red dots and lines lays on top of the dot and line. Let's change the order of siblings by moving the group to the beginning of the list, like this:
+
+| Code
+    segment color rotation =
+        Svg.g []
+              [ Svg.g
+                  [ Svg.Attributes.transform
+                      (String.concat
+                          [ "rotate("
+                          , String.fromFloat rotation
+                          , ") translate(80)"
+                          ]
+                      )
+                  ]
+                  [ dot "red" 15
+                  , line "red" 15
+                  , dot "blue" -15
+                  , line "blue" -15
+                  ]
+              , dot color rotation
+              , line color rotation
+              ]
+
+With this two changes applied you should see something like this in the browser:
+
+| Note
+    TODO: Example
+
+So now each segment has two sub segments: red and blue. The red one is rotate 15 degree clockwise, the blue one is rotated 15 degree counterclockwise. But they are look the same. Our goal is to make them variable. This means that we need one more parameter to the segment: list of children. Let's do it:
+
+| Code
+    module Main exposing (main)
+
+    import Element
+    import Svg exposing (Svg)
+    import Svg.Attributes
+
+
+    main =
+        [ segment "skyblue"
+            0
+            [ dot "red" 15
+            , line "red" 15
+            , dot "blue" -15
+            , line "blue" -15
+            ]
+        , segment "orange" 72 []
+        , segment "red" 144 []
+        , segment "lime" 216 []
+        , segment "maroon" 288 []
+        ]
+            |> Svg.svg
+                [ Svg.Attributes.height "100%"
+                , Svg.Attributes.width "100%"
+                , Svg.Attributes.style "background: none"
+                , Svg.Attributes.viewBox "-500 -500 1000 1000"
+                ]
+            |> Element.html
+            |> Element.layout
+                [ Element.width Element.fill
+                , Element.height Element.fill
+                ]
+
+
+    segment : String -> Float -> List (Svg msg) -> Svg.Svg msg
+    segment color rotation children =
+        Svg.g []
+            [ Svg.g
+                [ Svg.Attributes.transform
+                    (String.concat
+                        [ "rotate("
+                        , String.fromFloat rotation
+                        , ") translate(80)"
+                        ]
+                    )
+                ]
+                children
+            , dot color rotation
+            , line color rotation
+            ]
+
+
+    dot color rotation =
+        Svg.circle
+            [ Svg.Attributes.r "10"
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ") translate(80)"
+                    ]
+                )
+            ]
+            []
+
+
+    line color rotation =
+        Svg.line
+            [ Svg.Attributes.strokeWidth "1"
+            , Svg.Attributes.x1 "0"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.x1 "80"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.stroke color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ")"
+                    ]
+                )
+            ]
+            []
+
+
+We have added third parameter to {Code|segment} function on line 34 (before the changes it was 28). The parameter is named {Code|children} and we use it in place of the list on line 45 (previously 39). The list itself was moved to line 11, where it is passed as a value for the parameter of the first segment. This move made all the lines shift down. All other segments will also need to get value for {Code|children} parameter, so let's just give each of them an empty list - meaning they have 0 children.
+
+Once all changes are applied, reload the browser and see that only first, skyblue segment has child segments.
+
+| Note
+    TODO: Example
+
+Before we continue, let's notice that on lines 11 - 15 we have a nice opportunity to remove some repetition. Consider that dot and a line with same rotation and color are just a segment. We can replace:
+
+| Code
+    [ dot "red" 15
+    , line "red" 15
+    , dot "blue" -15
+    , line "blue" -15
+    ]
+
+with:
+
+| Code
+    [ segment "red" 15 []
+    , segment "blue" -15 []
+    ]
+
+A segment within a segment! Consider that every segment can have child segments. This way we can build a tree as complex as we want. Try adding more children:
+
+| Code
+    module Main exposing (main)
+
+    import Element
+    import Svg exposing (Svg)
+    import Svg.Attributes
+
+
+    main =
+        [ segment "skyblue"
+            0
+            [ segment "red" 15 []
+            , segment "blue"
+                -15
+                [ segment "yellow" 45 []
+                , segment "pink" -20 []
+                , segment "green" -90 []
+                ]
+            ]
+        , segment "orange" 72 []
+        , segment "red" 144 []
+        , segment "lime" 216 []
+        , segment "maroon" 288 []
+        ]
+            |> Svg.svg
+                [ Svg.Attributes.height "100%"
+                , Svg.Attributes.width "100%"
+                , Svg.Attributes.style "background: none"
+                , Svg.Attributes.viewBox "-500 -500 1000 1000"
+                ]
+            |> Element.html
+            |> Element.layout
+                [ Element.width Element.fill
+                , Element.height Element.fill
+                ]
+
+
+    segment color rotation children =
+        Svg.g []
+            [ Svg.g
+                [ Svg.Attributes.transform
+                    (String.concat
+                        [ "rotate("
+                        , String.fromFloat rotation
+                        , ") translate(80)"
+                        ]
+                    )
+                ]
+                children
+            , dot color rotation
+            , line color rotation
+            ]
+
+
+    dot color rotation =
+        Svg.circle
+            [ Svg.Attributes.r "10"
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ") translate(80)"
+                    ]
+                )
+            ]
+            []
+
+
+    line color rotation =
+        Svg.line
+            [ Svg.Attributes.strokeWidth "1"
+            , Svg.Attributes.x1 "0"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.x1 "80"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.stroke color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ")"
+                    ]
+                )
+            ]
+            []
+
+| Emphasize
+    Time to play 🌳🌲🌴🎄
+
+| Emphasize
+
+    Try building your own tree this way. Play with colors and sizes.
+
+| Note
+    Hint: if you add {Code|Svg.attributes.strokeWidth "2"} attribute to the line, you will get thicker branches. Try different combinations of stroke width and radius of a dot.

Start writing Rule Based Tree

On by Tadeusz Łazurski

index 80d31eb..3dbc52a 100644
--- a/content/day-4.txt
+++ b/content/day-4.txt
@@ -370,3 +370,30 @@ A segment within a segment! Consider that every segment can have child segments.
=
=| Note
=    Hint: if you add {Code|Svg.attributes.strokeWidth "2"} attribute to the line, you will get thicker branches. Try different combinations of stroke width and radius of a dot.
+
+| Header
+    Rules Based Tree
+
+I hope you had fun and are proud of your tree. If you made a big one, perhaps you have noticed that all this nesting gets pretty tedious. What if we could make the computer do the hard work? We can do it, but as you may have noticed computers are not very smart, so we need to give them exact rules.
+
+Let's say we have three types of segments: brown, green and red. The child segments depend on the parent like this:
+
+| List
+    #. We always start with a brown segment going up (-90 degree).
+
+    #. The brown segment will have three green child segments:
+
+        - one going straight (0 degrees),
+        - one a little bit to the right (20 degree)
+        - and one little bit to the left (-30 degree).
+
+    #. Green segments will have three red child segments
+
+        - one at -45 degree
+        - one at -5 degree
+        - one at 50 degree
+
+The tree following these rules will look like this:
+
+| Note
+    TODO: Example

Upgrade mdgriffith/elm-markup to 2.0.6

On by Tadeusz Łazurski

Fixed startWith bugs.

index 439720e..64c0692 100644
--- a/elm.json
+++ b/elm.json
@@ -22,7 +22,7 @@
=            "feathericons/elm-feather": "1.3.0",
=            "ianmackenzie/elm-geometry": "1.2.1",
=            "ianmackenzie/elm-geometry-svg": "1.0.2",
-            "mdgriffith/elm-markup": "2.0.5",
+            "mdgriffith/elm-markup": "2.0.6",
=            "mdgriffith/elm-ui": "1.1.0",
=            "turboMaCk/any-dict": "1.0.1"
=        },

Make the Dot example work, add fill property to Container

On by Tadeusz Łazurski

Day 1 is updated to work with Dots. Day 2 needs to get updated still.

index d1fc7a3..680cd91 100644
--- a/content/day-1.txt
+++ b/content/day-1.txt
@@ -111,10 +111,18 @@ This was a warmup. Let's try something more challenging.
=We want to display a dot at the center of the screen, like this:
=
=| Window
-    | DotWithViewBox
-        background=none
-        fill=skyblue
-        viewBox=-300 -300 600 600
+    | Dots
+        | Container
+            background = none
+            viewBox = -300 -300 600 600
+            fill = True
+
+
+        | Dot
+            cx = 0
+            cy = 0
+            color = skyblue
+            radius = 10
=
=Below is the complete code to draw a dot like this, but don't type it in your editor yet! We are going to recreate it together, step by step.
=
@@ -193,9 +201,17 @@ Start the reactor again by entering:
=Reload the browser. You should see something like this:
=
=| Window
-    | Dot
-        background=none
-        fill=black
+    | Dots
+        | Container
+            background = none
+            fill = False
+            viewBox = auto
+
+        | Dot
+            radius = 10
+            cx = 0
+            cy = 0
+            color = black
=
=At this point we have a dot on the screen - it's at the top left corner of the viewport (the area of the browser window where the content is displayed). We can see only a quarter of it - the rest is outside of the viewport.
=
@@ -220,9 +236,17 @@ First we have to realize that the dot is inside an {Code|svg} element that itsel
=Reloading the browser should reveal something like this:
=
=| Window
-    | Dot
-        background=pink
-        fill=black
+    | Dots
+        | Container
+            background = pink
+            fill = False
+            viewBox = auto
+
+        | Dot
+            radius = 10
+            cx = 0
+            cy = 0
+            color = black
=
=Since the dot is inside the {Code|svg} element, and the {Code|svg} element doesn't cover the center of the screen, it's impossible to place the dot at the center. Before anything else, let's make the {Code|svg} fill entire {Definition|term=viewport|definiens=the area of the browser window where the content is displayed}.
=
@@ -271,11 +295,17 @@ As before, press {Key|enter} to follow the plan. Now let's use the package. Chan
=Reload the browser. The SVG space now fills the entire viewport. Even the small white margin is gone!
=
=| Window
-    | DotInElement
-        background=pink
-        fill=black
-        cx=0
-        cy=0
+    | Dots
+        | Container
+            background = pink
+            fill = True
+            viewBox = auto
+
+        | Dot
+            radius = 10
+            cx = 0
+            cy = 0
+            color = black
=
=It should now be possible to place our dot in the center of the screen. Currently the dot is in the top left corner of the viewport, but at least the parent {Code|svg} element is covering it, so the center of the viewport is somewher within the {Code|svg} element.
=
@@ -334,11 +364,17 @@ To change the position of the dot, we can use the {Code|cx} and {Code|cy} attrib
=            ]
=
=| Window
-    | DotInElement
-        background=pink
-        fill=black
-        cx=100
-        cy=50
+    | Dots
+        | Container
+            background = none
+            fill = True
+            viewBox = auto
+
+        | Dot
+            radius = 10
+            cx = 100
+            cy = 50
+            color = black
=
=| Note
=    The {Code|cx} stands for /center x/ and {Code|cy} for /center y/, where center refers to the circle. We will discuss what the center of the circle is in more exact terms on day 2, but you probably get the concept.
@@ -415,10 +451,17 @@ In the code we set the viewbox as an attribute of the {Code|svg} element, like t
=There are only two changes here. First we reset the {Code|cx} and {Code|cy} attributes of the {Code|circle} back to {Code|"0"} (on lines 11 and 12). We want the dot to be back at origin. Then we set up the position of the viewbox on line 20. Try experimenting with different values. If you set them right and refresh the broswer the dot should be in the center of the screen.
=
=| Window
-    | DotWithViewBox
-        background=pink
-        fill=black
-        viewBox=-300 -300 600 600
+    | Dots
+        | Container
+            background = pink
+            fill = True
+            viewBox = -300 -300 600 600
+
+        | Dot
+            radius = 10
+            cx = 0
+            cy = 0
+            color = black
=
=You can also remove line 19 to remove the pink background, or set it to some other color.
=
@@ -458,10 +501,17 @@ Speaking of colors, now that we've centered our dot, it's time to give it a colo
=The only change is on line 13. Reload the browser and see this:
=
=| Window
-    | DotWithViewBox
-        background=none
-        fill=skyblue
-        viewBox=-300 -300 600 600
+    | Dots
+        | Container
+            background = none
+            fill = True
+            viewBox = -300 -300 600 600
+
+        | Dot
+            radius = 10
+            cx = 0
+            cy = 0
+            color = skyblue
=
=| Emphasize
=    Wow!
index 4ebd806..e4d98a5 100644
--- a/src/Examples.elm
+++ b/src/Examples.elm
@@ -3,12 +3,9 @@ module Examples exposing (Model, Msg(..), init, subscriptions, update)
=import Examples.CartesianCoordinates
=import Examples.Circle
=import Examples.Counter
-import Examples.Dot as Dot
-import Examples.DotInElement as DotInElement
-import Examples.DotWithViewBox as DotWithViewBox
+import Examples.Dots
=import Examples.Gradient
=import Examples.Line
-import Examples.MultipleDots as MultipleDots
=import Examples.NestedTransformations
=import Examples.PolarCoordinates
=import Examples.RosetteTypedTransformations
deleted file mode 100644
index 5581546..0000000
--- a/src/Examples/Dot.elm
+++ /dev/null
@@ -1,32 +0,0 @@
-module Examples.Dot exposing (Config, defaults, ui)
-
-import Html exposing (Html)
-import Svg
-import Svg.Attributes
-
-
-type alias Config =
-    { background : String
-    , fill : String
-    }
-
-
-defaults : Config
-defaults =
-    Config "none" "black"
-
-
-main : Html msg
-main =
-    ui defaults
-
-
-ui : Config -> Html msg
-ui { background, fill } =
-    Svg.svg [ Svg.Attributes.style <| "background: " ++ background ]
-        [ Svg.circle
-            [ Svg.Attributes.r "10"
-            , Svg.Attributes.fill fill
-            ]
-            []
-        ]
deleted file mode 100644
index ee127b3..0000000
--- a/src/Examples/DotInElement.elm
+++ /dev/null
@@ -1,44 +0,0 @@
-module Examples.DotInElement exposing (Config, main, ui)
-
-import Element exposing (Element)
-import Svg
-import Svg.Attributes
-
-
-type alias Config =
-    { background : String
-    , fill : String
-    , cx : String
-    , cy : String
-    }
-
-
-defaults : Config
-defaults =
-    Config "pink" "black" "0" "0"
-
-
-main =
-    Element.layout
-        [ Element.width Element.fill
-        , Element.height Element.fill
-        ]
-        (ui defaults)
-
-
-ui : Config -> Element msg
-ui { background, fill, cx, cy } =
-    Svg.svg
-        [ Svg.Attributes.height "100%"
-        , Svg.Attributes.width "100%"
-        , Svg.Attributes.style <| "background: " ++ background
-        ]
-        [ Svg.circle
-            [ Svg.Attributes.r "10"
-            , Svg.Attributes.fill fill
-            , Svg.Attributes.cx cx
-            , Svg.Attributes.cy cy
-            ]
-            []
-        ]
-        |> Element.html
deleted file mode 100644
index 6f91dc6..0000000
--- a/src/Examples/DotWithViewBox.elm
+++ /dev/null
@@ -1,57 +0,0 @@
-module Examples.DotWithViewBox exposing (Config, main, ui)
-
-import Element
-import Svg
-import Svg.Attributes
-
-
-{-| This program demonstrates how to center an element within an SVG viewport. The idea is to make sure that (0, 0) point (so called origin) should be in the middle of the ViewBox. We can achive that, by making the top-left corner of the ViewBox same distance from the (0, 0) as the bottom right.
-
-The viewBox property takes a string consisting of four numbers:
-
-    - position of the top edge
-    - position of the left edge
-    - width
-    - and height
-
-If we set the width to 200 and then set the left edge to -100, that will place the right edge at +100. Both edges are 100 away from the 0!
-
-The same way you can control the bottom edge by tweaking the position of the top edge and the height.
-
-In fact it doesnt matter how wide and high the viewBox is. As long as the top will be -1/2 of the width and left will be -1/2 of the width.
-
-That's the trick to position an object (like a dot) exactly in the center of the SVG viewport
-
--}
-type alias Config =
-    { background : String
-    , fill : String
-    , viewBox : String
-    }
-
-
-defaults : Config
-defaults =
-    Config "white" "black" "-500 -500 1000 1000"
-
-
-main =
-    Element.layout
-        [ Element.width Element.fill
-        , Element.height Element.fill
-        ]
-        (ui defaults)
-
-
-ui { background, fill, viewBox } =
-    Svg.svg
-        [ Svg.Attributes.viewBox viewBox
-        , Svg.Attributes.style <| "background: " ++ background
-        ]
-        [ Svg.circle
-            [ Svg.Attributes.r "10"
-            , Svg.Attributes.fill fill
-            ]
-            []
-        ]
-        |> Element.html
index 5944489..4285cb8 100644
--- a/src/Examples/Dots.elm
+++ b/src/Examples/Dots.elm
@@ -10,6 +10,7 @@ import Circle2d
=import Element exposing (Element)
=import Geometry.Svg
=import Html exposing (Html)
+import Html.Attributes
=import Point2d exposing (Point2d)
=import Svg exposing (Attribute, Svg)
=import Svg.Attributes
@@ -24,6 +25,7 @@ type alias Config =
=type alias Container =
=    { background : String
=    , viewBox : String
+    , fill : Bool
=    }
=
=
@@ -40,6 +42,7 @@ defaults =
=    { container =
=        { background = "none"
=        , viewBox = "-300 -300 600 600"
+        , fill = True
=        }
=    , dots =
=        [ { cx = 0
@@ -69,13 +72,35 @@ ui { container, dots } =
=                |> Point2d.fromCoordinates
=                |> Circle2d.withRadius radius
=                |> Geometry.Svg.circle2d [ Svg.Attributes.fill color ]
+
+        width =
+            if container.fill then
+                "100%"
+
+            else
+                "300"
+
+        height =
+            if container.fill then
+                "100%"
+
+            else
+                "150"
+
+        padding =
+            if container.fill then
+                "0"
+
+            else
+                "5px"
=    in
=    dots
=        |> List.map dot
=        |> Svg.svg
=            [ Svg.Attributes.viewBox container.viewBox
-            , Svg.Attributes.style <| "background: " ++ container.background
-            , Svg.Attributes.width "100%"
-            , Svg.Attributes.height "100%"
+            , Html.Attributes.style "background" container.background
+            , Svg.Attributes.width width
+            , Svg.Attributes.height height
+            , Html.Attributes.style "margin" padding
=            ]
=        |> Element.html
deleted file mode 100644
index b0625da..0000000
--- a/src/Examples/MultipleDots.elm
+++ /dev/null
@@ -1,41 +0,0 @@
-module Examples.MultipleDots exposing (Config, main, ui)
-
-import Element
-import Svg exposing (Attribute)
-import Svg.Attributes
-
-
-type alias Config msg =
-    { background : String
-    , viewBox : String
-    , dotsAttributes : List (List (Attribute msg))
-    }
-
-
-defaults : Config msg
-defaults =
-    Config "white" "-500 -500 1000 1000" [ [ Svg.Attributes.r "10" ] ]
-
-
-main =
-    Element.layout
-        [ Element.width Element.fill
-        , Element.height Element.fill
-        ]
-        (ui defaults)
-
-
-ui { background, viewBox, dotsAttributes } =
-    Svg.svg
-        [ Svg.Attributes.viewBox viewBox
-        , Svg.Attributes.style <| "background: " ++ background
-        ]
-        (dotsAttributes
-            |> List.map
-                (\attributes ->
-                    Svg.circle
-                        attributes
-                        []
-                )
-        )
-        |> Element.html
index eae8b16..b2da4d8 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -22,7 +22,6 @@ import Examples
=import Examples.CartesianCoordinates
=import Examples.Circle
=import Examples.Counter
-import Examples.DotInElement
=import Examples.Dots
=import Examples.Gradient
=import Examples.Line
@@ -459,7 +458,6 @@ document =
=
=        examples =
=            [ counter
-            , dotInElement
=            , dots
=            , circle
=            , line
@@ -490,20 +488,6 @@ document =
=            in
=            Mark.stub "Counter" render
=
-        dotInElement : Mark.Block (Examples.Model -> Element Msg)
-        dotInElement =
-            let
-                render config model =
-                    Examples.DotInElement.ui config
-            in
-            Mark.record4 "DotInElement"
-                Examples.DotInElement.Config
-                (Mark.field "background" Mark.string)
-                (Mark.field "fill" Mark.string)
-                (Mark.field "cx" Mark.string)
-                (Mark.field "cy" Mark.string)
-                |> Mark.map render
-
=        dots : Mark.Block (Examples.Model -> Element Msg)
=        dots =
=            let
@@ -516,10 +500,11 @@ document =
=
=                container : Mark.Block Examples.Dots.Container
=                container =
-                    Mark.record2 "Container"
+                    Mark.record3 "Container"
=                        Examples.Dots.Container
=                        (Mark.field "background" Mark.string)
=                        (Mark.field "viewBox" Mark.string)
+                        (Mark.field "fill" Mark.bool)
=
=                dot : Mark.Block Examples.Dots.Dot
=                dot =
@@ -530,48 +515,13 @@ document =
=                        (Mark.field "radius" Mark.float)
=                        (Mark.field "color" Mark.string)
=            in
-            {- FIXME: This should really be:
-
-                   Mark.block "Dots"
-                       render
-                       (Mark.startWith
-                           Examples.Dots.Config
-                           container
-                           (Mark.manyOf [ dot ])
-                       )
-
-               and then markup:
-
-                   | Dots
-                       | Container
-                           background = brown
-                           viewBox = -200 -200 400 400
-
-                       | Dot
-                           cx = 10
-                           cy = 20
-                           color = blue
-                           radius = 33
-
-                       | Dot
-                           ...
-
-               but it produces parsing errors:
-
-                   End at 36:1
-                   Expecting newline at 36:1
-                   Block start at 36:1
-
-               TODO: Create a minimal reproducible example on Ellie and report issue at github.com/mdgriffith/elm-markup
-            -}
=            Mark.block "Dots"
-                (Examples.Dots.Config
-                    { background = "none"
-                    , viewBox = "-300 -300 600 600"
-                    }
-                    >> render
+                render
+                (Mark.startWith
+                    Examples.Dots.Config
+                    container
+                    (Mark.manyOf [ dot ])
=                )
-                (Mark.manyOf [ dot ])
=
=        circle : Mark.Block (Examples.Model -> Element Msg)
=        circle =

Merge branch 'content' into wip-dots-example

On by Tadeusz Łazurski

Add a little spacing between lines in the Terminal block

On by Tadeusz Łazurski

It's easier to read that way.

index 8430329..ddbec4e 100644
--- a/src/Mark/Custom.elm
+++ b/src/Mark/Custom.elm
@@ -235,6 +235,7 @@ terminal =
=                            ]
=                        , Font.color colors.gray
=                        , Element.scrollbarY
+                        , Element.spacing 6
=                        ]
=                ]
=    in

Write the section about rules based tree on day 4

On by Tadeusz Łazurski

index 3dbc52a..e5d8848 100644
--- a/content/day-4.txt
+++ b/content/day-4.txt
@@ -365,7 +365,6 @@ A segment within a segment! Consider that every segment can have child segments.
=    Time to play 🌳🌲🌴🎄
=
=| Emphasize
-
=    Try building your own tree this way. Play with colors and sizes.
=
=| Note
@@ -397,3 +396,372 @@ The tree following these rules will look like this:
=
=| Note
=    TODO: Example
+
+To represent this in code we will need to learn few new concepts: *dictionary*, *record*, *type alias* and *mapping over list*.
+
+Let's start with a record. Currently our {Code|segment} function takes three arguments first two are {Code|color : String} and {Code|rotation : Float}. The last one is a list of child segments. Instead we can merge the first two into one argument of type {Code|\{ color : String, rotation : Float \}}. It will look like this:
+
+| Code
+    module Main exposing (main)
+
+    import Element
+    import Svg exposing (Svg)
+    import Svg.Attributes
+
+
+    main =
+        [ segment { color = "skyblue", rotation = 0 }
+            [ segment { color = "red", rotation = 15 } []
+            , segment { color = "blue", rotation = -15 }
+                [ segment { color = "yellow", rotation = 45 } []
+                , segment { color = "pink", rotation = -20 } []
+                , segment { color = "green", rotation = -90 } []
+                ]
+            ]
+        , segment { color = "orange", rotation = 72 } []
+        , segment { color = "red", rotation = 144 } []
+        , segment { color = "lime", rotation = 216 } []
+        , segment { color = "maroon", rotation = 288 } []
+        ]
+            |> Svg.svg
+                [ Svg.Attributes.height "100%"
+                , Svg.Attributes.width "100%"
+                , Svg.Attributes.style "background: none"
+                , Svg.Attributes.viewBox "-500 -500 1000 1000"
+                ]
+            |> Element.html
+            |> Element.layout
+                [ Element.width Element.fill
+                , Element.height Element.fill
+                ]
+
+
+    segment { color, rotation } children =
+        Svg.g []
+            [ Svg.g
+                [ Svg.Attributes.transform
+                    (String.concat
+                        [ "rotate("
+                        , String.fromFloat rotation
+                        , ") translate(80)"
+                        ]
+                    )
+                ]
+                children
+            , dot color rotation
+            , line color rotation
+            ]
+
+
+    dot color rotation =
+        Svg.circle
+            [ Svg.Attributes.r "10"
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ") translate(80)"
+                    ]
+                )
+            ]
+            []
+
+
+    line color rotation =
+        Svg.line
+            [ Svg.Attributes.strokeWidth "1"
+            , Svg.Attributes.x1 "0"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.x1 "80"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.stroke color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ")"
+                    ]
+                )
+            ]
+            []
+
+Not much changed. Instead of passing two separate values for {Code|color} and {Code|rotation} we pass one value with two named fields. That's a record!
+
+Why did I do it? That way I can store complete information about a segment in one value. I need this for the *dictionary* of rules.
+
+Dictionary let's us associate one value (let's say color) with another value (let's say a list of segments). You can have as many entries in your dictionary as you want. That's perfect for storing our rules. We can start with an empty dictionary and then add our two rules: one for brown and one for green segments. We do it like this:
+
+| Code
+    module Main exposing (main)
+
+    import Dict
+    import Element
+    import Svg exposing (Svg)
+    import Svg.Attributes
+
+
+    main =
+        [ segment { color = "skyblue", rotation = 0 }
+            [ segment { color = "red", rotation = 15 } []
+            , segment { color = "blue", rotation = -15 }
+                [ segment { color = "yellow", rotation = 45 } []
+                , segment { color = "pink", rotation = -20 } []
+                , segment { color = "green", rotation = -90 } []
+                ]
+            ]
+        , segment { color = "orange", rotation = 72 } []
+        , segment { color = "red", rotation = 144 } []
+        , segment { color = "lime", rotation = 216 } []
+        , segment { color = "maroon", rotation = 288 } []
+        ]
+            |> Svg.svg
+                [ Svg.Attributes.height "100%"
+                , Svg.Attributes.width "100%"
+                , Svg.Attributes.style "background: none"
+                , Svg.Attributes.viewBox "-500 -500 1000 1000"
+                ]
+            |> Element.html
+            |> Element.layout
+                [ Element.width Element.fill
+                , Element.height Element.fill
+                ]
+
+
+    rules =
+        Dict.empty
+            |> Dict.insert "brown"
+                [ { color = "green", rotation = 0 }
+                , { color = "green", rotation = 20 }
+                , { color = "green", rotation = -30 }
+                ]
+            |> Dict.insert "green"
+                [ { color = "red", rotation = -45 }
+                , { color = "red", rotation = -5 }
+                , { color = "red", rotation = 50 }
+                ]
+
+
+    segment { color, rotation } children =
+        Svg.g []
+            [ Svg.g
+                [ Svg.Attributes.transform
+                    (String.concat
+                        [ "rotate("
+                        , String.fromFloat rotation
+                        , ") translate(80)"
+                        ]
+                    )
+                ]
+                children
+            , dot color rotation
+            , line color rotation
+            ]
+
+
+    dot color rotation =
+        Svg.circle
+            [ Svg.Attributes.r "10"
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ") translate(80)"
+                    ]
+                )
+            ]
+            []
+
+
+    line color rotation =
+        Svg.line
+            [ Svg.Attributes.strokeWidth "1"
+            , Svg.Attributes.x1 "0"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.x1 "80"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.stroke color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ")"
+                    ]
+                )
+            ]
+            []
+
+We have added an import statement for {Code|Dict} module and created a dictionary with two entries: one for {Code|"brown"} and one for {Code|"green"}. The values associated with these keys are a list of segment records. The meaning of this is following:
+
+| List
+    - Whenever a brown segment is created, also create three green children
+    - Whenever a green segment is created, also create three red children
+
+There is no rule for red segments, so they will have no children. Now we will apply the rules. We have to change the definition of {Code|segment} function. It will no longer take the second argument - children will be added according to rules, not arbitrarily.
+
+Here it is:
+
+| Code
+    segment { color, rotation } =
+        Svg.g []
+            [ rules
+                |> Dict.get color
+                |> Maybe.withDefault []
+                |> List.map segment
+                |> Svg.g
+                    [ Svg.Attributes.transform
+                        (String.concat
+                            [ "rotate("
+                            , String.fromFloat rotation
+                            , ") translate(80)"
+                            ]
+                        )
+                    ]
+            , dot color rotation
+            , line color rotation
+            ]
+
+We also have to change the definition of {Code|main}. Segment no longer takes two arguments, and it's children are calculated according to rules, so we can just add one brown segment and rotate it upward (-90 degree)
+
+| Code
+    main =
+        [ segment { color = "brown", rotation = -90 } ]
+            |> Svg.svg
+                [ Svg.Attributes.height "100%"
+                , Svg.Attributes.width "100%"
+                , Svg.Attributes.style "background: none"
+                , Svg.Attributes.viewBox "-500 -500 1000 1000"
+                ]
+            |> Element.html
+            |> Element.layout
+                [ Element.width Element.fill
+                , Element.height Element.fill
+                ]
+
+The result should be exactly like we wanted it to be:
+
+| Monospace
+    TODO: Implement tree example
+
+    | Tree
+        | Axiom
+            color = brown
+            rotation = -90
+
+        | Rule
+            | Parent
+                color = brown
+
+            | Child
+              color = "green"
+              rotation = 0
+
+            | Child
+              color = "green"
+              rotation = 20
+
+            | Child
+              color = "green"
+              rotation = -30
+
+        | Rule
+            | Parent
+                color = green
+
+            | Child
+              color = "red"
+              rotation = -45
+
+            | Child
+              color = "red"
+              rotation = -5
+
+            | Child
+              color = "red"
+              rotation = 50
+
+Nice, but what's going on here:
+
+| Code
+    segment { color, rotation } =
+        Svg.g []
+            [ rules
+                |> Dict.get color
+                |> Maybe.withDefault []
+                |> List.map segment
+                |> Svg.g
+                    [ Svg.Attributes.transform
+                        (String.concat
+                            [ "rotate("
+                            , String.fromFloat rotation
+                            , ") translate(80)"
+                            ]
+                        )
+                    ]
+            , dot color rotation
+            , line color rotation
+            ]
+
+There are three new things here:
+
+| List
+    #. {Code|Dict.get}
+
+    #. {Code|Maybe.withDefault}
+
+    #. {Code|List.map}
+
+The first one is pretty simple. Previously we have been inserting entries into the dictionary. Each entry has a key (color of the segment) and value (list of child segments). Now we are getting these values back.
+
+But what if the entry for a given key is not there? For example we have not inserted the entry for the {Code|"red"} key? Well, then we get {Code|Nothing}. But our {Code|segment} function cannot work with nothing - it has to get a record with color and rotation. So when we have nothing, we can't call the {Code|segment} function. To understand this, consider the following.
+
+If there is an entry, we will get a list of records. We want a list of segments. Each of the records can be passed to the {Code|segment} function to produce one segment. That's where {Code|List.map} comes in. Given a list and a function it will call the function with each of the elements in the list and give back the list of produced values. Let's see a simpler example in the REPL:
+
+| Terminal
+    > fun something = something ++ " is fun!"
+    <function> : String -> String
+    > fun "Learning Elm"
+    "Learning Elm is fun!" : String
+    > fun "Tree"
+    "Tree is fun!" : String
+    > fun "Software"
+    "Software is fun!" : String
+    > things = [ "Elm", "Software", "Tree" ]
+    ["Elm","Software","Tree"] : List String
+    > List.map fun things
+    ["Elm is fun!","Software is fun!","Tree is fun!"] : List String
+    >
+
+Take some time to grasp it and then compare with our code.
+
+| Monospace
+    rules
+        |> Dict.get color
+        |> Maybe.withDefault []
+        |> List.map segment
+        |> Svg.g
+            [ Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ") translate(80)"
+                    ]
+                )
+            ]
+
+Let's analyze it line by line:
+
+| List
+    #. First line evaluates to a dictionary of rules.
+    #. Second takes that dictionary and gets a list of records for a given color (it maybe nothing if there is no entry, for example in case of {Code|"red"}).
+    #. Third gives an empty list if the result of second line was nothing.
+    #. Fourth maps this list of records with {Code|segment} function, producing a list of segments (the result can be an empty list if the input was empty)
+    #. Finally we pass the list to the {Code|Svg.g} function, producing a single group (again, possibly empty).
+
+It's a lot to absorb, so take your time to think about it.
+

Update day-2 to use new Dots block.

On by Fana

index 5c27c5f..d0a4ad1 100644
--- a/content/day-2.txt
+++ b/content/day-2.txt
@@ -26,34 +26,11 @@ Previously we have created a program that displays one dot in the center of the
=        radius = 80
=        scatter = False
=
-
-| Window
-    | Dots
-        | Dot
-            cx = 0
-            cy = 0
-            color = skyblue
-            radius = 10
-
-        | Dot
-            cx = -50
-            cy = 0
-            color = pink
-            radius = 10
-
-        | Dot
-            cx = 50
-            cy = 0
-            color = lime
-            radius = 10
-
=| Header
=    Multiple Dots
=
-| Note
-    See the FIXME comment in {Code|Main.elm} on {Code|wip-dots-example} branch.
=
-First obvious difference is that now we have multiple dots. The only dot we have is created using {Code|Svg.circle} function on line 9 - 15, this code block:
+First obvious difference is that now we have multiple dots. The only dot we have created using {Code|Svg.circle} function on line 9 - 15, this code block:
=
=| Monospace
=    Svg.circle
@@ -103,20 +80,24 @@ If you look closely in your source code, the block above is surrounded by {Code|
=
=Now the list spans from lines 9 to 23 and contains two items: skyblue and orange dots. The result should be:
=
-| Monospace
-    | Window
-        | Dots
-            | Dot
-                cx = 0
-                cy = 0
-                color = skyblue
-                radius = 10
-
-            | Dot
-                cx = 0
-                cy = 0
-                color = orange
-                radius = 10
+| Window
+    | Dots
+        | Container
+            background = none
+            fill = True
+            viewBox = -300 -300 600 600
+
+        | Dot
+            radius = 10
+            cx = 0
+            cy = 0
+            color = skyblue
+
+        | Dot
+            radius = 10
+            cx = 0
+            cy = 0
+            color = orange
=
=Oh oh! The list contains two dots, but we can only see the orange one on the screen. Where is the blue one? It's behind the orange. Recall how the cartesian coordinates work: if {Code|x} and {Code|y} of the center are the same, then the dots are in the same place. If we want to see both dots, let's move one of them to a different place, like this:
=
@@ -157,20 +138,24 @@ Oh oh! The list contains two dots, but we can only see the orange one on the scr
=
=Now you should see this:
=
-| Monospace
-    | Window
-        | Dots
-            | Dot
-                cx = 0
-                cy = 0
-                color = skyblue
-                radius = 10
-
-            | Dot
-                cx = 50
-                cy = 0
-                color = orange
-                radius = 10
+| Window
+    | Dots
+        | Container
+            background = none
+            fill = True
+            viewBox = -300 -300 600 600
+
+        | Dot
+            radius = 10
+            cx = 0
+            cy = 0
+            color = skyblue
+
+        | Dot
+            radius = 10
+            cx = 50
+            cy = 0
+            color = orange
=
=We can add more dots, by simply multiplying the code blocks inside the list. Each block must be separated from others with a {Code|,} (comma). Each time the {Code|cx} and {Code|cy} coordinates need to be different. We can also give dots different colors. Make five of them.
=
@@ -238,47 +223,41 @@ The {Code|r} attribute stands for radius. Then it was basically dictating the si
=        radius = 80
=
=| Row
-    | Monospace
-        TODO: Dots example
-        | Dots
-            | Container
-                viewBox = -100 -100 200 200
-                background = none
-                fill = True
-
-            | Dot
-                radius = 40
-                cx = 0
-                cy = 0
-                color = skyblue
+    | Dots
+        | Container
+            background = none
+            fill = True
+            viewBox = -100 -100 200 200
=
-    | Monospace
-        TODO: Dots example
-        | Dots
-            | Container
-                viewBox = -100 -100 200 200
-                background = none
-                fill = True
-
-            | Dot
-                radius = 60
-                cx = 0
-                cy = 0
-                color = skyblue
+        | Dot
+            radius = 40
+            cx = 0
+            cy = 0
+            color = skyblue
=
-    | Monospace
-        TODO: Dots example
-        | Dots
-            | Container
-                viewBox = -100 -100 200 200
-                background = none
-                fill = True
-
-            | Dot
-                radius = 80
-                cx = 0
-                cy = 0
-                color = skyblue
+    | Dots
+        | Container
+            background = none
+            fill = True
+            viewBox = -100 -100 200 200
+
+        | Dot
+            radius = 60
+            cx = 0
+            cy = 0
+            color = skyblue
+
+    | Dots
+        | Container
+            background = none
+            fill = True
+            viewBox = -100 -100 200 200
+
+        | Dot
+            radius = 80
+            cx = 0
+            cy = 0
+            color = skyblue
=
=This time the radius will affect the size of an imaginary circle on which the dots are laying.
=
@@ -387,8 +366,6 @@ In physical world we often use a tool called protractor to measure angles. It ty
=    radius = 400
=    strokeWidth = 2
=
-| Note
-    TODO: Modify the image to make marks 0 - 360. We can convert it to Elm code (using https:////level.app//svg-to-elm) and then reuse it in our examples.
=
=Now if we make the turns of the ruler equal between placing each dot, then the dots will be placed evenly. Equal turns mean that the number of degrees covered by each turn will be the same, including the turn you would need to make to go from the last dot back to the first. But how many degrees shall it be?
=
@@ -558,7 +535,7 @@ For that we need to add another transformation before the move. First we will ro
=        []
=    ]
=
-This will rotate the element by 72 degrees. On its own it wouldn't make any difference, because the dot is round - it doesn't matter how you rotate it, it always looks the same. But It also rotates its internal coordinates system. So the translation will move it in a different direction then the first dot.
+This will rotate the element by 72 degrees. On its own it wouldn't make any difference, because the dot is round - it doesn't matter how you rotate it, it always looks the same. But It also rotates it's internal coordinates system. So the translation will move it in a different direction then the first dot.
=
=Now it's time to add the third, red dot. Duplicate the second one, change fill to `"red"` and put `144` for rotate function, like this:
=

Merge remote-tracking branch 'origin/wip-dots-example' into content

On by Tadeusz Łazurski

Write section about halting on day 4

On by Tadeusz Łazurski

index e5d8848..9617799 100644
--- a/content/day-4.txt
+++ b/content/day-4.txt
@@ -652,6 +652,8 @@ The result should be exactly like we wanted it to be:
=        | Axiom
=            color = brown
=            rotation = -90
+            age = -1
+            growing = False
=
=        | Rule
=            | Parent
@@ -765,3 +767,176 @@ Let's analyze it line by line:
=
=It's a lot to absorb, so take your time to think about it.
=
+| Header
+    Towards Infinity
+
+Our tree is very cool but rather simple. Wouldn't it be nice if it could grow bigger and bigger? What if we could say that a brown segment produces two green (left and right) and a brown (straight)? Then the brown child would have two green and a brown too. But brown gives more brown /ad infinitum/ (pardon my latin).
+
+You can try that, but don't expect to see anything interesting in the browser. Your computer will trying very hard to draw a tree for you, but after going through hundreds of thousands of segments' generations it will give up, perhaps saying "too much recursion". According to the rules you gave there is no stopping to this tree.
+
+| Note
+    A funny fact about computers is that they generally can't tell if the task you give them can be finished or not. It's called {Link|the halting problem|url=https://en.wikipedia.org/wiki/Halting_problem}. Best they can do is give up after certain time. That's what happens here.
+
+So that won't work. But it would be nice to have arbitrarily complex trees with repeating patterns - that's how real trees grow. Since our problem is not ever stopping, maybe we can tell the program to simply stop after certain number of generations.
+
+We can do it like this. When we create our first segment (the brown one), we will pass it a second argument (let's call it {Code|age}). It will be an integer. The age of a parent will be decremented and passed to all it's children (so every child is younger than it's parent by one generation). Eventually the program will reach segments with {Code|age} being {Code|0}. This segment will have no children, thus stopping the whole process. Here is how we can implement it:
+
+| Code
+    module Main exposing (main)
+
+    import Dict
+    import Element
+    import Svg exposing (Svg)
+    import Svg.Attributes
+
+
+    main =
+        [ segment 4 { color = "brown", rotation = -90 } ]
+            |> Svg.svg
+                [ Svg.Attributes.height "100%"
+                , Svg.Attributes.width "100%"
+                , Svg.Attributes.style "background: none"
+                , Svg.Attributes.viewBox "-500 -500 1000 1000"
+                ]
+            |> Element.html
+            |> Element.layout
+                [ Element.width Element.fill
+                , Element.height Element.fill
+                ]
+
+
+    rules =
+        Dict.empty
+            |> Dict.insert "brown"
+                [ { color = "brown", rotation = 0 }
+                , { color = "green", rotation = 20 }
+                , { color = "green", rotation = -30 }
+                ]
+            |> Dict.insert "green"
+                [ { color = "red", rotation = -45 }
+                , { color = "red", rotation = -5 }
+                , { color = "red", rotation = 50 }
+                ]
+
+
+    segment age { color, rotation } =
+        if age == 0 then
+            Svg.g [] []
+
+        else
+            Svg.g []
+                [ rules
+                    |> Dict.get color
+                    |> Maybe.withDefault []
+                    |> List.map (segment (age - 1))
+                    |> Svg.g
+                        [ Svg.Attributes.transform
+                            (String.concat
+                                [ "rotate("
+                                , String.fromFloat rotation
+                                , ") translate(80)"
+                                ]
+                            )
+                        ]
+                , dot color rotation
+                , line color rotation
+                ]
+
+
+    dot color rotation =
+        Svg.circle
+            [ Svg.Attributes.r "10"
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ") translate(80)"
+                    ]
+                )
+            ]
+            []
+
+
+    line color rotation =
+        Svg.line
+            [ Svg.Attributes.strokeWidth "1"
+            , Svg.Attributes.x1 "0"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.x1 "80"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.stroke color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ")"
+                    ]
+                )
+            ]
+            []
+
+On line 10 we pass the age to the {Code|segment} function as the first argument. The record is a second parameter  now. The reason is that it makes mapping on line 47 easier. Age is the same for all the children (age of the parent minus 1).
+
+On line 39 we see something new. It's the {Code|if ... then ... else ...} expression. You can probably guess how it works: first we give it a condition (in this case {Code|age == 0} - notice the double {Code|=}). That's our halting condition.
+
+In the {Code|then} block we put the value to produce if the condition is met (here an empty SVG group). This will effectively stop the growth of the tree - no more children from this segment.
+
+The {Code|else} block contains the value to be produced when the condition is not met. In our case it means that the segment is old enough to have children.
+
+The {Code|else} value is almost the same as the whole {Code|segment} function before the change. The only difference is that on line 47 we pass the {Code|age - 1} to the {Code|segment} function used in {Code|List.map}.
+
+The result should be like this:
+
+| Monospace
+    TODO: Implement tree example
+
+    | Tree
+        | Axiom
+            color = brown
+            rotation = -90
+            age = 4
+            growing = False
+
+        | Rule
+            | Parent
+                color = brown
+
+            | Child
+              color = "green"
+              rotation = 0
+
+            | Child
+              color = "green"
+              rotation = 20
+
+            | Child
+              color = "green"
+              rotation = -30
+
+        | Rule
+            | Parent
+                color = green
+
+            | Child
+              color = "red"
+              rotation = -45
+
+            | Child
+              color = "red"
+              rotation = -5
+
+            | Child
+              color = "red"
+              rotation = 50
+
+| Emphasize
+    Go ahead and play with the rules and age.
+
+| Emphasize
+    😺🎾
+
+| Emphasize
+    It's safe - the tree will always stop growing after a given number of generations.

Write section about segments growing on day 4

On by Tadeusz Łazurski

index 9617799..f731a33 100644
--- a/content/day-4.txt
+++ b/content/day-4.txt
@@ -940,3 +940,205 @@ The result should be like this:
=
=| Emphasize
=    It's safe - the tree will always stop growing after a given number of generations.
+
+| Header
+    Every Branch was Once a Twig
+
+I hope you had fun playing with the tree. You can make it really interesting and complicated now. But it doesn't look very much like a real, organic tree. One problem is that with a real tree old branches are thick and long, while young are thin and short.
+
+Since the size depends on the age, and the age is given for every segment, we can fix that relatively easy. Let's start by making the dots smaller or bigger depending on the age of the segment, like this:
+
+| Code
+    segment age { color, rotation } =
+        if age == 0 then
+            Svg.g [] []
+
+        else
+            Svg.g []
+                [ rules
+                    |> Dict.get color
+                    |> Maybe.withDefault []
+                    |> List.map (segment (age - 1))
+                    |> Svg.g
+                        [ Svg.Attributes.transform
+                            (String.concat
+                                [ "rotate("
+                                , String.fromFloat rotation
+                                , ") translate(80)"
+                                ]
+                            )
+                        ]
+                , dot age color rotation
+                , line color rotation
+                ]
+
+
+    dot age color rotation =
+        Svg.circle
+            [ Svg.Attributes.r (String.fromFloat age)
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ") translate(80)"
+                    ]
+                )
+            ]
+            []
+
+| Monospace
+    TODO: Describe the change to code
+
+Next let's pass the age to the {Code|line} function and use it for both the thickness {Code|strokWidth} and length {Code|x2}.
+
+If you reload now, the tree will look as if it exploded. It's funny. The reason is that we did not adjust the translation of the child groups nor the dots. It's always 80, even though the length of the lines is variable - see lines {Code|TODO: line of group translation} and {Code|TODO: line of dot translation}. Let's change it like that:
+
+| Code
+    module Main exposing (main)
+
+    import Dict
+    import Element
+    import Svg exposing (Svg)
+    import Svg.Attributes
+
+
+    main =
+        [ segment 4 { color = "brown", rotation = -90 } ]
+            |> Svg.svg
+                [ Svg.Attributes.height "100%"
+                , Svg.Attributes.width "100%"
+                , Svg.Attributes.style "background: none"
+                , Svg.Attributes.viewBox "-500 -500 1000 1000"
+                ]
+            |> Element.html
+            |> Element.layout
+                [ Element.width Element.fill
+                , Element.height Element.fill
+                ]
+
+
+    rules =
+        Dict.empty
+            |> Dict.insert "brown"
+                [ { color = "brown", rotation = 0 }
+                , { color = "green", rotation = 20 }
+                , { color = "green", rotation = -30 }
+                ]
+            |> Dict.insert "green"
+                [ { color = "red", rotation = -45 }
+                , { color = "red", rotation = -5 }
+                , { color = "red", rotation = 50 }
+                ]
+
+
+    segment age { color, rotation } =
+        if age == 0 then
+            Svg.g [] []
+
+        else
+            Svg.g []
+                [ rules
+                    |> Dict.get color
+                    |> Maybe.withDefault []
+                    |> List.map (segment (age - 1))
+                    |> Svg.g
+                        [ Svg.Attributes.transform
+                            (String.concat
+                                [ "rotate("
+                                , String.fromFloat rotation
+                                , ") translate("
+                                , String.fromFloat (age * 10)
+                                , ")"
+                                ]
+                            )
+                        ]
+                , dot age color rotation
+                , line age color rotation
+                ]
+
+
+    dot age color rotation =
+        Svg.circle
+            [ Svg.Attributes.r (String.fromFloat age)
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ") translate("
+                    , String.fromFloat (age * 10)
+                    , ")"
+                    ]
+                )
+            ]
+            []
+
+
+    line age color rotation =
+        Svg.line
+            [ Svg.Attributes.strokeWidth "1"
+            , Svg.Attributes.x1 "0"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.x1 (String.fromFloat (age * 10))
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.stroke color
+            , Svg.Attributes.strokeWidth (String.fromFloat age)
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ")"
+                    ]
+                )
+            ]
+            []
+
+Now the tree should look correctly, like this:
+
+| Monospace
+    TODO: Implement tree example
+
+    | Tree
+        | Axiom
+            color = brown
+            rotation = -90
+            age = 4
+            growing = True
+
+        | Rule
+            | Parent
+                color = brown
+
+            | Child
+              color = "green"
+              rotation = 0
+
+            | Child
+              color = "green"
+              rotation = 20
+
+            | Child
+              color = "green"
+              rotation = -30
+
+        | Rule
+            | Parent
+                color = green
+
+            | Child
+              color = "red"
+              rotation = -45
+
+            | Child
+              color = "red"
+              rotation = -5
+
+            | Child
+              color = "red"
+              rotation = 50
+

Add congratulations block to day 4

On by Tadeusz Łazurski

index f731a33..98e45c9 100644
--- a/content/day-4.txt
+++ b/content/day-4.txt
@@ -1142,3 +1142,11 @@ Now the tree should look correctly, like this:
=              color = "red"
=              rotation = 50
=
+| Emphasize
+    Congratulations!
+
+| Emphasize
+    {Icon|name=award}
+
+| Emphasize
+    You are ready for {Link|the final day|url=/day-4.html}!

Fix mistakes on day 5 header

On by Tadeusz Łazurski

index e54b4b9..b7f1ebe 100644
--- a/content/day-5.txt
+++ b/content/day-5.txt
@@ -1,8 +1,14 @@
=| Title
-    Day 3
+    Day 5
=
=| Emphasize
-    Let's Make the Tree Grow
+    Let the Tree Grow
+
+
+| Note
+    *We are still working on this content.*
+
+    Stay tuned {Icon|name=radio}
=
=
=| Note
@@ -15,7 +21,4 @@
=| Header
=    The Problem
=
-| Note
-    *We are still working on this content.*
=
-    Stay tuned {Icon|name=radio}

Fix bug in halting condition of segment (day 4)

On by Tadeusz Łazurski

index 98e45c9..1816461 100644
--- a/content/day-4.txt
+++ b/content/day-4.txt
@@ -820,7 +820,7 @@ We can do it like this. When we create our first segment (the brown one), we wil
=
=
=    segment age { color, rotation } =
-        if age == 0 then
+        if age <= 0 then
=            Svg.g [] []
=
=        else
@@ -880,7 +880,7 @@ We can do it like this. When we create our first segment (the brown one), we wil
=
=On line 10 we pass the age to the {Code|segment} function as the first argument. The record is a second parameter  now. The reason is that it makes mapping on line 47 easier. Age is the same for all the children (age of the parent minus 1).
=
-On line 39 we see something new. It's the {Code|if ... then ... else ...} expression. You can probably guess how it works: first we give it a condition (in this case {Code|age == 0} - notice the double {Code|=}). That's our halting condition.
+On line 39 we see something new. It's the {Code|if ... then ... else ...} expression. You can probably guess how it works: first we give it a condition (in this case {Code|age <= 0} - meaning that age is zero or less). That's our halting condition.
=
=In the {Code|then} block we put the value to produce if the condition is met (here an empty SVG group). This will effectively stop the growth of the tree - no more children from this segment.
=
@@ -950,7 +950,7 @@ Since the size depends on the age, and the age is given for every segment, we ca
=
=| Code
=    segment age { color, rotation } =
-        if age == 0 then
+        if age <= 0 then
=            Svg.g [] []
=
=        else
@@ -1035,7 +1035,7 @@ If you reload now, the tree will look as if it exploded. It's funny. The reason
=
=
=    segment age { color, rotation } =
-        if age == 0 then
+        if age <= 0 then
=            Svg.g [] []
=
=        else

On by Tadeusz Łazurski

index 1816461..03acab1 100644
--- a/content/day-4.txt
+++ b/content/day-4.txt
@@ -1149,4 +1149,4 @@ Now the tree should look correctly, like this:
=    {Icon|name=award}
=
=| Emphasize
-    You are ready for {Link|the final day|url=/day-4.html}!
+    You are ready for {Link|the final day|url=/day-5.html}!

Draft day 5

On by Tadeusz Łazurski

index b7f1ebe..ce02674 100644
--- a/content/day-5.txt
+++ b/content/day-5.txt
@@ -21,4 +21,372 @@
=| Header
=    The Problem
=
+We have a nice picture of a tree, but ultimately it may have been easier to just draw it using some graphics program. We promised you a growing tree, so let's make it grow like this:
=
+| Monospace
+    TODO: Implement growing tree example
+
+    | AnimatedTree
+        | Axiom
+            color = brown
+            rotation = -90
+            minAge = 0
+            maxAge = 10
+
+        | Rule
+            | Parent
+                color = brown
+
+            | Child
+              color = "green"
+              rotation = 0
+
+            | Child
+              color = "green"
+              rotation = 20
+
+            | Child
+              color = "green"
+              rotation = -30
+
+        | Rule
+            | Parent
+                color = green
+
+            | Child
+              color = "red"
+              rotation = -45
+
+            | Child
+              color = "red"
+              rotation = -5
+
+            | Child
+              color = "red"
+              rotation = 50
+
+| Header
+    The Elm Architecture
+
+So far our programs were very static. The value of {Code|main} would be evaluated once, painted on the screen and stay there forever. Now we need to make our program react to the events in outside world. What events? A passage of time {Icon|name=watch} If we want our tree to grow over time, we need to know how much time have passed, so we can update it's age accordingly.
+
+In Elm we can do it using a different type of {Code|main}. One that ties together four functions:
+
+| List
+    #. {Code|init} specifying what's the initial state of the running program
+
+    #. {Code|subscriptions} specifying what events we are expecting and what messages will be sent when they happen
+
+    #. {Code|update} specifying how the state of the application will change when messages are received
+
+    #. {Code|view} specifying what value should be displayed depending on the current state.
+
+State is sometimes called model. There are also commands, but we will not use them in our program.
+
+| Image
+    src = /assets/Elm MVU architecture.jpg
+    description = "Model View Update - The Elm Architecture"
+
+| Monospace
+    TODO: Attribution
+
+First let's change the value of {Code|main} and create the {Code|view} function, like this:
+
+| Code
+    module Main exposing (main)
+
+    import Browser
+    import Dict
+    import Element
+    import Svg exposing (Svg)
+    import Svg.Attributes
+
+
+    main =
+        Browser.element
+            { init = init
+            , view = view
+            , update = update
+            , subscriptions = subscriptions
+            }
+
+
+    view age =
+        [ segment (age / 5000) { color = "brown", rotation = -90 } ]
+            |> Svg.svg
+                [ Svg.Attributes.height "100%"
+                , Svg.Attributes.width "100%"
+                , Svg.Attributes.style "background: none"
+                , Svg.Attributes.viewBox "-500 -500 1000 1000"
+                ]
+            |> Element.html
+            |> Element.layout
+                [ Element.width Element.fill
+                , Element.height Element.fill
+                ]
+
+
+    rules =
+        Dict.empty
+            |> Dict.insert "brown"
+                [ { color = "brown", rotation = 0 }
+                , { color = "green", rotation = 20 }
+                , { color = "green", rotation = -30 }
+                ]
+            |> Dict.insert "green"
+                [ { color = "red", rotation = -45 }
+                , { color = "red", rotation = -5 }
+                , { color = "red", rotation = 50 }
+                ]
+
+
+    segment age { color, rotation } =
+        if age <= 0 then
+            Svg.g [] []
+
+        else
+            Svg.g []
+                [ rules
+                    |> Dict.get color
+                    |> Maybe.withDefault []
+                    |> List.map (segment (age - 1))
+                    |> Svg.g
+                        [ Svg.Attributes.transform
+                            (String.concat
+                                [ "rotate("
+                                , String.fromFloat rotation
+                                , ") translate("
+                                , String.fromFloat (age * 10)
+                                , ")"
+                                ]
+                            )
+                        ]
+                , dot age color rotation
+                , line age color rotation
+                ]
+
+
+    dot age color rotation =
+        Svg.circle
+            [ Svg.Attributes.r (String.fromFloat age)
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ") translate("
+                    , String.fromFloat (age * 10)
+                    , ")"
+                    ]
+                )
+            ]
+            []
+
+
+    line age color rotation =
+        Svg.line
+            [ Svg.Attributes.strokeWidth "1"
+            , Svg.Attributes.x1 "0"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.x1 (String.fromFloat (age * 10))
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.stroke color
+            , Svg.Attributes.strokeWidth (String.fromFloat age)
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ")"
+                    ]
+                )
+            ]
+            []
+
+Notice that the {Code|view} takes an argument called {Code|age} and passes it to the first (brown) segment. The value of {Code|age} is our state. In some programs the type of state can be very complex, but in our program it's simply a {Code|Float} number. It will indicate how much time in milliseconds have passed from the start of the program. Because the time is counted in milliseconds, to get correct results we need to divide it by 5000 (so the tree will grows one generation of segments per five second).
+
+Now let's provide the init function. It takes an argument sometimes called flags, but we will ignore it. Let's just say that it will always receive {Code|()} - an empty value called unit. It needs to return two values bound together in a structure called /tuple/.
+
+| Note
+    Tuple is like a list, but have a fixed number of elements. Unlike the lists, elements of tuples can have different types.
+
+The second value is a command. We don't need any, so we just give it a {Code|Cmd.none}, which as you can guess produces a command that does nothing.
+
+First value is more important. This is the initial age of the tree, right after the program starts. Since no time have passed yet, we set it to 0.
+
+That's our init:
+
+| Code
+    init () =
+        ( 0, Cmd.none )
+
+Time for the {Code|update} function. It will get two arguments: an incoming message and the current state. Incoming message will always be a duration of time since previous frame, so let's simply call it {Code|duration}. The state is the current age of the tree, so again we can call it {Code|age}. In return we must produce a tuple with:
+
+| List
+    #. the new state ({Code|age + duration})
+    #. a command (we don't need it, so we give {Code|Cmd.none})
+
+It looks like this:
+
+| Code
+    update duration age =
+        ( age + duration
+        , Cmd.none
+        )
+
+Finally let's subscribe to the events marking the passage of time. We can do it using {Code|Browser.Events.onAnimationFrameDelta}. Here is how it works. Whenever the browser is ready for the next frame it will send us a message containing the number of milliseconds that have passed since previous frame.
+
+To use it we first need to import the {Code|Browser.Events} module. The {Code|Browser.Events.onAnimationFrameDelta} function expects us to give it a function that will get a duration and return a message. But our message is simply a duration! We don't need to do anything with it, just take it as it is. So we need a function that just returns whatever it gets. This function is called {Code|identity}.
+
+| Note
+    Perhaps you have noticed that we use a lot of functions. Functions are passed to functions that sometimes return functions 🤕 That's why Elm is called a *functional programming language*.
+
+The whole {Code|subscriptions} looks like that:
+
+| Code
+    subscriptions age =
+        Browser.Events.onAnimationFrameDelta identity
+
+And the complete code like this:
+
+| Code
+    module Main exposing (main)
+
+    import Browser
+    import Browser.Events
+    import Dict
+    import Element
+    import Svg exposing (Svg)
+    import Svg.Attributes
+
+
+    main =
+        Browser.element
+            { init = init
+            , view = view
+            , update = update
+            , subscriptions = subscriptions
+            }
+
+
+    init () =
+        ( 0, Cmd.none )
+
+
+    view age =
+        [ segment (age / 5000) { color = "brown", rotation = -90 } ]
+            |> Svg.svg
+                [ Svg.Attributes.height "100%"
+                , Svg.Attributes.width "100%"
+                , Svg.Attributes.style "background: none"
+                , Svg.Attributes.viewBox "-500 -500 1000 1000"
+                ]
+            |> Element.html
+            |> Element.layout
+                [ Element.width Element.fill
+                , Element.height Element.fill
+                ]
+
+
+    update duration age =
+        ( duration
+            |> Debug.log "Duration"
+            |> (+) age
+            |> Debug.log "Age"
+        , Cmd.none
+        )
+
+
+    subscriptions age =
+        Browser.Events.onAnimationFrameDelta identity
+
+
+    rules =
+        Dict.empty
+            |> Dict.insert "brown"
+                [ { color = "brown", rotation = 0 }
+                , { color = "green", rotation = 20 }
+                , { color = "green", rotation = -30 }
+                ]
+            |> Dict.insert "green"
+                [ { color = "red", rotation = -45 }
+                , { color = "red", rotation = -5 }
+                , { color = "red", rotation = 50 }
+                ]
+
+
+    segment age { color, rotation } =
+        if age <= 0 then
+            Svg.g [] []
+
+        else
+            Svg.g []
+                [ rules
+                    |> Dict.get color
+                    |> Maybe.withDefault []
+                    |> List.map (segment (age - 1))
+                    |> Svg.g
+                        [ Svg.Attributes.transform
+                            (String.concat
+                                [ "rotate("
+                                , String.fromFloat rotation
+                                , ") translate("
+                                , String.fromFloat (age * 10)
+                                , ")"
+                                ]
+                            )
+                        ]
+                , dot age color rotation
+                , line age color rotation
+                ]
+
+
+    dot age color rotation =
+        Svg.circle
+            [ Svg.Attributes.r (String.fromFloat age)
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ") translate("
+                    , String.fromFloat (age * 10)
+                    , ")"
+                    ]
+                )
+            ]
+            []
+
+
+    line age color rotation =
+        Svg.line
+            [ Svg.Attributes.strokeWidth "1"
+            , Svg.Attributes.x1 "0"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.x1 (String.fromFloat (age * 10))
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.stroke color
+            , Svg.Attributes.strokeWidth (String.fromFloat age)
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ")"
+                    ]
+                )
+            ]
+            []
+
+| Emphasize
+    That's it!
+
+| Emphasize
+    {Icon|name=flag}
+
+| Emphasize
+    Your tree is growing over time!
+
+We hope you had good time learning programming with us. If we still have some time left, we can play with the code.

Improve Fana's bio.

On by Fana

Also fixed some grammar

index e5d8848..9f75900 100644
--- a/content/day-4.txt
+++ b/content/day-4.txt
@@ -94,12 +94,12 @@ Type this code at the bottom of the file and then replace main with the followin
=
=Reload the browser. There should still be no visible difference in the behavior of the program, but our source code is getting more readable. That's good.
=
-The big difference between our program and the one we are trying to build is that our have only one level of segments, whereas in the example segments grow from the tip of other segments. You can think of it as segments having child segments: the green segment has two child brown segments. Each brown segment has three red child segments
+The big difference between our program and the one we are trying to build is that ours have only one level of segments, whereas in the example segments grow from the tip of other segments. You can think of it as segments having child segments: the green segment has two child brown segments. Each brown segment has three red child segments
=
=| Note
=    TODO: Make sure the description matches the example
=
-It's similar to how the way an SVG group has child elements. But group itself (together with it's children) is an element, so we can put a group within a group. Let's do that! Change the definition of {Code|segment} function as follows:
+It's similar to how the way an SVG group has child elements. But group itself (together with its children) is an element, so we can put a group within a group. Let's do that! Change the definition of {Code|segment} function as follows:
=
=| Code
=    segment color rotation =
@@ -764,4 +764,3 @@ Let's analyze it line by line:
=    #. Finally we pass the list to the {Code|Svg.g} function, producing a single group (again, possibly empty).
=
=It's a lot to absorb, so take your time to think about it.
-
index f181d18..c2939da 100644
--- a/content/index.txt
+++ b/content/index.txt
@@ -29,4 +29,4 @@ I love the creativity of the software development and hope to share that passion
=
=*Sam* is a co-author of the workshop. He is an Elm developer at {Link|itravel|url=https://www.itravel.de/} in Cologne, Germany.
=
-*Fana* is a coordinator of our project. She keeps us on the right track. Also she is taking care of our media presence.
+*Fana* is a junior software developer (an ex marine biologist) and coordinator of our project. She keeps us on the right track. Also she is taking care of our media presence.

Replaced old tree example with new tree

On by Fana

index eaed7b3..26d559e 100644
--- a/src/Examples/Tree.elm
+++ b/src/Examples/Tree.elm
@@ -1,291 +1,189 @@
=module Examples.Tree exposing
-    ( Model
-    , Msg
-    , init
+    ( Axiom
+    , Config
+    , Segment
=    , main
-    , subscriptions
=    , ui
-    , update
=    )
=
-import Browser
-import Browser.Events
=import Dict exposing (Dict)
=import Element exposing (Element)
-import Html exposing (Html)
-import Json.Decode exposing (Decoder)
-import List.Extra as List
-import Svg exposing (..)
-import Svg.Attributes exposing (..)
+import Svg exposing (Svg)
+import Svg.Attributes
=
=
-main : Program Flags Model Msg
-main =
-    Browser.element
-        { init = init
-        , view = view
-        , update = update
-        , subscriptions = subscriptions
+defaults : Config
+defaults =
+    { axiom =
+        { color = "brown"
+        , rotation = -90
+        , age = 8
=        }
+    , rules =
+        [ ( "brown"
+          , [ { color = "green"
+              , rotation = 45
+              }
+            , { color = "green"
+              , rotation = 20
+              }
+            , { color = "green"
+              , rotation = -20
+              }
+            ]
+          )
+        , ( "green"
+          , [ { color = "purple"
+              , rotation = 0
+              }
+            , { color = "purple"
+              , rotation = -90
+              }
+            , { color = "purple"
+              , rotation = -20
+              }
+            ]
+          )
+        , ( "purple"
+          , [ { color = "orange"
+              , rotation = 0
+              }
+            , { color = "orange"
+              , rotation = 60
+              }
+            , { color = "brown"
+              , rotation = -20
+              }
+            ]
+          )
+        ]
+    }
=
=
-type alias Flags =
-    ()
-
-
-type alias Model =
-    { time : Float
-    , rules : Rules
+type alias Config =
+    { axiom : Axiom
+    , rules : List Rule
=    }
=
=
-type Msg
-    = Progress Float
-    | Regress Float
+type alias Axiom =
+    { color : String
+    , rotation : Float
+    , age : Float
+    }
=
=
-type alias Color =
-    String
+type alias Rule =
+    ( String, List Segment )
=
=
=type alias Segment =
-    { color : Color
-    , angle : Float
-    , length : Float
-    }
-
+    { color : String, rotation : Float }
=
-type alias Rules =
-    Dict Color (List Segment)
=
-
-init : Flags -> ( Model, Cmd Msg )
-init () =
-    ( { time = 0
-      , rules =
-            Dict.fromList
-                [ ( "brown"
-                  , [ Segment "green" -115 1
-                    , Segment "green" -65 1
-                    , Segment "saddlebrown" 90 2
+ui : Config -> Element msg
+ui config =
+    let
+        dot age color rotation =
+            Svg.circle
+                [ Svg.Attributes.r (String.fromFloat age)
+                , Svg.Attributes.cx "0"
+                , Svg.Attributes.cy "0"
+                , Svg.Attributes.fill color
+                , Svg.Attributes.transform
+                    (String.concat
+                        [ "rotate("
+                        , String.fromFloat rotation
+                        , ") translate("
+                        , String.fromFloat (age * 10)
+                        , ")"
+                        ]
+                    )
+                ]
+                []
+
+        line age color rotation =
+            Svg.line
+                [ Svg.Attributes.strokeWidth "1"
+                , Svg.Attributes.x1 "0"
+                , Svg.Attributes.y1 "0"
+                , Svg.Attributes.x2 (String.fromFloat (age * 10))
+                , Svg.Attributes.y2 "0"
+                , Svg.Attributes.stroke color
+                , Svg.Attributes.strokeWidth (String.fromFloat age)
+                , Svg.Attributes.transform
+                    (String.concat
+                        [ "rotate("
+                        , String.fromFloat rotation
+                        , ")"
+                        ]
+                    )
+                ]
+                []
+
+        rules : Dict String (List Segment)
+        rules =
+            Dict.empty
+                |> Dict.insert "brown"
+                    [ { color = "green", rotation = 20 }
+                    , { color = "green", rotation = -20 }
+                    , { color = "brown", rotation = 0 }
=                    ]
-                  )
-                , ( "green"
-                  , [ Segment "green" 20 3
-                    , Segment "green" -20 3
-                    , Segment "red" 90 1
-                    , Segment "red" -90 1
+                |> Dict.insert "green"
+                    [ { color = "blue", rotation = -25 }
+                    , { color = "blue", rotation = -5 }
+                    , { color = "blue", rotation = 15 }
=                    ]
-                  )
-                , ( "saddlebrown"
-                  , [ Segment "saddlebrown" 20 3
-                    , Segment "saddlebrown" -20 3
+                |> Dict.insert "blue"
+                    [ { color = "brown", rotation = -25 }
+                    , { color = "purple", rotation = -5 }
+                    , { color = "purple", rotation = 25 }
=                    ]
-                  )
-                ]
-      }
-    , Cmd.none
-    )
-
-
-view : Model -> Html Msg
-view model =
-    Element.layout
-        [ Element.width Element.fill
-        , Element.height Element.fill
-        ]
-        (ui model)
-
=
-ui : Model -> Element Msg
-ui model =
-    let
-        gradients : List (Svg Msg)
-        gradients =
-            model.rules
-                |> Dict.map
-                    (\parentColor children ->
-                        children
-                            |> List.map (\child -> ( parentColor, child.color ))
-                    )
-                |> Dict.values
-                |> List.concat
-                |> List.unique
-                |> List.map
-                    (\(( start, end ) as colors) ->
-                        linearGradient
-                            [ id (gradientId colors)
-                            , x1 "0"
-                            , y1 "0"
-                            , x2 "1"
-                            , y2 "0"
-                            , gradientUnits "userSpaceOnUse"
+        segment : Float -> Segment -> Svg msg
+        segment age { color, rotation } =
+            if age <= 0 then
+                Svg.g [] []
+
+            else
+                Svg.g []
+                    [ config.rules
+                        |> Dict.fromList
+                        |> Dict.get color
+                        |> Maybe.withDefault []
+                        |> List.map (segment (age - 1))
+                        |> Svg.g
+                            [ Svg.Attributes.transform
+                                (String.concat
+                                    [ "rotate("
+                                    , String.fromFloat rotation
+                                    , ") translate("
+                                    , String.fromFloat (age * 10)
+                                    , ")"
+                                    ]
+                                )
=                            ]
-                            [ stop
-                                [ stopColor start, offset "0" ]
-                                []
-                            , stop
-                                [ stopColor end, offset "1" ]
-                                []
-                            ]
-                    )
-
-        tree =
-            "brown"
-                |> svgTree model.rules (model.time / 5000)
-                |> g
-                    [ transform
-                        ("scale("
-                            ++ String.fromFloat (model.time / 1000)
-                            ++ ")"
-                        )
+                    , dot age color rotation
+                    , line age color rotation
=                    ]
=    in
-    Element.html <|
-        svg
-            [ viewBox "-1000 -1000 2000 2000"
-            , height "100%"
-            , width "100%"
-            ]
-            [ defs [] gradients
-            , tree
+    [ segment config.axiom.age
+        { color = config.axiom.color
+        , rotation = config.axiom.rotation
+        }
+    ]
+        |> Svg.svg
+            [ Svg.Attributes.height "100%"
+            , Svg.Attributes.width "100%"
+            , Svg.Attributes.style "background: none"
+            , Svg.Attributes.viewBox "-500 -500 1000 1000"
=            ]
+        |> Element.html
=
=
-update : Msg -> Model -> ( Model, Cmd Msg )
-update msg model =
-    case msg of
-        Progress delta ->
-            ( { model | time = model.time + delta }
-            , Cmd.none
-            )
-
-        Regress delta ->
-            ( { model | time = model.time - delta }
-            , Cmd.none
-            )
-
-
-subscriptions : Model -> Sub Msg
-subscriptions model =
-    let
-        handleKeyPress : Decoder Msg
-        handleKeyPress =
-            Json.Decode.field "key" Json.Decode.string
-                |> Json.Decode.andThen
-                    (\key ->
-                        case key of
-                            "," ->
-                                Json.Decode.succeed (Regress 10)
-
-                            "." ->
-                                Json.Decode.succeed (Progress 10)
-
-                            "<" ->
-                                Json.Decode.succeed (Regress 1000)
-
-                            ">" ->
-                                Json.Decode.succeed (Progress 1000)
-
-                            _ ->
-                                Json.Decode.fail "Unknown key press"
-                    )
-    in
-    Sub.batch
-        [ Browser.Events.onKeyPress handleKeyPress
-        , Browser.Events.onAnimationFrameDelta Progress
-        ]
-
-
-svgTree : Rules -> Float -> Color -> List (Svg Msg)
-svgTree rules age color =
-    if age <= 0 then
-        []
-
-    else
-        let
-            subtrees =
-                rules
-                    |> Dict.get color
-                    |> Maybe.withDefault []
-                    |> List.map subtree
-
-            subtree segment =
-                let
-                    gradient =
-                        "url(#" ++ gradientId ( color, segment.color ) ++ ")"
-
-                    scale =
-                        age / (age + 1)
-                in
-                g
-                    [ applyTransformations
-                        [ Rotate segment.angle
-                        , Scale scale scale
-                        , Translate segment.length 0
-                        ]
-                    ]
-                    (line
-                        [ stroke gradient
-                        , x1 "0"
-                        , x2 "1"
-                        , y1 "0"
-                        , y2 "0"
-                        , strokeWidth "0.2"
-                        , strokeLinecap "round"
-                        , applyTransformations
-                            [ Translate (0 - segment.length) 0
-                            , Scale segment.length 1
-                            ]
-                        ]
-                        []
-                        :: svgTree rules (age - 1) segment.color
-                    )
-        in
-        subtrees
-
-
-type Transformation
-    = Identity
-    | Scale Float Float
-    | Translate Float Float
-    | Rotate Float
-
-
-applyTransformations : List Transformation -> Svg.Attribute Msg
-applyTransformations transformations =
-    let
-        toString : Transformation -> String
-        toString transformation =
-            case transformation of
-                Identity ->
-                    ""
-
-                Scale x y ->
-                    "scale("
-                        ++ String.fromFloat x
-                        ++ ", "
-                        ++ String.fromFloat y
-                        ++ ")"
-
-                Translate x y ->
-                    "translate("
-                        ++ String.fromFloat x
-                        ++ ", "
-                        ++ String.fromFloat y
-                        ++ ")"
-
-                Rotate angle ->
-                    "rotate("
-                        ++ String.fromFloat angle
-                        ++ ")"
-    in
-    transformations
-        |> List.map toString
-        |> String.join " "
-        |> transform
-
-
-gradientId : ( Color, Color ) -> String
-gradientId ( start, end ) =
-    "connection-" ++ start ++ "-" ++ end
+main =
+    ui defaults
+        |> Element.layout
+            [ Element.width Element.fill
+            , Element.height Element.fill
+            ]

Replace Code block with a new Edito block supporting highlits

On by Tadeusz Łazurski

On the way I separated the css function from the Mark.Custom to the Element.Extra module.

new file mode 100644
index 0000000..2553eb0
--- /dev/null
+++ b/src/Editor.elm
@@ -0,0 +1,296 @@
+module Editor exposing (Config, Highlight, defaults, editor)
+
+import Browser
+import Dict exposing (Dict)
+import Element exposing (Element)
+import Element.Background as Background
+import Element.Border as Border
+import Element.Extra as Element
+import Element.Font as Font
+import FeatherIcons exposing (icons)
+import Html exposing (Html)
+import List.Extra as List
+import Svg exposing (Svg)
+import Svg.Attributes
+
+
+type alias Config =
+    { path : String
+    , offset : Int
+    , colors :
+        { annotations : Element.Color
+        , background : Element.Color
+        , primary : Element.Color
+        , secondary : Element.Color
+        , window : Element.Color
+        }
+    }
+
+
+defaults : Config
+defaults =
+    { path = "src/Main.elm"
+    , offset = 1
+    , colors =
+        { primary = Element.rgb 0 0 0
+        , secondary = Element.rgb 0.8 0.8 0.8
+        , annotations = Element.rgb 1 0.6 0.6
+        , background = Element.rgb 1 1 1
+        , window = Element.rgb 0.2 0.2 0.2
+        }
+    }
+
+
+type alias Highlight =
+    { from : Int
+    , to : Int
+    , offset : Int
+    , width : Int
+    }
+
+
+highlight : Highlight -> Element msg
+highlight { from, to, offset, width } =
+    Element.row []
+        [ " "
+            |> String.repeat offset
+            |> Element.text
+        , " "
+            |> String.repeat width
+            |> List.repeat (to - from + 1)
+            |> List.map Element.text
+            |> List.map (Element.el [ Element.padding 10 ])
+            |> Element.column
+                [ Element.inFront
+                    (Element.el
+                        [ Element.width Element.fill
+                        , Element.height Element.fill
+                        , Border.color (Element.rgba 1 0 0 0.8)
+                        , Border.rounded 10
+                        , Border.width 2
+                        , Border.dashed
+                        , Element.scale 1.1
+                        ]
+                        Element.none
+                    )
+                , Element.css "pointer-events" "none"
+                , Element.css "user-select" "none"
+                , Element.css "-webkit-user-select" "none"
+                , Element.css "-ms-user-select" "none"
+                , Element.css "-webkit-touch-callout" "none"
+                , Element.css "-o-user-select" "none"
+                , Element.css "-moz-user-select" "none"
+                ]
+        ]
+
+
+editor : Config -> List Highlight -> String -> Element msg
+editor { path, offset, colors } highlights contents =
+    let
+        highlighted : Dict Int Highlight
+        highlighted =
+            highlights
+                |> List.foldl extract Dict.empty
+
+        extract item memo =
+            Dict.insert item.from item memo
+    in
+    Element.column
+        [ Border.width 3
+        , Border.rounded 5
+        , Border.color colors.window
+        , Element.css "page-break-inside" "avoid"
+        , Font.family
+            [ Font.typeface "Source Code Pro"
+            , Font.monospace
+            ]
+        , Element.css "page-break-inside" "avoid"
+        , Element.width Element.fill
+        ]
+        [ Element.row
+            [ Element.width Element.fill
+            , Background.color colors.window
+            , Font.color colors.secondary
+            ]
+            [ FeatherIcons.fileText
+                |> FeatherIcons.toHtml []
+                |> Element.html
+                |> Element.el
+                    [ Element.height (Element.px 35)
+                    , Element.padding 8
+                    ]
+            , Element.el
+                [ Element.width Element.fill
+                , Font.size 16
+                , Font.family
+                    [ Font.typeface "Source Code Pro"
+                    , Font.monospace
+                    ]
+                ]
+                (Element.text "src/Main.elm")
+            ]
+        , contents
+            |> String.lines
+            |> List.indexedMap
+                (\n loc ->
+                    Element.row []
+                        [ Element.el
+                            [ Font.color colors.secondary
+                            , Font.extraLight
+                            , Element.width (Element.px 40)
+                            , Element.padding 10
+                            , Font.alignRight
+                            , Element.css "user-select" "none"
+                            , Element.css "-webkit-user-select" "none"
+                            , Element.css "-ms-user-select" "none"
+                            , Element.css "-webkit-touch-callout" "none"
+                            , Element.css "-o-user-select" "none"
+                            , Element.css "-moz-user-select" "none"
+                            ]
+                            ((n + 1)
+                                |> String.fromInt
+                                |> Element.text
+                            )
+                        , Element.el
+                            [ Element.width Element.fill
+                            , Element.padding 10
+                            , highlighted
+                                |> Dict.get (n + 1)
+                                |> Maybe.map highlight
+                                |> Maybe.withDefault Element.none
+                                |> Element.inFront
+                            ]
+                            (Element.text loc)
+                        ]
+                )
+            |> Element.column
+                [ Element.width Element.fill
+                , Font.size 16
+                , Element.scrollbarY
+                ]
+        ]
+
+
+main : Html msg
+main =
+    """module Main exposing (main)
+
+import Browser
+import Dict
+import Element
+import Svg exposing (Svg)
+import Svg.Attributes
+
+
+main =
+    Browser.element
+        { init = init
+        , view = view
+        , update = update
+        , subscriptions = subscriptions
+        }
+
+
+view age =
+    [ segment (age / 5000) { color = "brown", rotation = -90 } ]
+        |> Svg.svg
+            [ Svg.Attributes.height "100%"
+            , Svg.Attributes.width "100%"
+            , Svg.Attributes.style "background: none"
+            , Svg.Attributes.viewBox "-500 -500 1000 1000"
+            ]
+        |> Element.html
+        |> Element.layout
+            [ Element.width Element.fill
+            , Element.height Element.fill
+            ]
+
+
+rules =
+    Dict.empty
+        |> Dict.insert "brown"
+            [ { color = "brown", rotation = 0 }
+            , { color = "green", rotation = 20 }
+            , { color = "green", rotation = -30 }
+            ]
+        |> Dict.insert "green"
+            [ { color = "red", rotation = -45 }
+            , { color = "red", rotation = -5 }
+            , { color = "red", rotation = 50 }
+            ]
+
+
+segment age { color, rotation } =
+    if age <= 0 then
+        Svg.g [] []
+
+    else
+        Svg.g []
+            [ rules
+                |> Dict.get color
+                |> Maybe.withDefault []
+                |> List.map (segment (age - 1))
+                |> Svg.g
+                    [ Svg.Attributes.transform
+                        (String.concat
+                            [ "rotate("
+                            , String.fromFloat rotation
+                            , ") translate("
+                            , String.fromFloat (age * 10)
+                            , ")"
+                            ]
+                        )
+                    ]
+            , dot age color rotation
+            , line age color rotation
+            ]
+
+
+dot age color rotation =
+    Svg.circle
+        [ Svg.Attributes.r (String.fromFloat age)
+        , Svg.Attributes.cx "0"
+        , Svg.Attributes.cy "0"
+        , Svg.Attributes.fill color
+        , Svg.Attributes.transform
+            (String.concat
+                [ "rotate("
+                , String.fromFloat rotation
+                , ") translate("
+                , String.fromFloat (age * 10)
+                , ")"
+                ]
+            )
+        ]
+        []
+
+
+    line age color rotation =
+    Svg.line
+        [ Svg.Attributes.strokeWidth "1"
+        , Svg.Attributes.x1 "0"
+        , Svg.Attributes.y1 "0"
+        , Svg.Attributes.x1 (String.fromFloat (age * 10))
+        , Svg.Attributes.y1 "0"
+        , Svg.Attributes.stroke color
+        , Svg.Attributes.strokeWidth (String.fromFloat age)
+        , Svg.Attributes.transform
+            (String.concat
+                [ "rotate("
+                , String.fromFloat rotation
+                , ")"
+                ]
+            )
+        ]
+        []
+"""
+        |> editor defaults
+            [ Highlight 10 16 0 38
+            , Highlight 3 3 0 15
+            , Highlight 19 19 0 10
+            , Highlight 20 20 15 10
+            ]
+        |> Element.layout
+            [ Element.width Element.fill
+            , Element.height Element.fill
+            ]
new file mode 100644
index 0000000..784c614
--- /dev/null
+++ b/src/Element/Extra.elm
@@ -0,0 +1,17 @@
+module Element.Extra exposing (css)
+
+import Element exposing (Element)
+import Element.Background as Background
+import Element.Border as Border
+import Element.Font as Font
+import Html exposing (Html)
+import Html.Attributes
+
+
+css : String -> String -> Element.Attribute msg
+css property value =
+    Element.htmlAttribute
+        (Html.Attributes.style
+            property
+            value
+        )
index a0c8d8f..3354f14 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -454,7 +454,7 @@ document =
=            ]
=
=        widgets =
-            [ Mark.Custom.code
+            [ Mark.Custom.editor
=            , Mark.Custom.terminal
=            , Mark.Custom.note
=            ]
index ddbec4e..a96ce20 100644
--- a/src/Mark/Custom.elm
+++ b/src/Mark/Custom.elm
@@ -1,7 +1,6 @@
=module Mark.Custom exposing
-    ( code
-    , colors
-    , css
+    ( colors
+    , editor
=    , emphasize
=    , header
=    , icon
@@ -19,9 +18,11 @@ module Mark.Custom exposing
=
=import BrowserWindow
=import Dict
+import Editor
=import Element exposing (Element)
=import Element.Background as Background
=import Element.Border as Border
+import Element.Extra as Element
=import Element.Font as Font
=import FeatherIcons exposing (icons)
=import Html exposing (Html)
@@ -54,8 +55,8 @@ paragraph =
=            Element.paragraph
=                [ Element.paddingXY 0 10
=                , Element.spacing 12
-                , css "hyphens" "auto"
-                , css "orphans" "3"
+                , Element.css "hyphens" "auto"
+                , Element.css "orphans" "3"
=                , Font.justify
=                ]
=                (content model)
@@ -77,105 +78,37 @@ monospace =
=            , Font.monospace
=            ]
=        , Element.scrollbarY
-        , css "page-break-inside" "avoid"
+        , Element.css "page-break-inside" "avoid"
=        ]
=
=
-type alias File =
-    { path : String
-    , line : Int
-    }
-
-
-code : Mark.Block (a -> Element msg)
-code =
+editor : Mark.Block (a -> Element msg)
+editor =
=    let
-        render { path, line } contents model =
-            Element.column
-                [ Border.width 3
-                , Border.rounded 5
-                , Border.color colors.charcoal
-                , css "page-break-inside" "avoid"
-                , Font.family
-                    [ Font.typeface "Source Code Pro"
-                    , Font.monospace
-                    ]
-                , css "page-break-inside" "avoid"
-                ]
-                [ Element.row
-                    [ Element.width Element.fill
-                    , Background.color colors.charcoal
-                    , Font.color colors.gray
-                    ]
-                    [ FeatherIcons.fileText
-                        |> FeatherIcons.toHtml []
-                        |> Element.html
-                        |> Element.el
-                            [ Element.height (Element.px 35)
-                            , Element.padding 8
-                            ]
-                    , Element.el
-                        [ Element.width Element.fill
-                        , Font.size 16
-                        , Font.family
-                            [ Font.typeface "Source Code Pro"
-                            , Font.monospace
-                            ]
-                        ]
-                        (Element.text path)
-                    ]
-                , contents
-                    |> String.split "\n"
-                    |> List.indexedMap
-                        (\n loc ->
-                            Element.row []
-                                [ Element.el
-                                    [ Font.color colors.gray
-                                    , Font.extraLight
-                                    , Element.width (Element.px 40)
-                                    , Element.padding 10
-                                    , Font.alignRight
-                                    , css "user-select" "none"
-                                    , css "-webkit-user-select" "none"
-                                    , css "-ms-user-select" "none"
-                                    , css "-webkit-touch-callout" "none"
-                                    , css "-o-user-select" "none"
-                                    , css "-moz-user-select" "none"
-                                    ]
-                                    (n
-                                        |> (+) line
-                                        |> String.fromInt
-                                        |> Element.text
-                                    )
-                                , Element.el
-                                    [ Element.width Element.fill
-                                    , Element.padding 10
-                                    ]
-                                    (Element.text loc)
-                                ]
-                        )
-                    |> Element.column
-                        [ Element.width Element.fill
-                        , Font.size 16
-                        , Element.scrollbarY
-                        ]
-                ]
+        render contents highlights model =
+            Editor.editor Editor.defaults highlights contents
+
+        code : Mark.Block String
+        code =
+            Mark.block "Code"
+                identity
+                Mark.multiline
+
+        highlight : Mark.Block Editor.Highlight
+        highlight =
+            Mark.record4 "Highlight"
+                Editor.Highlight
+                (Mark.field "from" Mark.int)
+                (Mark.field "to" Mark.int)
+                (Mark.field "offset" Mark.int)
+                (Mark.field "width" Mark.int)
=    in
-    -- FIXME: There is a weird bug where 1st line of contents gets rendered indented 4 spaces
-    -- Mark.block "Code"
-    --     identity
-    --     (Mark.startWith
-    --         render
-    --         (Mark.record2 "File"
-    --             File
-    --             (Mark.field "path" Mark.string)
-    --             (Mark.field "line" Mark.int)
-    --         )
-    --         Mark.multiline
-    --     )
-    Mark.block "Code"
-        (render (File "src/Main.elm" 1))
-        Mark.multiline
+    Mark.block "Editor"
+        identity
+        (Mark.startWith render
+            code
+            (Mark.manyOf [ highlight ])
+        )
=
=
=terminal : Mark.Block (a -> Element msg)
@@ -187,12 +120,12 @@ terminal =
=                , Border.rounded 5
=                , Border.color colors.charcoal
=                , Background.color colors.charcoal
-                , css "page-break-inside" "avoid"
+                , Element.css "page-break-inside" "avoid"
=                , Font.family
=                    [ Font.typeface "Source Code Pro"
=                    , Font.monospace
=                    ]
-                , css "page-break-inside" "avoid"
+                , Element.css "page-break-inside" "avoid"
=                ]
=                [ Element.row
=                    [ Element.width Element.fill
@@ -255,7 +188,7 @@ window block =
=                    [ Element.height (Element.px 400)
=                    , Element.width Element.fill
=                    ]
-                |> BrowserWindow.window [ css "page-break-inside" "avoid" ]
+                |> BrowserWindow.window [ Element.css "page-break-inside" "avoid" ]
=    in
=    Mark.block "Window"
=        render
@@ -413,7 +346,7 @@ icon =
=                    )
=                |> Element.el
=                    [ Element.padding 4
-                    , css "vertical-align" "middle"
+                    , Element.css "vertical-align" "middle"
=                    ]
=        )
=        |> Mark.inlineString "name"
@@ -492,19 +425,6 @@ definition =
=        |> Mark.inlineString "definiens"
=
=
-
--- Helpers
-
-
-css : String -> String -> Element.Attribute msg
-css property value =
-    Element.htmlAttribute
-        (Html.Attributes.style
-            property
-            value
-        )
-
-
=colors =
=    { maroon = Element.rgb 0.7 0 0
=    , gray = Element.rgb 0.8 0.8 0.8