Commits: 8
Fix performance issues (closes #2)
The parsing of the markup is now performed only after the content is fetched, not as part of the view function like before.
To make it work without too much type-system-fu, the part of the model containing the examples is separated in it's own row (model.examples) with it's own type (Examples.Model). Along with it the new Examples module exposes:
update : Examples.Msg -> Examples.Model -> (Examples.Model, Cmd
Examples.Msg)
subscriptions : Examples.Model -> Sub Examples.Msg
init : (Examples.Model, Cmd Examples.Msg)
That way Main was very much simplified.
new file mode 100644
index 0000000..4ebd806
--- /dev/null
+++ b/src/Examples.elm
@@ -0,0 +1,125 @@
+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.Gradient
+import Examples.Line
+import Examples.MultipleDots as MultipleDots
+import Examples.NestedTransformations
+import Examples.PolarCoordinates
+import Examples.RosetteTypedTransformations
+import Examples.Spiral
+import Examples.Transformations
+import Examples.Tree
+import Examples.ViewBox
+
+
+type alias Model =
+ { counter : Examples.Counter.Model
+ , transformations : Examples.Transformations.Model
+ , nestedTransformations : Examples.NestedTransformations.Model
+ , cartesianCoordinates : Examples.CartesianCoordinates.Model
+ , polarCoordinates : Examples.PolarCoordinates.Model
+ , tree : Maybe Examples.Tree.Model
+ , viewBox : Examples.ViewBox.Model
+ }
+
+
+type Msg
+ = CounterMsg Examples.Counter.Msg
+ | TransformationsMsg Examples.Transformations.Msg
+ | NestedTransformationsMsg Examples.NestedTransformations.Msg
+ | CartesianCoordinatesMsg Examples.CartesianCoordinates.Msg
+ | PolarCoordinatesMsg Examples.PolarCoordinates.Msg
+ | ToggleTree (Maybe Examples.Tree.Model)
+ | TreeMsg Examples.Tree.Msg
+ | ViewBoxMsg Examples.ViewBox.Msg
+
+
+init : ( Model, Cmd Msg )
+init =
+ ( { counter = Examples.Counter.init
+ , transformations = Examples.Transformations.init
+ , nestedTransformations = Examples.NestedTransformations.init
+ , cartesianCoordinates = Examples.CartesianCoordinates.init
+ , polarCoordinates = Examples.PolarCoordinates.init
+ , tree = Nothing
+ , viewBox = Examples.ViewBox.init
+ }
+ , Cmd.none
+ )
+
+
+update : Msg -> Model -> ( Model, Cmd Msg )
+update msg model =
+ case msg of
+ CounterMsg m ->
+ ( { model | counter = Examples.Counter.update m model.counter }
+ , Cmd.none
+ )
+
+ TransformationsMsg m ->
+ ( { model
+ | transformations =
+ Examples.Transformations.update m model.transformations
+ }
+ , Cmd.none
+ )
+
+ NestedTransformationsMsg m ->
+ ( { model
+ | nestedTransformations =
+ Examples.NestedTransformations.update m model.nestedTransformations
+ }
+ , Cmd.none
+ )
+
+ CartesianCoordinatesMsg m ->
+ ( { model
+ | cartesianCoordinates =
+ Examples.CartesianCoordinates.update m model.cartesianCoordinates
+ }
+ , Cmd.none
+ )
+
+ PolarCoordinatesMsg m ->
+ ( { model
+ | polarCoordinates =
+ Examples.PolarCoordinates.update m model.polarCoordinates
+ }
+ , Cmd.none
+ )
+
+ ToggleTree tree ->
+ ( { model | tree = tree }, Cmd.none )
+
+ TreeMsg m ->
+ case model.tree of
+ Just tree ->
+ Examples.Tree.update m tree
+ |> (\( newTree, treeCmd ) ->
+ ( { model | tree = Just newTree }
+ , Cmd.map TreeMsg treeCmd
+ )
+ )
+
+ Nothing ->
+ ( model, Cmd.none )
+
+ ViewBoxMsg m ->
+ ( { model | viewBox = Examples.ViewBox.update m model.viewBox }, Cmd.none )
+
+
+subscriptions : Model -> Sub Msg
+subscriptions model =
+ case model.tree of
+ Nothing ->
+ Sub.none
+
+ Just tree ->
+ Examples.Tree.subscriptions tree
+ |> Sub.map TreeMsgindex f8def73..315d5af 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -17,6 +17,7 @@ import Element.Border as Border
=import Element.Events
=import Element.Font as Font
=import Element.Input as Input
+import Examples
=import Examples.CartesianCoordinates
=import Examples.Circle
=import Examples.Counter
@@ -68,47 +69,57 @@ type alias Flags =
=type alias Model =
= { url : Url
= , key : Navigation.Key
- , markup : Maybe String
- , counter : Examples.Counter.Model
- , transformations : Examples.Transformations.Model
- , nestedTransformations : Examples.NestedTransformations.Model
- , cartesianCoordinates : Examples.CartesianCoordinates.Model
- , polarCoordinates : Examples.PolarCoordinates.Model
- , tree : Maybe Examples.Tree.Model
- , viewBox : Examples.ViewBox.Model
+ , content : Content
+ , examples : Examples.Model
= }
=
=
=type Msg
= = UrlRequested Browser.UrlRequest
+ | UrlRequested Browser.UrlRequest
= | UrlChanged Url
= | ContentFetched (Result Http.Error String)
- | CounterMsg Examples.Counter.Msg
- | TransformationsMsg Examples.Transformations.Msg
- | NestedTransformationsMsg Examples.NestedTransformations.Msg
- | CartesianCoordinatesMsg Examples.CartesianCoordinates.Msg
- | PolarCoordinatesMsg Examples.PolarCoordinates.Msg
- | ToggleTree (Maybe Examples.Tree.Model)
- | TreeMsg Examples.Tree.Msg
- | ViewBoxMsg Examples.ViewBox.Msg
+ | ExamplesMsg Examples.Msg
+
+
+type Content
+ = Loading
+ | FetchError Http.Error
+ | ParseError (List DeadEnd)
+ | Loaded (Examples.Model -> View)
+
+
+type alias DeadEnd =
+ Parser.Advanced.DeadEnd Mark.Context Mark.Problem
+
+
+type alias View =
+ { title : String
+ , body : Element Msg
+ }
+
+
+type alias Document =
+ Mark.Document (Examples.Model -> View)
=
=
=init : Flags -> Url -> Navigation.Key -> ( Model, Cmd Msg )
=init flags url key =
+ let
+ ( examplesModel, examplesCmd ) =
+ Examples.init
+ in
= ( { url = url
= , key = key
- , markup = Nothing
- , counter = Examples.Counter.init
- , transformations = Examples.Transformations.init
- , nestedTransformations = Examples.NestedTransformations.init
- , cartesianCoordinates = Examples.CartesianCoordinates.init
- , polarCoordinates = Examples.PolarCoordinates.init
- , tree = Nothing
- , viewBox = Examples.ViewBox.init
+ , content = Loading
+ , examples = examplesModel
= }
- , url
- |> Routes.parse
- |> loadContent
+ , Cmd.batch
+ [ url
+ |> Routes.parse
+ |> loadContent
+ , Cmd.map ExamplesMsg examplesCmd
+ ]
= )
=
=
@@ -117,15 +128,35 @@ view model =
= let
= content : View
= content =
- case model.markup of
- Nothing ->
+ case model.content of
+ Loading ->
= loadingView
=
- Just markup ->
- markup
- |> Mark.parse document
- |> Result.map (\render -> render model)
- |> Result.extract deadEndsView
+ FetchError error ->
+ (case error of
+ Http.BadUrl message ->
+ "Malformed URL: " ++ message
+
+ Http.Timeout ->
+ "Request timeout"
+
+ Http.NetworkError ->
+ "Network error"
+
+ Http.BadStatus status ->
+ "Bad response status: " ++ String.fromInt status
+
+ Http.BadBody message ->
+ "Malformed response body: " ++ message
+ )
+ |> Element.text
+ |> View "Software Garden : Error fetching content"
+
+ ParseError deadEnds ->
+ deadEndsView deadEnds
+
+ Loaded documentView ->
+ documentView model.examples
=
= loadingView : View
= loadingView =
@@ -291,88 +322,44 @@ update msg model =
= )
=
= UrlChanged url ->
- ( { model | url = url }
- , url
- |> Routes.parse
- |> loadContent
- )
-
- ContentFetched (Ok markup) ->
- ( { model | markup = Just markup }
- , Cmd.none
- )
-
- ContentFetched (Err err) ->
- ( { model | markup = Nothing }
- , Cmd.none
- )
-
- CounterMsg m ->
- ( { model | counter = Examples.Counter.update m model.counter }
- , Cmd.none
- )
-
- TransformationsMsg m ->
= ( { model
- | transformations =
- Examples.Transformations.update m model.transformations
+ | url = url
+ , content = Loading
= }
= , Cmd.none
= )
=
- NestedTransformationsMsg m ->
+ ContentFetched (Ok markup) ->
= ( { model
- | nestedTransformations =
- Examples.NestedTransformations.update m model.nestedTransformations
+ | content =
+ markup
+ |> Mark.parse document
+ |> Result.map Loaded
+ |> Result.extract ParseError
= }
= , Cmd.none
= )
=
- CartesianCoordinatesMsg m ->
- ( { model
- | cartesianCoordinates =
- Examples.CartesianCoordinates.update m model.cartesianCoordinates
- }
+ ContentFetched (Err error) ->
+ ( { model | content = FetchError error }
= , Cmd.none
= )
=
- PolarCoordinatesMsg m ->
- ( { model
- | polarCoordinates =
- Examples.PolarCoordinates.update m model.polarCoordinates
- }
- , Cmd.none
+ ExamplesMsg examplesMsg ->
+ let
+ ( examplesModel, examplesCmd ) =
+ Examples.update examplesMsg model.examples
+ in
+ ( { model | examples = examplesModel }
+ , Cmd.map ExamplesMsg examplesCmd
= )
=
- ToggleTree tree ->
- ( { model | tree = tree }, Cmd.none )
-
- TreeMsg m ->
- case model.tree of
- Just tree ->
- Examples.Tree.update m tree
- |> (\( newTree, treeCmd ) ->
- ( { model | tree = Just newTree }
- , Cmd.map TreeMsg treeCmd
- )
- )
-
- Nothing ->
- ( model, Cmd.none )
-
- ViewBoxMsg m ->
- ( { model | viewBox = Examples.ViewBox.update m model.viewBox }, Cmd.none )
-
=
=subscriptions : Model -> Sub Msg
=subscriptions model =
- case model.tree of
- Nothing ->
- Sub.none
-
- Just tree ->
- Examples.Tree.subscriptions tree
- |> Sub.map TreeMsg
+ model.examples
+ |> Examples.subscriptions
+ |> Sub.map ExamplesMsg
=
=
=loadContent : Route -> Cmd Msg
@@ -394,24 +381,14 @@ loadContent route =
= Cmd.none
=
=
-type alias DeadEnd =
- Parser.Advanced.DeadEnd Mark.Context Mark.Problem
-
-
-type alias View =
- { title : String
- , body : Element Msg
- }
-
-
-document : Mark.Document (Model -> View)
+document : Document
=document =
= let
= content :
= { title : String
- , children : List (Model -> Element Msg)
+ , children : List (Examples.Model -> Element Msg)
= }
- -> Model
+ -> Examples.Model
= -> View
= content { title, children } model =
= children
@@ -486,7 +463,7 @@ document =
= ]
=
= -- Embeded programs' blocks
- counter : Mark.Block (Model -> Element Msg)
+ counter : Mark.Block (Examples.Model -> Element Msg)
= counter =
= let
= render model =
@@ -496,14 +473,15 @@ document =
= [ Element.centerX
= , Element.centerY
= ]
- |> Element.map CounterMsg
+ |> Element.map Examples.CounterMsg
+ |> Element.map ExamplesMsg
= in
= Mark.stub "Counter" render
=
- dot : Mark.Block (Model -> Element Msg)
+ dot : Mark.Block (Examples.Model -> Element Msg)
= dot =
= let
- render : Dot.Config -> Model -> Element Msg
+ render : Dot.Config -> Examples.Model -> Element Msg
= render config model =
= Dot.ui config
= |> Element.html
@@ -520,7 +498,7 @@ document =
= (Mark.field "fill" Mark.string)
= |> Mark.map render
=
- dotInElement : Mark.Block (Model -> Element Msg)
+ dotInElement : Mark.Block (Examples.Model -> Element Msg)
= dotInElement =
= let
= render config model =
@@ -534,7 +512,7 @@ document =
= (Mark.field "cy" Mark.string)
= |> Mark.map render
=
- dotWithViewBox : Mark.Block (Model -> Element Msg)
+ dotWithViewBox : Mark.Block (Examples.Model -> Element Msg)
= dotWithViewBox =
= let
= render config model =
@@ -547,7 +525,7 @@ document =
= (Mark.field "viewBox" Mark.string)
= |> Mark.map render
=
- twoDots : Mark.Block (Model -> Element Msg)
+ twoDots : Mark.Block (Examples.Model -> Element Msg)
= twoDots =
= let
= render config model =
@@ -575,10 +553,10 @@ document =
= (Mark.field "secondCX" Mark.string)
= |> Mark.map render
=
- circle : Mark.Block (Model -> Element Msg)
+ circle : Mark.Block (Examples.Model -> Element Msg)
= circle =
= let
- render : Examples.Circle.Config -> Model -> Element Msg
+ render : Examples.Circle.Config -> Examples.Model -> Element Msg
= render config model =
= Examples.Circle.ui config
= in
@@ -592,7 +570,7 @@ document =
= (Mark.field "scatter" Mark.bool)
= |> Mark.map render
=
- line : Mark.Block (Model -> Element Msg)
+ line : Mark.Block (Examples.Model -> Element Msg)
= line =
= let
= render model =
@@ -604,7 +582,7 @@ document =
= in
= Mark.stub "Line" render
=
- gradient : Mark.Block (Model -> Element Msg)
+ gradient : Mark.Block (Examples.Model -> Element Msg)
= gradient =
= let
= render model =
@@ -616,7 +594,7 @@ document =
= in
= Mark.stub "Gradient" render
=
- transformations : Mark.Block (Model -> Element Msg)
+ transformations : Mark.Block (Examples.Model -> Element Msg)
= transformations =
= let
= render model =
@@ -625,11 +603,12 @@ document =
= |> Element.el
= [ Element.width Element.fill
= ]
- |> Element.map TransformationsMsg
+ |> Element.map Examples.TransformationsMsg
+ |> Element.map ExamplesMsg
= in
= Mark.stub "Transformations" render
=
- nestedTransformations : Mark.Block (Model -> Element Msg)
+ nestedTransformations : Mark.Block (Examples.Model -> Element Msg)
= nestedTransformations =
= let
= render model =
@@ -638,21 +617,23 @@ document =
= |> Element.el
= [ Element.width Element.fill
= ]
- |> Element.map NestedTransformationsMsg
+ |> Element.map Examples.NestedTransformationsMsg
+ |> Element.map ExamplesMsg
= in
= Mark.stub "NestedTransformations" render
=
- cartesianCoordinates : Mark.Block (Model -> Element Msg)
+ cartesianCoordinates : Mark.Block (Examples.Model -> Element Msg)
= cartesianCoordinates =
= let
= render model =
= model.cartesianCoordinates
= |> Examples.CartesianCoordinates.ui
- |> Element.map CartesianCoordinatesMsg
+ |> Element.map Examples.CartesianCoordinatesMsg
+ |> Element.map ExamplesMsg
= in
= Mark.stub "CartesianCoordinates" render
=
- polarCoordinates : Mark.Block (Model -> Element Msg)
+ polarCoordinates : Mark.Block (Examples.Model -> Element Msg)
= polarCoordinates =
= let
= render model =
@@ -661,11 +642,12 @@ document =
= |> Element.el
= [ Element.width Element.fill
= ]
- |> Element.map PolarCoordinatesMsg
+ |> Element.map Examples.PolarCoordinatesMsg
+ |> Element.map ExamplesMsg
= in
= Mark.stub "PolarCoordinates" render
=
- rosette : Mark.Block (Model -> Element Msg)
+ rosette : Mark.Block (Examples.Model -> Element Msg)
= rosette =
= let
= render model =
@@ -677,7 +659,7 @@ document =
= in
= Mark.stub "Rosette" render
=
- spiral : Mark.Block (Model -> Element Msg)
+ spiral : Mark.Block (Examples.Model -> Element Msg)
= spiral =
= let
= render model =
@@ -689,7 +671,7 @@ document =
= in
= Mark.stub "Spiral" render
=
- tree : Mark.Block (Model -> Element Msg)
+ tree : Mark.Block (Examples.Model -> Element Msg)
= tree =
= let
= render model =
@@ -701,11 +683,12 @@ document =
= , Element.width Element.fill
= ]
= |> BrowserWindow.window []
- |> Element.map TreeMsg
+ |> Element.map Examples.TreeMsg
+ |> Element.map ExamplesMsg
= in
= Mark.stub "Tree" render
=
- viewBox : Mark.Block (Model -> Element Msg)
+ viewBox : Mark.Block (Examples.Model -> Element Msg)
= viewBox =
= let
= render model =
@@ -714,7 +697,8 @@ document =
= |> Element.el
= [ Element.width Element.fill
= ]
- |> Element.map ViewBoxMsg
+ |> Element.map Examples.ViewBoxMsg
+ |> Element.map ExamplesMsg
= in
= Mark.stub "ViewBox" render
= inReset scroll position on URL change
index 315d5af..84aa27a 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -8,6 +8,7 @@ It fetches the Elm Markup file at /index.txt and renders it. There are a number
=
=import Basics.Extra exposing (curry)
=import Browser
+import Browser.Dom as Dom
=import Browser.Navigation as Navigation
=import BrowserWindow
=import Dict
@@ -46,6 +47,7 @@ import Parser.Advanced
=import Result.Extra as Result
=import Routes exposing (Route)
=import Svg.Attributes
+import Task
=import Transformations
=import Url exposing (Url)
=
@@ -75,7 +77,7 @@ type alias Model =
=
=
=type Msg
- = UrlRequested Browser.UrlRequest
+ = NoOp
= | UrlRequested Browser.UrlRequest
= | UrlChanged Url
= | ContentFetched (Result Http.Error String)
@@ -309,6 +311,11 @@ view model =
=update : Msg -> Model -> ( Model, Cmd Msg )
=update msg model =
= case msg of
+ NoOp ->
+ ( model
+ , Cmd.none
+ )
+
= UrlRequested (Browser.Internal url) ->
= ( model
= , url
@@ -326,7 +333,13 @@ update msg model =
= | url = url
= , content = Loading
= }
- , Cmd.none
+ , Cmd.batch
+ [ url
+ |> Routes.parse
+ |> loadContent
+ , Dom.setViewport 0 0
+ |> Task.perform (always NoOp)
+ ]
= )
=
= ContentFetched (Ok markup) ->Various edits to days 1 and 2
Also nicer Code blocks, new Terminal block and other layout / typography improvements.
index 31b4007..a8e1794 100644
--- a/content/day-1.txt
+++ b/content/day-1.txt
@@ -9,8 +9,9 @@
= Today we are going to learn about
=
= | List
+ -> Setting up an Elm program
= -> Scalable Vector Graphics
- -> Cartesian Coordinates Systems
+ -> Cartesian coordinates systems
= -> Layouts with Elm UI
= -> Lists
=
@@ -25,33 +26,36 @@ So the first step is to create a directory for our new program. Let's call it "s
=
=In the terminal type:
=
-| Monospace
+| Terminal
= mkdir software-garden/
=
-press enter key. Then type:
+press {Key|enter} key. Then type:
=
-| Monospace
+| Terminal
= cd software-garden/
=
=| Note
- Press enter again. Generally when we ask you to type something in a terminal, we imply that you press enter afterwards. That's how you signal that you are done typing and expect computer to process your command. Alternatively we call it entering a command.
+ Press {Key|enter} again. Generally when we ask you to type something in a terminal, we imply that you press enter afterwards. That's how you signal that you are done typing and expect computer to process your command. Alternatively we call it entering a command.
+
+ In this case the first command ({Code|mkdir}) will *m*a*k*e our new *dir*ectory and the second ({Code|cd}) will set it as the *c*urrent *d*irectory, so that subsequent commands will be executed in the context of {Code|software-garden//} directory.
+
+ Don't worry about the details too much. Just remember that if you close your terminal and then open it again, you will need to enter:
+
+ {Code|cd software-garden//}
=
- In this case the first command will create our new directory and the second will make it the current directory, so that subsequent commands will be executed in it's context. Again, don't worry about the details, but if you close your terminal and then open it again, you will need to enter `cd software-garden/` to set this context again.
+ to set the current directory again.
=
=We can easily create a new program by entering a following command:
=
-| Monospace
+| Terminal
= elm init
=
=Then to create the file which will contain our source code, type:
=
-| Monospace
+| Terminal
= atom src/Main.elm
=
-This command should open a new Atom window with an empty text file. If you prefer to use different editor, feel free to do so.
-
-| Header
- Main.elm
+It may take few seconds, but eventually this command should open a new Atom editor window with an empty text file. If you prefer to use different editor, feel free to do so. Once the editor is ready, type the code belowand save the file.
=
=| Code
= module Main exposing (main)
@@ -62,28 +66,22 @@ This command should open a new Atom window with an empty text file. If you prefe
= main=
= Html.text "Hello, Tree"
=
-Type the above in the editor and save the file.
-To run the program, type the following command in the terminal:
+Then enter the following command in the terminal:
=
-| Monospace
+| Terminal
= elm reactor
=
-This command will start the Elm reactor, which will allow you to run your program in the web browser. Note that it won't give you the command line back - it will run as long as you don't stop it.
-
-
-| Emphasize
- Voila!
-
-
-Open following address in the web browser:
+This command will start the Elm Reactor, which will allow you to run your program in the web browser. Note that it won't give you the command prompt back - it will run in the terminal until you stop it. Leave it running and open the following address in the web browser:
=
=| Emphasize
= {Link|http:////localhost:8000//src//Main.elm|url=http://localhost:8000/src/Main.elm}
=
-| Note
- *TODO*: Make the link display // characters
+You should see something like this:
+
+| Window
+ Hello, Tree!
=
-Note that the same address was printed by the Elm reactor.
+This was a warmup. Let's try something more challenging.
=
=| Header
= The problem
@@ -93,7 +91,7 @@ We want to display a dot at the center of the screen, like this:
=| Window
= | DotWithViewBox
= background=none
- fill=black
+ fill=skyblue
= viewBox=-300 -300 600 600
=
=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.
@@ -114,6 +112,7 @@ Below is the complete code to draw a dot like this, but don't type it in your ed
= [ Svg.Attributes.r "10"
= , Svg.Attributes.cx "0"
= , Svg.Attributes.cy "0"
+ , Svg.Attributes.color "skyblue"
= ]
= []
= ]
@@ -123,18 +122,21 @@ Below is the complete code to draw a dot like this, but don't type it in your ed
= , Element.height Element.fill
= ]
=
-We are going to use a technology called SVG (Scalable Vector Graphics).
+We are going to use a technology called *SVG (Scalable Vector Graphics)*. It's all about drawing shapes on the screen. Let's install an Elm Package to help us work with SVG. Stop the reactor running in the terminal by pressing {Key|ctrl} + {Key|c} and then enter the following command:
=
-*Scalable icon should be here*
+| Terminal
+ elm install elm/svg
=
-S for Scalable
+| Note
+ To press {Key|ctrl} + {Key|c} means:
=
-Its all about drawing shapes on the screen. Let's install an Elm Package to help us work with SVG. Stop the reactor running in the terminal by pressing {Code|CTRL} + {Code|C} and then type the following:
+ | List
+ - Press and hold {Key|ctrl} button (on some keyboards it's labeled as {Key|control}).
+ - While holding press the {Key|c} button once.
+ - Finally release the {Key|ctrl}.
=
-| Monospace
- elm install elm/svg
=
-Now that we have {Code|elm//svg} installed, we can write the code that will draw our dot:
+Now that we have {Code|elm//svg} installed, we can write the code that will draw a dot for us. Change the code to look like this:
=
=| Code
= module Main exposing (main)
@@ -149,13 +151,13 @@ Now that we have {Code|elm//svg} installed, we can write the code that will draw
= [ Svg.Attributes.r "10" ] []
= ]
=
-Start the reactor again:
+Start the reactor again by entering:
=
-| Monospace
+| Terminal
= elm reactor
=
=| Note
- You can press the up arrow {Icon|name=arrow-up} on the keyboard to retrieve commands entered previously.
+ You can press the up arrow {Key|▲} on the keyboard to retrieve commands entered previously.
=
=Reload the browser. You should see something like this:
=
@@ -164,11 +166,14 @@ Reload the browser. You should see something like this:
= background=none
= fill=black
=
-Something isn't quite right here. Why is the dot in the corner?
+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.
=
-There are two reasons. First, because the center of the dot is at a point called the {Code|origin} or {Code|(0, 0)}. We'll explore exactly what this means later.
+| Note
+ TODO: Picture // example showing the viewport
=
-Second, it's because the SVG space doesn't fill the browser window. Don't believe me? We can see that the space doesn't fill the window by changing the background color of the SVG element.
+It's a good start, but the dot is not in the center of the screen yet. Let's try to understand why it is where it is.
+
+First we have to realize that the dot is inside an SVG element that itself has a position, width and height. We can make the background of the SVG element pink to see where it is:
=
=| Code
= module Main exposing (main)
@@ -184,20 +189,31 @@ Second, it's because the SVG space doesn't fill the browser window. Don't believ
= []
= ]
=
-When you reload the browser, you'll see this:
+Realoading the browser should reveal something like this:
=
=| Window
= | Dot
= background=pink
= fill=black
=
+Since the dot is inside the SVG element, and the 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 SVG fill entire {Definition|term=viewport|definiens=the area of the browser window where the content is displayed}.
+
+There are two reasons. First, because the center of the dot is at a point called the {Code|origin} or {Code|(0, 0)}. We'll explore exactly what this means later.
+
+Second, it's because the SVG space doesn't fill the browser window.
+
+
+When you reload the browser, you'll see this:
+
+
+
=Now we can clearly see that our SVG element (which is pink) does not fill the screen.
=
=We can easily correct this with the help of a very handy elm package, {Code|mdgriffith//elm-ui}.
=
=Install it with the terminal.
=
-Press {Code|CTRL + C} to stop the elm-reactor, type {Code|elm install mdgriffith//elm-ui} and press {Code|Enter}.
+Press {Key|ctrl} + {Key|c} to stop the elm-reactor, type {Code|elm install mdgriffith//elm-ui} and press {Key|enter}.
=
=Then in {Code|src//Main.elm} add the necessary import {Code|import Element} and change {Code|Main} to look like this:
=
@@ -239,7 +255,7 @@ Reload the browser to confirm your hypothesis. Voila! The SVG space now fills th
=
=It should now be possible to place our dot in the center of the screen. But, how exactly will we achieve it?
=
-Let's return to the idea we introduced earlier, that the center of the dot is at a point called the {Code|origin} or {Code|(0, 0)}. What eactly does this mean?
+Let's return to the idea we introduced earlier, that the center of the dot is at a point called the {Code|origin} or {Code|(0, 0)}. What exactly does this mean?
=
=* a picture of a vase on a table should be here *
=index 8114efd..1ab9957 100644
--- a/content/day-3.txt
+++ b/content/day-3.txt
@@ -18,4 +18,7 @@
=| Header
= The Problem
=
-...
+| Note
+ *We are still working on this content.*
+
+ Stay tuned {Icon|name=radio}index 751dc76..ea07e93 100644
--- a/content/day-4.txt
+++ b/content/day-4.txt
@@ -14,4 +14,7 @@
=| Header
= The Problem
=
-...
+| Note
+ *We are still working on this content.*
+
+ Stay tuned {Icon|name=radio}index d6be380..32fc805 100644
--- a/content/day-5.txt
+++ b/content/day-5.txt
@@ -15,4 +15,7 @@
=| Header
= The Problem
=
-...
+| Note
+ *We are still working on this content.*
+
+ Stay tuned {Icon|name=radio}index 84aa27a..2e7510a 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -409,6 +409,8 @@ document =
= |> Element.textColumn
= [ Element.centerX
= , Element.width (Element.maximum 800 Element.fill)
+ , Element.spacing 20
+ , Element.padding 80
= ]
= |> View title
=
@@ -430,7 +432,7 @@ document =
= [ Element.width Element.fill
= , Element.paddingXY 0 32
= , Font.center
- , Font.size 86
+ , Font.size 64
= , Font.extraBold
= ]
= in
@@ -444,13 +446,14 @@ document =
= , Mark.Custom.paragraph
= , Mark.Custom.monospace
= , Mark.Custom.code
+ , Mark.Custom.terminal
= , Mark.Custom.note
= , Mark.Custom.emphasize
= , Mark.Custom.list
= , Mark.Custom.image
= ]
= ++ examples
- ++ [ Mark.Custom.window (Mark.oneOf examples)
+ ++ [ Mark.Custom.window (Mark.oneOf (Mark.Custom.paragraph :: examples))
= , Mark.Custom.row (Mark.manyOf examples)
= ]
= )index bf2f03e..d8b37ab 100644
--- a/src/Mark/Custom.elm
+++ b/src/Mark/Custom.elm
@@ -11,6 +11,7 @@ module Mark.Custom exposing
= , note
= , paragraph
= , row
+ , terminal
= , text
= , title
= , window
@@ -51,7 +52,8 @@ paragraph =
= let
= render content model =
= Element.paragraph
- [ Element.paddingXY 0 24
+ [ Element.paddingXY 0 10
+ , Element.spacing 12
= ]
= (content model)
= in
@@ -63,63 +65,177 @@ paragraph =
=monospace : Mark.Block (model -> Element msg)
=monospace =
= Mark.Default.monospace
- [ Element.padding 20
- , Font.size 14
- , Element.width Element.fill
- , Border.color colors.gray
- , Border.width 3
- , Border.rounded 5
+ [ Element.width Element.fill
+ , Element.padding 20
+ , Font.size 16
+ , Font.color colors.maroon
= , Font.family
= [ sourceCodePro
= , Font.monospace
= ]
= , Element.scrollbarY
+ , css "page-break-inside" "avoid"
= ]
=
=
+type alias File =
+ { path : String
+ , line : Int
+ }
+
+
=code : Mark.Block (a -> Element msg)
=code =
= let
- render string model =
- string
- |> String.split "\n"
- |> List.indexedMap
- (\n loc ->
- Element.row [ Element.spacing 10 ]
- [ Element.el
- [ Font.color colors.gray
- , Font.extraLight
- , Element.width (Element.px 40)
- , 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"
- ]
- (Element.text (String.fromInt (n + 1)))
- , Element.el
- [ Element.width Element.fill
- ]
- (Element.text loc)
+ render { path, line } contents model =
+ Element.column
+ [ Border.width 3
+ , Border.rounded 5
+ , Border.color colors.charcoal
+ , css "page-break-inside" "avoid"
+ , Font.family
+ [ sourceCodePro
+ , Font.monospace
+ ]
+ , css "page-break-inside" "avoid"
+ ]
+ [ Element.row
+ [ Element.width Element.fill
+ , Background.color colors.charcoal
+ , Font.color colors.gray
+ ]
+ [ FeatherIcons.file
+ |> 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
+ [ sourceCodePro
+ , Font.monospace
= ]
- )
- |> Element.column
- [ Element.padding 20
- , Element.spacing 10
- , Element.width Element.fill
- , Border.color colors.gray
- , Border.width 3
- , Border.rounded 5
- , Font.size 18
- , Font.family
- [ sourceCodePro
- , Font.monospace
= ]
- , Element.scrollbarY
+ (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
+ ]
+ ]
= 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
+
+
+terminal : Mark.Block (a -> Element msg)
+terminal =
+ let
+ render content model =
+ Element.column
+ [ Border.width 3
+ , Border.rounded 5
+ , Border.color colors.charcoal
+ , css "page-break-inside" "avoid"
+ , Font.family
+ [ sourceCodePro
+ , Font.monospace
+ ]
+ , css "page-break-inside" "avoid"
+ ]
+ [ Element.row
+ [ Element.width Element.fill
+ , Background.color colors.charcoal
+ , Font.color colors.gray
+ ]
+ [ FeatherIcons.terminal
+ |> 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
+ [ sourceCodePro
+ , Font.monospace
+ ]
+ ]
+ (Element.text "terminal")
+ ]
+ , content
+ |> String.split "\n"
+ |> List.map Element.text
+ |> Element.column
+ [ Element.width Element.fill
+ , Element.padding 20
+ , Background.color colors.black
+ , Border.roundEach
+ { topLeft = 0
+ , topRight = 0
+ , bottomLeft = 3
+ , bottomRight = 3
+ }
+ , Font.size 18
+ , Font.family
+ [ sourceCodePro
+ , Font.monospace
+ ]
+ , Font.color colors.gray
+ , Element.scrollbarY
+ ]
+ ]
+ in
+ Mark.block "Terminal"
= render
= Mark.multiline
=
@@ -135,7 +251,7 @@ window block =
= [ Element.height (Element.px 400)
= , Element.width Element.fill
= ]
- |> BrowserWindow.window []
+ |> BrowserWindow.window [ css "page-break-inside" "avoid" ]
= in
= Mark.block "Window"
= render
@@ -177,8 +293,9 @@ note =
= , Element.width Element.fill
= , Border.width 1
= , Border.color colors.gray
- , Font.color colors.gray
+ , Element.alpha 0.6
= , Border.rounded 5
+ , Font.size 16
= ]
= in
= Mark.block "Note"
@@ -249,7 +366,24 @@ text =
= in
= Mark.Default.textWith
= { defaultTextStyle
- | inlines = defaultTextStyle.inlines ++ [ icon ]
+ | inlines =
+ defaultTextStyle.inlines
+ ++ [ icon
+ , definition
+ , key
+ , drop
+ ]
+ , code =
+ [ Font.family [ sourceCodePro, Font.monospace ]
+ , css "font-size" "0.8em"
+ , Font.color colors.maroon
+ , Element.paddingEach
+ { top = 3
+ , right = 5
+ , bottom = 2
+ , left = 5
+ }
+ ]
= }
=
=
@@ -259,16 +393,99 @@ icon =
= (\name model ->
= icons
= |> Dict.get name
- |> Maybe.map (FeatherIcons.toHtml [])
+ |> Maybe.map
+ (FeatherIcons.toHtml
+ [ Html.Attributes.height 14
+ , Html.Attributes.width 14
+ ]
+ )
= |> Maybe.map Element.html
= |> Maybe.withDefault
= (Element.text
= ("Icon not found: '" ++ name ++ "'")
= )
+ |> Element.el
+ [ Element.padding 4
+
+ -- , Element.width (Element.px 12)
+ ]
= )
= |> Mark.inlineString "name"
=
=
+key : Mark.Inline (model -> Element msg)
+key =
+ Mark.inline "Key"
+ (\name model ->
+ name
+ |> List.map
+ (\chunk ->
+ Mark.Default.textFragment chunk model
+ )
+ |> Element.row
+ [ Element.paddingEach
+ { top = 6
+ , bottom = 4
+ , right = 8
+ , left = 8
+ }
+ , Border.width 1
+ , Border.rounded 3
+ , Font.variant Font.smallCaps
+ , Font.size 12
+ , Element.moveUp 2
+ , Font.bold
+ , Background.color colors.charcoal
+ , Font.color colors.white
+ ]
+ )
+ |> Mark.inlineText
+
+
+drop : Mark.Inline (model -> Element msg)
+drop =
+ Mark.inline "Drop"
+ (\chunks model ->
+ chunks
+ |> List.map
+ (\chunk ->
+ Mark.Default.textFragment chunk model
+ )
+ |> Element.row
+ [ Element.paddingEach
+ { top = 8
+ , bottom = 0
+ , right = 8
+ , left = 8
+ }
+ , Font.size 40
+ , Element.spacing 0
+ , Element.alignLeft
+ ]
+ )
+ |> Mark.inlineText
+
+
+definition : Mark.Inline (model -> Element msg)
+definition =
+ Mark.inline "Definition"
+ (\term definiens model ->
+ term
+ |> Element.text
+ |> Element.el
+ [ Font.letterSpacing 1.1
+ , Font.shadow
+ { offset = ( 0, 0 )
+ , blur = 1
+ , color = colors.gray
+ }
+ , Element.htmlAttribute (Html.Attributes.title definiens)
+ ]
+ )
+ |> Mark.inlineString "term"
+ |> Mark.inlineString "definiens"
+
+
=
=-- Helpers
=
@@ -285,14 +502,17 @@ css property value =
=sourceCodePro : Font.Font
=sourceCodePro =
= Font.external
- { url = "https://fonts.googleapis.com/css?family=Source+Code+Pro:200,400&subset=latin-ext"
+ { url = "https://fonts.googleapis.com/css?family=Source+Code+Pro:200,400,700&subset=latin-ext"
= , name = "Source Code Pro"
= }
=
=
-colors : { gray : Element.Color, maroon : Element.Color, pink : Element.Color }
=colors =
= { maroon = Element.rgb 0.7 0 0
= , gray = Element.rgb 0.8 0.8 0.8
+ , charcoal = Element.rgb 0.2 0.2 0.2
= , pink = Element.rgb 1 0.6 0.6
+ , white = Element.rgb 1 1 1
+ , black = Element.rgb 0 0 0
+ , badass = Element.rgb255 0xBA 0xDA 0x55
= }Add general information about the workshop to index document
index 3565ea5..c2832b6 100644
--- a/content/index.txt
+++ b/content/index.txt
@@ -7,6 +7,10 @@
=| Emphasize
= A software development workshop for non-programmers
=
+Hello! We are running a workshop that will give you a glimpse into the way software is created. Our workshop is intended for people with no prior experience in programming and doesn't require any technical knowledge. Everybody is welcome!
+
+Below is material through which we will be going during the workshop. Only the first section is a required read before you come, but feel free to read more to get more prepared.
+
=| List
= # {Link|Before the course begins|url=/preparation.html}
= # {Link|Day 1 - Let's Make some Dots|url=/day-1.html}Merge branch 'master' into master-merge
Merge remote-tracking branch 'refs/remotes/origin/master'
Merge branch 'master-merge'
Some edits to preparations document
index 33c56c2..9513bc3 100644
--- a/content/preparation.txt
+++ b/content/preparation.txt
@@ -33,14 +33,16 @@ and follow the installation instructions.
=
=Some of the tools we use with Elm require Node.js. Go to the {Link|Node.js|url=https://nodejs.org/en/} website and install the current version (the green button on the right)
=
-We will need to use the terminal {Icon|name=terminal} a little bit. Don't be scared. It's easy.
+We will need to use the {Definition|term=terminal|definiens=a program that let's you enter commands for your computer} {Icon|name=terminal} a little bit.
+
+| Note
+ TODO: Explain how to open a terminal
=
=| Image
= src = /assets/mac-launchpad-terminal.png
= description = Here is a picture of terminal
=
=
-Mac Terminal app window
=You should see a window like this
=
=
@@ -51,12 +53,12 @@ You should see a window like this
=Type the following in the terminal.
=
=
-| Monospace
+| Terminal
= elm repl
=
-Note that the command line changes. You should see something like this
+Then press the {Key|enter} key. Note that the command line changes. You should see something like this
=
-| Monospace
+| Terminal
= ---- Elm 0.19.0 --------------------------------------------------
= Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help
= ------------------------------------------------------------------
@@ -64,12 +66,15 @@ Note that the command line changes. You should see something like this
=
=It's the Elm REPL (Read-Evaluate-Print Loop). Inside the REPL type.
=
-| Monospace
+| Terminal
= 2+2
=
-And expect to see
+| Note
+ Press {Key|enter} again. Generally when we ask you to type something in a terminal, we imply that you press enter afterwards. That's how you signal that you are done typing and expect computer to process your command. Alternatively we call it entering a command.
+
+You should see:
=
-| Monospace
+| Terminal
= ---- Elm 0.19.0 ----------------
= Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help
= --------------------------------
@@ -77,9 +82,11 @@ And expect to see
= 4 : number
= >
=
-Easy, huh?
-We will learn more about REPL later. For now type
-:exit to close it.
+Easy, huh? We will learn more about REPL later. For now enter the following command to close it.
+
+| Terminal
+ :exit
+
=The command line should get back to its initial state.
=
=| Header
@@ -91,7 +98,7 @@ Go to {Link|atom.io|url=https://atom.io} and download the editor. After installa
=
=One last thing we need is Elm support for Atom editor. You can install it by typing in the terminal:
=
-| Monospace
+| Terminal
= apm install language-elm
=
=APM is the Atom Package Manager, but don't pay much attention to it. We won't be using it again.