From fd5df391b1aec9f1b27a81ee344bd530ec173e64 Mon Sep 17 00:00:00 2001 From: Decio Ferreira Date: Tue, 7 Oct 2025 23:06:51 +0100 Subject: [PATCH 01/11] WIP updated version --- elm.json | 3 +- lib/index.js | 19 +- .../{examples => example-files}/buttons.guida | 0 .../{examples => example-files}/hello.guida | 0 public/index.html | 8 +- public/robots.txt | 2 + src/Components/Button.elm | 129 ++++++++ src/Components/GridPattern.elm | 80 +++++ src/Components/HeroPattern.elm | 32 ++ src/Components/Link.elm | 8 + src/Components/Logo.elm | 26 ++ src/Components/MobileNavigation.elm | 57 ++++ src/Components/ThemeToggle.elm | 78 +++++ src/Icon.elm | 22 +- src/Layout/Footer.elm | 47 +-- src/Layout/Global.elm | 34 ++ src/Layout/Header.elm | 300 +++++------------ src/Layout/Main.elm | 113 +++++++ src/Layout/Navigation.elm | 122 +++++++ src/Main.elm | 110 +++++-- src/Page/Community.elm | 106 ++++++ src/Page/Docs.elm | 140 ++++++++ src/Page/Examples.elm | 119 +++++++ src/Page/Home.elm | 305 +++++++++++------- src/Page/NotFound.elm | 32 +- src/Page/Try.elm | 117 ++++--- src/Route.elm | 10 +- src/Session.elm | 65 +++- styles/tailwind.css | 4 + 29 files changed, 1615 insertions(+), 473 deletions(-) rename public/{examples => example-files}/buttons.guida (100%) rename public/{examples => example-files}/hello.guida (100%) create mode 100644 public/robots.txt create mode 100644 src/Components/Button.elm create mode 100644 src/Components/GridPattern.elm create mode 100644 src/Components/HeroPattern.elm create mode 100644 src/Components/Link.elm create mode 100644 src/Components/Logo.elm create mode 100644 src/Components/MobileNavigation.elm create mode 100644 src/Components/ThemeToggle.elm create mode 100644 src/Layout/Global.elm create mode 100644 src/Layout/Main.elm create mode 100644 src/Layout/Navigation.elm create mode 100644 src/Page/Community.elm create mode 100644 src/Page/Docs.elm create mode 100644 src/Page/Examples.elm diff --git a/elm.json b/elm.json index 5d229c8..4af5615 100644 --- a/elm.json +++ b/elm.json @@ -13,7 +13,8 @@ "elm/json": "1.1.3", "elm/project-metadata-utils": "1.0.2", "elm/svg": "1.0.1", - "elm/url": "1.0.0" + "elm/url": "1.0.0", + "fapian/elm-html-aria": "1.4.0" }, "indirect": { "elm/bytes": "1.0.8", diff --git a/lib/index.js b/lib/index.js index cd062c9..4ecb27b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -7,7 +7,24 @@ import { Elm } from "../tmp/elm.js"; GUIDA_REGISTRY: "https://guida-package-registry.fly.dev" }); - const app = Elm.Main.init({ flags: new Date().getFullYear() }); + const theme = localStorage.getItem("theme"); + const app = Elm.Main.init({ flags: { theme, year: new Date().getFullYear() } }); + + if (theme === "dark") { + document.documentElement.classList.add("dark"); + } else { + document.documentElement.classList.remove("dark"); + } + + app.ports.setTheme.subscribe((theme) => { + localStorage.setItem("theme", theme); + + if (theme === "dark") { + document.documentElement.classList.add("dark"); + } else { + document.documentElement.classList.remove("dark"); + } + }); const setEditorContentAndRebuild = async (content) => { if (content) { diff --git a/public/examples/buttons.guida b/public/example-files/buttons.guida similarity index 100% rename from public/examples/buttons.guida rename to public/example-files/buttons.guida diff --git a/public/examples/hello.guida b/public/example-files/hello.guida similarity index 100% rename from public/examples/hello.guida rename to public/example-files/hello.guida diff --git a/public/index.html b/public/index.html index de45de4..663bf4f 100644 --- a/public/index.html +++ b/public/index.html @@ -1,14 +1,16 @@ - + - Guida + Guida programming language + - + diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..6f27bb6 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: \ No newline at end of file diff --git a/src/Components/Button.elm b/src/Components/Button.elm new file mode 100644 index 0000000..67b4719 --- /dev/null +++ b/src/Components/Button.elm @@ -0,0 +1,129 @@ +module Components.Button exposing + ( Arrow(..) + , Type(..) + , Variant(..) + , view + ) + +import Components.Link as Link +import Html exposing (Html) +import Html.Attributes as Attr +import Html.Attributes.Aria as Aria +import Svg +import Svg.Attributes as SvgAttr + + +arrowIcon : List (Html.Attribute msg) -> Html msg +arrowIcon attrs = + Svg.svg + (SvgAttr.viewBox "0 0 20 20" + :: SvgAttr.fill "none" + :: Aria.ariaHidden True + :: attrs + ) + [ Svg.path + [ SvgAttr.stroke "currentColor" + , SvgAttr.strokeLinecap "round" + , SvgAttr.strokeLinejoin "round" + , SvgAttr.d "m11.5 6.5 3 3.5m0 0-3 3.5m3-3.5h-9" + ] + [] + ] + + +type Type + = Button + | Link String + + +type Variant + = Primary + | Secondary + | Filled + | Outline + | Text + + +variantStyles : Variant -> String +variantStyles variant = + case variant of + Primary -> + "rounded-full bg-zinc-900 py-1 px-3 text-white hover:bg-zinc-700 dark:bg-amber-400/10 dark:text-amber-400 dark:ring-1 dark:ring-inset dark:ring-amber-400/20 dark:hover:bg-amber-400/10 dark:hover:text-amber-300 dark:hover:ring-amber-300" + + Secondary -> + "rounded-full bg-zinc-100 py-1 px-3 text-zinc-900 hover:bg-zinc-200 dark:bg-zinc-800/40 dark:text-zinc-400 dark:ring-1 dark:ring-inset dark:ring-zinc-800 dark:hover:bg-zinc-800 dark:hover:text-zinc-300" + + Filled -> + "rounded-full bg-zinc-900 py-1 px-3 text-white hover:bg-zinc-700 dark:bg-amber-500 dark:text-white dark:hover:bg-amber-400" + + Outline -> + "rounded-full py-1 px-3 text-zinc-700 ring-1 ring-inset ring-zinc-900/10 hover:bg-zinc-900/2.5 hover:text-zinc-900 dark:text-zinc-400 dark:ring-white/10 dark:hover:bg-white/5 dark:hover:text-white" + + Text -> + "text-amber-500 hover:text-amber-600 dark:text-amber-400 dark:hover:text-amber-500" + + +type Arrow + = LeftArrow + | RightArrow + + +view : Type -> Variant -> Maybe Arrow -> List (Html.Attribute msg) -> List (Html msg) -> Html msg +view type_ variant maybeArrow attrs children = + let + classAttrs : List (Html.Attribute msg) + classAttrs = + [ Attr.classList + [ ( "inline-flex gap-0.5 justify-center overflow-hidden text-sm font-medium transition", True ) + , ( variantStyles variant, True ) + ] + ] + + arrowIconElem : Html msg + arrowIconElem = + let + variantAttrs = + case variant of + Text -> + [ SvgAttr.class "relative top-px" ] + + _ -> + [] + + arrowAttrs = + case maybeArrow of + Just LeftArrow -> + [ SvgAttr.class "-ml-1 rotate-180" ] + + Just RightArrow -> + [ SvgAttr.class "-mr-1" ] + + Nothing -> + [] + in + arrowIcon + (SvgAttr.class "mt-0.5 h-5 w-5" + :: variantAttrs + ++ arrowAttrs + ) + + inner : List (Html msg) + inner = + case maybeArrow of + Just LeftArrow -> + arrowIconElem :: children + + Just RightArrow -> + children ++ [ arrowIconElem ] + + Nothing -> + children + in + case type_ of + Button -> + Html.button (classAttrs ++ attrs) + inner + + Link href -> + Link.view (Attr.href href :: classAttrs ++ attrs) + inner diff --git a/src/Components/GridPattern.elm b/src/Components/GridPattern.elm new file mode 100644 index 0000000..17c9e8a --- /dev/null +++ b/src/Components/GridPattern.elm @@ -0,0 +1,80 @@ +module Components.GridPattern exposing + ( Config + , view + ) + +import Html exposing (Html) +import Html.Attributes.Aria as Aria +import Svg +import Svg.Attributes as SvgAttr + + +type alias Config = + { width : Int + , height : Int + , x : Int + , y : Int + , squares : List ( Int, Int ) + } + + +view : List (Html.Attribute msg) -> Config -> Html msg +view attrs config = + let + patternId : String + patternId = + "grid-pattern-id" + + squareElems : List (Html msg) + squareElems = + case config.squares of + [] -> + [] + + _ -> + [ Svg.svg + [ SvgAttr.x (String.fromInt config.x) + , SvgAttr.y (String.fromInt config.y) + , SvgAttr.class "overflow-visible" + ] + (List.map + (\( x, y ) -> + Svg.rect + [ SvgAttr.strokeWidth "0" + , SvgAttr.width (String.fromInt (config.width + 1)) + , SvgAttr.height (String.fromInt (config.height + 1)) + , SvgAttr.x (String.fromInt (x * config.width)) + , SvgAttr.y (String.fromInt (y * config.height)) + ] + [] + ) + config.squares + ) + ] + in + Svg.svg (Aria.ariaHidden True :: attrs) + (Svg.defs [] + [ Svg.pattern + [ SvgAttr.id patternId + , SvgAttr.width (String.fromInt config.width) + , SvgAttr.height (String.fromInt config.height) + , SvgAttr.patternUnits "userSpaceOnUse" + , SvgAttr.x (String.fromInt config.x) + , SvgAttr.y (String.fromInt config.y) + ] + [ Svg.path + [ SvgAttr.d ("M.5 " ++ String.fromInt config.height ++ "V.5H" ++ String.fromInt config.width) + , SvgAttr.fill "none" + ] + [] + ] + ] + :: Svg.rect + [ SvgAttr.width "100%" + , SvgAttr.height "100%" + , SvgAttr.strokeWidth "0" + , SvgAttr.fill ("url(#" ++ patternId ++ ")") + ] + [] + :: squareElems + ) diff --git a/src/Components/HeroPattern.elm b/src/Components/HeroPattern.elm new file mode 100644 index 0000000..9555c39 --- /dev/null +++ b/src/Components/HeroPattern.elm @@ -0,0 +1,32 @@ +module Components.HeroPattern exposing (view) + +import Components.GridPattern as GridPattern +import Html exposing (Html) +import Html.Attributes as Attr +import Html.Attributes.Aria as Aria +import Svg +import Svg.Attributes as SvgAttr + + +view : Html msg +view = + Html.div [ Attr.class "absolute inset-0 -z-10 mx-0 max-w-none overflow-hidden" ] + [ Html.div [ Attr.class "absolute top-0 left-1/2 ml-[-38rem] h-[25rem] w-[81.25rem] dark:[mask-image:linear-gradient(white,transparent)]" ] + [ Html.div [ Attr.class "absolute inset-0 bg-linear-to-r from-[#FD9A00] to-[#FEF3C6] opacity-40 [mask-image:radial-gradient(farthest-side_at_top,white,transparent)] dark:from-[#FD9A00]/30 dark:to-[#FEF3C6]/30 dark:opacity-100" ] + [ GridPattern.view [ SvgAttr.class "absolute inset-x-0 h-[200%] w-full skew-y-[18deg] fill-black/40 stroke-black/50 mix-blend-overlay dark:fill-white/2.5 dark:stroke-white/5" ] + { width = 72 + , height = 56 + , x = -12 + , y = 4 + , squares = [ ( 4, 3 ), ( 2, 1 ), ( 7, 3 ), ( 10, 6 ) ] + } + ] + , Svg.svg + [ SvgAttr.viewBox "0 0 1113 440" + , Aria.ariaHidden True + , SvgAttr.class "absolute top-0 left-1/2 ml-[-19rem] w-[69.5625rem] fill-white blur-[26px] dark:hidden" + ] + [ Svg.path [ SvgAttr.d "M.016 439.5s-9.5-300 434-300S882.516 20 882.516 20V0h230.004v439.5H.016Z" ] [] + ] + ] + ] diff --git a/src/Components/Link.elm b/src/Components/Link.elm new file mode 100644 index 0000000..f321511 --- /dev/null +++ b/src/Components/Link.elm @@ -0,0 +1,8 @@ +module Components.Link exposing (view) + +import Html exposing (Html) + + +view : List (Html.Attribute msg) -> List (Html msg) -> Html msg +view = + Html.a diff --git a/src/Components/Logo.elm b/src/Components/Logo.elm new file mode 100644 index 0000000..9fa3a88 --- /dev/null +++ b/src/Components/Logo.elm @@ -0,0 +1,26 @@ +module Components.Logo exposing (view) + +import Html exposing (Html) +import Html.Attributes.Aria as Aria +import Svg +import Svg.Attributes as SvgAttr + + +view : String -> Html msg +view className = + Svg.svg + [ SvgAttr.viewBox "0 0 204.59181 52.44914" + , Aria.ariaHidden True + , SvgAttr.class className + ] + [ Svg.path + [ SvgAttr.class "fill-amber-500" + , SvgAttr.d "m 26.458803,37.014411 c -52.644472,10e-7 24.645507,37.220978 -8.178087,-3.938219 -32.823594,-41.1591977 -13.7338654,42.475269 -2.019734,-8.848779 11.714536,-51.324048 -41.771526,15.745136 5.659204,-7.096355 47.430489,-22.8414107 -38.354383,-22.8414107 9.076912,0 47.431294,22.84141 -6.055251,-44.227774 5.659204,7.096355 11.714536,51.324048 30.803457,-32.3100962 -2.019734,8.848779 -32.8227866,41.158875 44.466386,3.938219 -8.178087,3.938219 z" + ] + [] + , Svg.path + [ SvgAttr.class "fill-zinc-900 dark:fill-white" + , SvgAttr.d "m 102.23049,40.228971 q -2.06707,0.806158 -5.477735,1.612315 -3.410666,0.785487 -6.800661,0.785487 -7.854867,0 -12.299068,-4.258165 -4.444201,-4.278835 -4.444201,-11.802971 0,-7.172734 4.485542,-11.554923 4.485543,-4.40286 12.505775,-4.40286 3.038594,0 5.787797,0.558109 2.749204,0.537439 6.118531,2.170424 v 7.214075 h -0.88884 q -0.57878,-0.434084 -1.695001,-1.219571 -1.116218,-0.806158 -2.149753,-1.364267 -1.198901,-0.661462 -2.811215,-1.136888 -1.591644,-0.475426 -3.389996,-0.475426 -2.108411,0 -3.82408,0.620121 -1.715668,0.620121 -3.079934,1.901704 -1.302255,1.240243 -2.067071,3.162618 -0.744145,1.901705 -0.744145,4.40286 0,5.105663 2.707862,7.813525 2.707862,2.707862 7.999562,2.707862 0.454756,0 0.992194,-0.02067 0.558109,-0.02067 1.012864,-0.06201 v -6.035845 h -6.139198 v -5.808467 h 14.200771 z m 29.82783,1.75701 h -7.44146 v -2.563167 q -2.06707,1.570973 -3.78274,2.397801 -1.71566,0.806158 -4.09279,0.806158 -3.84476,0 -5.9325,-2.211765 -2.06707,-2.211766 -2.06707,-6.531942 V 18.772782 h 7.4828 v 11.513581 q 0,1.75701 0.10335,2.93524 0.12403,1.157559 0.53744,1.922375 0.39274,0.764816 1.17823,1.116218 0.80616,0.351402 2.23244,0.351402 0.95085,0 2.10841,-0.351402 1.15756,-0.351402 2.23243,-1.033535 V 18.772782 h 7.44146 z m 14.46949,0 h -7.44146 V 18.772782 h 7.44146 z m 0.2067,-26.520512 h -7.85486 V 9.8223675 h 7.85486 z m 29.39374,26.520512 h -7.44145 v -2.418472 q -1.92238,1.570973 -3.5967,2.315118 -1.67433,0.744146 -3.86543,0.744146 -4.23749,0 -6.77999,-3.265971 -2.54249,-3.265971 -2.54249,-8.80572 0,-2.95591 0.8475,-5.229687 0.86817,-2.294448 2.35646,-3.927434 1.4056,-1.550303 3.41066,-2.397801 2.00506,-0.86817 4.01012,-0.86817 2.08774,0 3.41066,0.454756 1.3436,0.434084 2.74921,1.116217 V 9.8223675 h 7.44145 z M 168.6868,35.578063 V 24.188506 q -0.78549,-0.330731 -1.65366,-0.475426 -0.86817,-0.144695 -1.59164,-0.144695 -2.93524,0 -4.40286,1.839692 -1.46762,1.819022 -1.46762,5.064323 0,3.410666 1.17823,4.960968 1.17823,1.529632 3.78274,1.529632 1.01286,0 2.14975,-0.372072 1.13689,-0.392744 2.00506,-1.012865 z m 28.52557,0.351402 v -4.836944 q -1.50896,0.124024 -3.26597,0.351402 -1.75701,0.206707 -2.66652,0.496096 -1.11622,0.351402 -1.71567,1.033536 -0.57878,0.661462 -0.57878,1.757009 0,0.723475 0.12402,1.17823 0.12403,0.454756 0.62013,0.86817 0.47542,0.413414 1.13688,0.620121 0.66147,0.186036 2.06707,0.186036 1.11622,0 2.25311,-0.454755 1.15756,-0.454756 2.02573,-1.198901 z m 0,3.596702 q -0.59945,0.454756 -1.48829,1.095548 -0.88884,0.640791 -1.67433,1.012864 -1.09554,0.496097 -2.27377,0.723475 -1.17823,0.248048 -2.58384,0.248048 -3.30731,0 -5.53975,-2.046399 -2.23244,-2.0464 -2.23244,-5.229688 0,-2.542497 1.13689,-4.154812 1.13689,-1.612314 3.22463,-2.542496 2.06707,-0.930182 5.12634,-1.322925 3.05926,-0.392743 6.3459,-0.57878 v -0.124024 q 0,-1.922375 -1.57097,-2.64585 -1.57098,-0.744145 -4.63024,-0.744145 -1.83969,0 -3.92743,0.661462 -2.08774,0.640792 -2.99725,0.992194 h -0.68214 v -5.60176 q 1.17823,-0.310061 3.82408,-0.723475 2.66652,-0.434085 5.33304,-0.434085 6.34591,0 9.15712,1.963717 2.83189,1.943046 2.83189,6.118528 v 15.792417 h -7.37944 z" + ] + [] + ] diff --git a/src/Components/MobileNavigation.elm b/src/Components/MobileNavigation.elm new file mode 100644 index 0000000..8dc454f --- /dev/null +++ b/src/Components/MobileNavigation.elm @@ -0,0 +1,57 @@ +module Components.MobileNavigation exposing (..) + +import Html exposing (Html) +import Html.Attributes as Attr +import Html.Attributes.Aria as Aria +import Html.Events as Events +import Svg +import Svg.Attributes as SvgAttr + + +menuIcon : String -> Html msg +menuIcon className = + Svg.svg + [ SvgAttr.viewBox "0 0 10 9" + , SvgAttr.fill "none" + , SvgAttr.strokeLinecap "round" + , Aria.ariaHidden True + , SvgAttr.class className + ] + [ Svg.path [ SvgAttr.d "M.5 1h9M.5 8h9M.5 4.5h9" ] [] + ] + + +xIcon : String -> Html msg +xIcon className = + Svg.svg + [ SvgAttr.viewBox "0 0 10 9" + , SvgAttr.fill "none" + , SvgAttr.strokeLinecap "round" + , Aria.ariaHidden True + , SvgAttr.class className + ] + [ Svg.path [ SvgAttr.d "m1.5 1 7 7M8.5 1l-7 7" ] [] + ] + + +view : { hasSidebar : Bool, isOpen : Bool, toggleMsg : msg } -> Html msg +view { hasSidebar, isOpen, toggleMsg } = + let + toogleIcon = + if isOpen then + xIcon + + else + menuIcon + in + Html.button + [ Attr.type_ "button" + , Attr.classList + [ ( "flex h-6 w-6 items-center justify-center rounded-md transition lg:hidden hover:bg-zinc-900/5 dark:hover:bg-white/5", True ) + , ( "md:hidden", not hasSidebar ) + ] + , Aria.ariaLabel "Toggle navigation" + , Events.onClick toggleMsg + ] + [ toogleIcon "w-2.5 stroke-zinc-900 dark:stroke-white" + ] diff --git a/src/Components/ThemeToggle.elm b/src/Components/ThemeToggle.elm new file mode 100644 index 0000000..f3261da --- /dev/null +++ b/src/Components/ThemeToggle.elm @@ -0,0 +1,78 @@ +module Components.ThemeToggle exposing + ( Theme(..) + , themeToString + , view + ) + +import Html exposing (Html) +import Html.Attributes as Attr +import Html.Attributes.Aria as Aria +import Html.Events as Events +import Svg +import Svg.Attributes as SvgAttr + + +sunIcon : String -> Html msg +sunIcon className = + Svg.svg + [ SvgAttr.viewBox "0 0 20 20" + , SvgAttr.fill "none" + , Aria.ariaHidden True + , SvgAttr.class className + ] + [ Svg.path [ SvgAttr.d "M12.5 10a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0Z" ] [] + , Svg.path + [ SvgAttr.strokeLinecap "round" + , SvgAttr.d "M10 5.5v-1M13.182 6.818l.707-.707M14.5 10h1M13.182 13.182l.707.707M10 15.5v-1M6.11 13.889l.708-.707M4.5 10h1M6.11 6.111l.708.707" + ] + [] + ] + + +moonIcon : String -> Html msg +moonIcon className = + Svg.svg + [ SvgAttr.viewBox "0 0 20 20" + , SvgAttr.fill "none" + , Aria.ariaHidden True + , SvgAttr.class className + ] + [ Svg.path [ SvgAttr.d "M15.224 11.724a5.5 5.5 0 0 1-6.949-6.949 5.5 5.5 0 1 0 6.949 6.949Z" ] [] + ] + + +type Theme + = Light + | Dark + + +themeToString : Theme -> String +themeToString theme = + case theme of + Light -> + "light" + + Dark -> + "dark" + + +view : Theme -> (Theme -> msg) -> Html msg +view resolvedTheme setTheme = + let + ( otherTheme, otherThemeLabel ) = + case resolvedTheme of + Dark -> + ( Light, "light" ) + + Light -> + ( Dark, "dark" ) + in + Html.button + [ Attr.type_ "button" + , Attr.class "flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-zinc-900/5 dark:hover:bg-white/5" + , Aria.ariaLabel ("Switch to " ++ otherThemeLabel ++ " theme") + , Events.onClick (setTheme otherTheme) + ] + [ sunIcon "h-5 w-5 stroke-zinc-900 dark:hidden" + , moonIcon "hidden h-5 w-5 stroke-white dark:block" + ] diff --git a/src/Icon.elm b/src/Icon.elm index 4d002f5..6a5c33b 100644 --- a/src/Icon.elm +++ b/src/Icon.elm @@ -3,6 +3,7 @@ module Icon exposing , burger , check , cross + , discord , github , logo , xMark @@ -10,6 +11,7 @@ module Icon exposing import Html import Html.Attributes as Attr +import Html.Attributes.Aria as Aria import Svg exposing (Svg) import Svg.Attributes as SvgAttr @@ -90,23 +92,33 @@ cross attrs = ] -github : List (Html.Attribute msg) -> Html.Html msg +github : List (Html.Attribute msg) -> Svg msg github attrs = Svg.svg - (SvgAttr.viewBox "0 0 24 24" - :: SvgAttr.stroke "currentColor" - :: Attr.attribute "aria-hidden" "true" + (SvgAttr.viewBox "0 0 20 20" + :: Aria.ariaHidden True :: attrs ) [ Svg.path [ SvgAttr.fillRule "evenodd" , SvgAttr.clipRule "evenodd" - , SvgAttr.d "M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" + , SvgAttr.d "M10 1.667c-4.605 0-8.334 3.823-8.334 8.544 0 3.78 2.385 6.974 5.698 8.106.417.075.573-.182.573-.406 0-.203-.011-.875-.011-1.592-2.093.397-2.635-.522-2.802-1.002-.094-.246-.5-1.005-.854-1.207-.291-.16-.708-.556-.01-.567.656-.01 1.124.62 1.281.876.75 1.292 1.948.93 2.427.705.073-.555.291-.93.531-1.143-1.854-.213-3.791-.95-3.791-4.218 0-.929.322-1.698.854-2.296-.083-.214-.375-1.09.083-2.265 0 0 .698-.224 2.292.876a7.576 7.576 0 0 1 2.083-.288c.709 0 1.417.096 2.084.288 1.593-1.11 2.291-.875 2.291-.875.459 1.174.167 2.05.084 2.263.53.599.854 1.357.854 2.297 0 3.278-1.948 4.005-3.802 4.219.302.266.563.78.563 1.58 0 1.143-.011 2.061-.011 2.35 0 .224.156.491.573.405a8.365 8.365 0 0 0 4.11-3.116 8.707 8.707 0 0 0 1.567-4.99c0-4.721-3.73-8.545-8.334-8.545Z" ] [] ] +discord : List (Html.Attribute msg) -> Svg msg +discord attrs = + Svg.svg + (SvgAttr.viewBox "0 0 20 20" + :: Aria.ariaHidden True + :: attrs + ) + [ Svg.path [ SvgAttr.d "M16.238 4.515a14.842 14.842 0 0 0-3.664-1.136.055.055 0 0 0-.059.027 10.35 10.35 0 0 0-.456.938 13.702 13.702 0 0 0-4.115 0 9.479 9.479 0 0 0-.464-.938.058.058 0 0 0-.058-.027c-1.266.218-2.497.6-3.664 1.136a.052.052 0 0 0-.024.02C1.4 8.023.76 11.424 1.074 14.782a.062.062 0 0 0 .024.042 14.923 14.923 0 0 0 4.494 2.272.058.058 0 0 0 .064-.02c.346-.473.654-.972.92-1.496a.057.057 0 0 0-.032-.08 9.83 9.83 0 0 1-1.404-.669.058.058 0 0 1-.029-.046.058.058 0 0 1 .023-.05c.094-.07.189-.144.279-.218a.056.056 0 0 1 .058-.008c2.946 1.345 6.135 1.345 9.046 0a.056.056 0 0 1 .059.007c.09.074.184.149.28.22a.058.058 0 0 1 .023.049.059.059 0 0 1-.028.046 9.224 9.224 0 0 1-1.405.669.058.058 0 0 0-.033.033.056.056 0 0 0 .002.047c.27.523.58 1.022.92 1.495a.056.056 0 0 0 .062.021 14.878 14.878 0 0 0 4.502-2.272.055.055 0 0 0 .016-.018.056.056 0 0 0 .008-.023c.375-3.883-.63-7.256-2.662-10.246a.046.046 0 0 0-.023-.021Zm-9.223 8.221c-.887 0-1.618-.814-1.618-1.814s.717-1.814 1.618-1.814c.908 0 1.632.821 1.618 1.814 0 1-.717 1.814-1.618 1.814Zm5.981 0c-.887 0-1.618-.814-1.618-1.814s.717-1.814 1.618-1.814c.908 0 1.632.821 1.618 1.814 0 1-.71 1.814-1.618 1.814Z" ] [] + ] + + logo : List (Html.Attribute msg) -> Svg msg logo attrs = Svg.svg diff --git a/src/Layout/Footer.elm b/src/Layout/Footer.elm index 3afc19d..7a1e232 100644 --- a/src/Layout/Footer.elm +++ b/src/Layout/Footer.elm @@ -1,35 +1,36 @@ module Layout.Footer exposing (view) +import Components.Link as Link import Html exposing (Html) import Html.Attributes as Attr import Icon +import Layout.Global as Global import Svg.Attributes as SvgAttr -view : Int -> Html msg -view year = - Html.footer - [ Attr.class "mx-auto max-w-7xl px-6 py-12 md:flex md:items-center md:justify-between lg:px-8" +socialLink : String -> (List (Html.Attribute msg) -> Html msg) -> List (Html msg) -> Html msg +socialLink href icon children = + Link.view [ Attr.href href, Attr.class "group" ] + [ Html.span [ Attr.class "sr-only" ] children + , icon [ SvgAttr.class "h-5 w-5 fill-zinc-700 transition group-hover:fill-zinc-900 dark:group-hover:fill-zinc-500" ] ] - [ Html.div - [ Attr.class "flex justify-center gap-x-6 md:order-2" - ] - [ Html.a - [ Attr.href "https://github.com/guida-lang" - , Attr.class "text-gray-600 hover:text-gray-800" - ] - [ Html.span - [ Attr.class "sr-only" - ] - [ Html.text "GitHub" ] - , Icon.github - [ SvgAttr.class "size-6" - , SvgAttr.fill "currentColor" - ] - ] + + +smallPrint : Int -> Html msg +smallPrint year = + Html.div [ Attr.class "flex flex-col items-center justify-between gap-5 border-t border-zinc-900/5 pt-8 sm:flex-row dark:border-white/5" ] + [ Html.p [ Attr.class "text-xs text-zinc-600 dark:text-zinc-400" ] + [ Html.text ("© " ++ String.fromInt year ++ " Décio Ferreira. All rights reserved.") ] - , Html.p - [ Attr.class "mt-8 text-center text-sm/6 text-gray-600 md:order-1 md:mt-0" + , Html.div [ Attr.class "flex gap-3" ] + [ socialLink Global.githubLink Icon.github [ Html.text "Follow us on GitHub" ] + , socialLink Global.discordLink Icon.discord [ Html.text "Join our Discord server" ] ] - [ Html.text ("© " ++ String.fromInt year ++ " Décio Ferreira. All rights reserved.") ] + ] + + +view : Int -> Html msg +view year = + Html.footer [ Attr.class "mx-auto w-full max-w-2xl space-y-10 pb-16 lg:max-w-5xl" ] + [ smallPrint year ] diff --git a/src/Layout/Global.elm b/src/Layout/Global.elm new file mode 100644 index 0000000..99a758f --- /dev/null +++ b/src/Layout/Global.elm @@ -0,0 +1,34 @@ +module Layout.Global exposing + ( NavItem + , discordLink + , githubLink + , topLevelNavItems + ) + +import Html exposing (Html) + + +type alias NavItem msg = + { href : String + , children : List (Html msg) + } + + +topLevelNavItems : List (NavItem msg) +topLevelNavItems = + [ { href = "/examples", children = [ Html.text "Examples" ] } + , { href = "/try", children = [ Html.text "Try" ] } + , { href = "/docs", children = [ Html.text "Docs" ] } + , { href = "/community", children = [ Html.text "Community" ] } + , { href = "https://package.guida-lang.org", children = [ Html.text "Packages" ] } + ] + + +githubLink : String +githubLink = + "https://github.com/guida-lang" + + +discordLink : String +discordLink = + "https://discord.gg/B6WgPzf5Aa" diff --git a/src/Layout/Header.elm b/src/Layout/Header.elm index ce0dffc..ae18a34 100644 --- a/src/Layout/Header.elm +++ b/src/Layout/Header.elm @@ -1,239 +1,113 @@ module Layout.Header exposing - ( Enabled(..) - , Item(..) - , Mode(..) + ( Config , view ) +import Components.Link as Link +import Components.Logo as Logo +import Components.MobileNavigation as MobileNavigation +import Components.ThemeToggle as ThemeToggle import Html exposing (Html) import Html.Attributes as Attr -import Html.Events as Events +import Html.Attributes.Aria as Aria import Icon +import Layout.Global as Global +import Session exposing (Session) import Svg.Attributes as SvgAttr -type Mode msg - = Navigation (List (Item msg)) - | Custom (Maybe (Item msg)) (List (Item msg)) (List (Html msg)) +type alias Config msg = + { session : Session + , className : Maybe String + , hasSidebar : Bool + , isInsideMobileNavigation : Bool + , setThemeMsg : ThemeToggle.Theme -> msg + , toggleNavigationMsg : msg + } -type Item msg - = Button { label : List (Html msg), msg : Enabled msg } - | Link { label : List (Html msg), href : String } +topLevelNavItem : Global.NavItem msg -> Html msg +topLevelNavItem { href, children } = + Html.li [] + [ Link.view + [ Attr.href href + , Attr.class "text-sm/5 text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" + ] + children + ] -type Enabled msg - = Enabled msg - | Disabled +socialLink : String -> (List (Html.Attribute msg) -> Html msg) -> List (Html msg) -> Html msg +socialLink href icon children = + Link.view [ Attr.href href, Attr.class "flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-zinc-900/5 dark:hover:bg-white/5" ] + [ Html.span [ Attr.class "sr-only" ] children + , icon [ SvgAttr.class "h-5 w-5 fill-zinc-900 dark:fill-white" ] + ] -view : Mode msg -> msg -> Bool -> Html msg -view mode toggleNavigation showNavigation = - Html.header [ Attr.class "absolute inset-x-0 top-0 z-50 shadow-sm" ] - (Html.nav - [ Attr.class "flex items-center justify-between p-6" - , Attr.attribute "aria-label" "Global" - ] - [ Html.div - [ Attr.class "flex lg:flex-1" - ] - [ Html.a - [ Attr.href "/" - , Attr.class "inline-flex items-center gap-x-1.5 font-mono text-2xl -m-1.5 p-1.5 text-amber-500 hover:text-amber-400" - ] - [ Icon.logo [ SvgAttr.class "h-8 w-auto" ] - , Html.div [ Attr.class "" ] - [ Html.text "guida" - ] - ] +view : Config msg -> Html msg +view config = + let + isMobileNavigationOpen = + Session.isMobileNavigationOpen config.session + + classNameAttrs = + config.className + |> Maybe.map (List.singleton << Attr.class) + |> Maybe.withDefault [] + in + Html.header + (Aria.role "banner" + :: Attr.classList + [ ( "fixed inset-x-0 top-0 z-50 flex h-14 items-center justify-between gap-12 px-4 transition sm:px-6", True ) + , ( "lg:left-72 lg:z-30 lg:px-8 xl:left-80", config.hasSidebar ) + , ( "backdrop-blur-xs dark:backdrop-blur-sm", not config.isInsideMobileNavigation ) + , ( "bg-white dark:bg-zinc-900", config.isInsideMobileNavigation ) + , ( "bg-white/[var(--bg-opacity-light)] dark:bg-zinc-900/[var(--bg-opacity-dark)]", not config.isInsideMobileNavigation ) + , ( "lg:justify-end", config.hasSidebar ) ] - , Html.div - [ Attr.class "flex lg:hidden" - ] - [ Html.button - [ Attr.type_ "button" - , Attr.class "-m-2.5 inline-flex items-center justify-center rounded-md p-2.5 text-gray-700" - , Events.onClick toggleNavigation - ] - [ Html.span - [ Attr.class "sr-only" - ] - [ Html.text "Open main menu" - ] - , Icon.burger [ SvgAttr.class "size-6" ] - ] - ] - , Html.div - [ Attr.class "hidden lg:flex lg:gap-x-8 items-center" + :: classNameAttrs + ) + [ Html.div + [ Attr.classList + [ ( "absolute inset-x-0 top-full h-px transition", True ) + , ( "bg-zinc-900/7.5 dark:bg-white/7.5", config.isInsideMobileNavigation || not isMobileNavigationOpen ) ] - (viewTopLevelMode mode) ] - :: navigationMenu mode toggleNavigation showNavigation - ) - - -viewTopLevelMode : Mode msg -> List (Html msg) -viewTopLevelMode mode = - case mode of - Navigation items -> - List.map viewTopLevelItem items - - Custom (Just highlightedItem) items _ -> - List.map viewTopLevelItem (highlightedItem :: items) - - Custom Nothing items _ -> - List.map viewTopLevelItem items - - -viewTopLevelItem : Item msg -> Html msg -viewTopLevelItem item = - case item of - Button button -> - let - eventAttrs : List (Html.Attribute msg) - eventAttrs = - case button.msg of - Enabled msg -> - [ Events.onClick msg ] - - Disabled -> - [ Attr.class "cursor-not-allowed" - , Attr.disabled True - ] - in - Html.button - (Attr.class "inline-flex items-center gap-x-1.5 rounded-md bg-amber-600 px-2 py-1 text-sm font-semibold text-white shadow-xs hover:bg-amber-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-600" - :: eventAttrs - ) - button.label - - Link link -> - Html.a - [ Attr.class "flex gap-x-3 text-sm/6 font-semibold text-gray-900" - , Attr.href link.href + [] + , Html.div + [ Attr.classList + [ ( "flex items-center gap-4", True ) + , ( "lg:hidden", config.hasSidebar ) ] - link.label - - -navigationMenu : Mode msg -> msg -> Bool -> List (Html msg) -navigationMenu mode toggleNavigation showNavigation = - if showNavigation then - let - highlightedItem : List (Html msg) - highlightedItem = - case mode of - Custom (Just item) _ _ -> - [ viewTopLevelItem item ] - - _ -> - [] - in - [ Html.div - [ Attr.class "relative z-50 lg:hidden" - , Attr.attribute "aria-labelledby" "slide-over-title" - , Attr.attribute "role" "dialog" - , Attr.attribute "aria-modal" "true" ] - [ Html.div - [ Attr.class "fixed inset-0 bg-gray-500/75" - , Attr.attribute "aria-hidden" "true" + [ MobileNavigation.view { hasSidebar = config.hasSidebar, isOpen = isMobileNavigationOpen, toggleMsg = config.toggleNavigationMsg } + , Link.view [ Attr.href "/", Aria.ariaLabel "Home" ] + [ Logo.view "h-6" ] - [] - , Html.div - [ Attr.class "fixed inset-0 overflow-hidden" + ] + , Html.div [ Attr.class "flex items-center gap-4" ] + [ Html.nav [ Aria.role "navigation", Attr.class "hidden md:block" ] + [ Html.ul [ Aria.role "list", Attr.class "flex items-center gap-6" ] + (List.map topLevelNavItem Global.topLevelNavItems) ] - [ Html.div - [ Attr.class "absolute inset-0 overflow-hidden" - ] - [ Html.div - [ Attr.class "pointer-events-none fixed inset-y-0 right-0 flex max-w-full pl-10" - ] - [ Html.div - [ Attr.class "pointer-events-auto w-screen max-w-sm" - ] - [ Html.div - [ Attr.class "flex h-full flex-col overflow-y-scroll bg-white py-6 shadow-xl" - ] - [ Html.div - [ Attr.class "px-6" - ] - [ Html.div - [ Attr.class "flex items-start" - , Attr.classList - [ ( "justify-between", not (List.isEmpty highlightedItem) ) - , ( "justify-end", List.isEmpty highlightedItem ) - ] - ] - (highlightedItem - ++ [ Html.div - [ Attr.class "flex h-7 items-center" - ] - [ Html.button - [ Attr.type_ "button" - , Attr.class "-m-2.5 inline-flex items-center justify-center rounded-md p-2.5 text-gray-700" - , Events.onClick toggleNavigation - ] - [ Html.span - [ Attr.class "sr-only" - ] - [ Html.text "Close panel" ] - , Icon.cross [ SvgAttr.class "size-6" ] - ] - ] - ] - ) - ] - , Html.div - [ Attr.class "relative mt-6 flex-1 px-6" - ] - (viewSidebarMode mode) - ] - ] - ] - ] + , Html.div [ Attr.class "hidden md:block md:h-5 md:w-px md:bg-zinc-900/10 md:dark:bg-white/15" ] [] + , Html.div [ Attr.class "flex gap-3" ] + [ ThemeToggle.view (Session.theme config.session) config.setThemeMsg + , socialLink Global.githubLink Icon.github [ Html.text "Follow us on GitHub" ] + , socialLink Global.discordLink Icon.discord [ Html.text "Join our Discord server" ] ] ] - ] - - else - [] - -viewSidebarMode : Mode msg -> List (Html msg) -viewSidebarMode mode = - case mode of - Navigation items -> - List.map viewSidebarItem items - - Custom _ _ content -> - content - - -viewSidebarItem : Item msg -> Html msg -viewSidebarItem item = - case item of - Button button -> - let - eventAttrs : List (Html.Attribute msg) - eventAttrs = - case button.msg of - Enabled msg -> - [ Events.onClick msg ] - - Disabled -> - [ Attr.class "cursor-not-allowed" - , Attr.disabled True - ] - in - Html.button - (Attr.class "rounded-md bg-amber-600 px-3 py-2 text-sm font-semibold text-white shadow-xs hover:bg-amber-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-600" - :: eventAttrs - ) - button.label - - Link link -> - Html.a - [ Attr.class "-mx-3 block rounded-lg px-3 py-2 text-base/7 font-semibold text-gray-900 hover:bg-gray-50" - , Attr.href link.href - ] - link.label + -- , Html.h1 [ Attr.class "title" ] [ Html.a [ Attr.href "/" ] [ Html.text "Guida" ] ] + -- , Html.p [ Attr.class "subtitle" ] [ Html.text "Functional programming, evolved!" ] + -- , Html.nav [ Aria.role "navigation" ] + -- [ Html.ul [] + -- [ Html.li [] [ Html.a [ Attr.href "/examples" ] [ Html.text "Examples" ] ] + -- , Html.li [] [ Html.a [ Attr.href "/try" ] [ Html.text "Try" ] ] + -- , Html.li [] [ Html.a [ Attr.href "/docs" ] [ Html.text "Documentation" ] ] + -- , Html.li [] [ Html.a [ Attr.href "/community" ] [ Html.text "Community" ] ] + -- , Html.li [] [ Html.a [ Attr.href "https://package.guida-lang.org" ] [ Html.text "Packages" ] ] + -- ] + -- ] + ] diff --git a/src/Layout/Main.elm b/src/Layout/Main.elm new file mode 100644 index 0000000..06b67b8 --- /dev/null +++ b/src/Layout/Main.elm @@ -0,0 +1,113 @@ +module Layout.Main exposing (view) + +import Components.Link as Link +import Components.Logo as Logo +import Html exposing (Html) +import Html.Attributes as Attr +import Html.Attributes.Aria as Aria +import Layout.Footer as Footer +import Layout.Header as Header +import Layout.Navigation as Navigation exposing (Navigation) +import Session exposing (Session) + + + +-- CONFIGURATION + + +type alias Config = + { sidebarNavigation : Navigation + } + + + +-- VIEW + + +view : Config -> Session -> (Session.Msg -> msg) -> List (Html msg) -> List (Html msg) +view config session toSessionMsg children = + let + header : Html msg + header = + Header.view + { session = session + , className = Nothing + , hasSidebar = not (List.isEmpty config.sidebarNavigation) + , isInsideMobileNavigation = False + , setThemeMsg = toSessionMsg << Session.SetTheme + , toggleNavigationMsg = toSessionMsg Session.ToggleMobileNavigation + } + + sidebar : Html msg + sidebar = + if List.isEmpty config.sidebarNavigation then + Html.div [ Attr.class "contents lg:pointer-events-auto" ] [ header ] + + else + Html.div [ Attr.class "contents lg:pointer-events-auto lg:block lg:w-72 lg:overflow-y-auto lg:border-r lg:border-zinc-900/10 lg:px-6 lg:pt-4 lg:pb-8 xl:w-80 lg:dark:border-white/10" ] + [ Html.div [ Attr.class "hidden lg:flex" ] + [ Link.view [ Attr.href "/", Aria.ariaLabel "Home" ] + [ Logo.view "h-6" + ] + ] + , header + , Navigation.view [ Attr.class "hidden lg:mt-10 lg:block" ] config.sidebarNavigation + ] + + openAttrs : List (Html.Attribute msg) + openAttrs = + if Session.isMobileNavigationOpen session then + [ Attr.attribute "open" "true" ] + + else + [] + in + [ Html.div [ Attr.class "contents" ] + [ Html.div [ Attr.class "w-full" ] + [ Html.div + [ Attr.classList + [ ( "h-full", True ) + , ( "lg:ml-72 xl:ml-80", not (List.isEmpty config.sidebarNavigation) ) + ] + ] + [ Html.header [ Attr.class "contents lg:pointer-events-none lg:fixed lg:inset-0 lg:z-40 lg:flex" ] + [ sidebar + ] + , Html.div [ Attr.class "relative flex h-full flex-col px-4 pt-14 sm:px-6 lg:px-8" ] + [ Html.main_ [ Attr.class "flex-auto" ] + [ Html.article [ Attr.class "flex h-full flex-col pt-16 pb-10" ] + [ Html.div + [ Attr.classList + [ ( "flex-auto", True ) + , ( "prose dark:prose-invert", True ) + + -- `html :where(& > *)` is used to select all direct children without an increase in specificity like you'd get from just `& > *` + , ( "[html_:where(&>*)]:mx-auto [html_:where(&>*)]:max-w-2xl lg:[html_:where(&>*)]:mx-[calc(50%-min(50%,var(--container-lg)))] lg:[html_:where(&>*)]:max-w-5xl", True ) + ] + ] + children + ] + ] + , Footer.view (Session.year session) + ] + ] + ] + ] + , Html.node "dialog" + (Attr.class "fixed inset-0 z-50 lg:hidden" + :: openAttrs + ) + [ Html.div [ Attr.class "fixed inset-0 top-14 bg-zinc-400/20 backdrop-blur-xs data-closed:opacity-0 data-enter:duration-300 data-enter:ease-out data-leave:duration-200 data-leave:ease-in dark:bg-black/40" ] [] + , Header.view + { session = session + , className = Just "data-closed:opacity-0 data-enter:duration-300 data-enter:ease-out data-leave:duration-200 data-leave:ease-in" + , hasSidebar = not (List.isEmpty config.sidebarNavigation) + , isInsideMobileNavigation = True + , setThemeMsg = toSessionMsg << Session.SetTheme + , toggleNavigationMsg = toSessionMsg Session.ToggleMobileNavigation + } + , Html.div [ Attr.class "fixed top-14 bottom-0 left-0 w-full overflow-y-auto bg-white px-4 pt-6 pb-4 ring-1 shadow-lg shadow-zinc-900/10 ring-zinc-900/7.5 duration-500 ease-in-out data-closed:-translate-x-full min-[416px]:max-w-sm sm:px-6 sm:pb-10 dark:bg-zinc-900 dark:ring-zinc-800" ] + [ Navigation.view [] config.sidebarNavigation + ] + ] + ] diff --git a/src/Layout/Navigation.elm b/src/Layout/Navigation.elm new file mode 100644 index 0000000..36fa790 --- /dev/null +++ b/src/Layout/Navigation.elm @@ -0,0 +1,122 @@ +module Layout.Navigation exposing + ( Navigation + , NavigationLink + , view + ) + +import Components.Link as Link +import Html exposing (Html) +import Html.Attributes as Attr +import Html.Attributes.Aria as Aria +import Layout.Global as Global + + +type alias Navigation = + List NavigationGroup + + +type alias NavigationGroup = + { title : String + , links : List NavigationLink + } + + +type alias NavigationLink = + { title : String + , href : String + , active : Bool + } + + +view : List (Html.Attribute msg) -> Navigation -> Html msg +view attrs navigation = + Html.nav attrs + [ Html.ul [ Aria.role "list" ] + (List.map topLevelNavItem Global.topLevelNavItems + ++ List.indexedMap navigationGroupView navigation + ) + ] + + +topLevelNavItem : Global.NavItem msg -> Html msg +topLevelNavItem { href, children } = + Html.li [ Attr.class "md:hidden" ] + [ Link.view + [ Attr.href href + , Attr.class "block py-1 text-sm text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" + ] + children + ] + + +navigationGroupView : Int -> NavigationGroup -> Html msg +navigationGroupView groupIndex navigationGroup = + Html.li + [ Attr.classList + [ ( "relative mt-6", True ) + , ( "md:mt-0", groupIndex == 0 ) + ] + ] + [ Html.h2 [ Attr.class "text-xs font-semibold text-zinc-900 dark:text-white" ] + [ Html.text navigationGroup.title + ] + , Html.div [ Attr.class "relative mt-3 pl-2" ] + (Html.div [ Attr.class "absolute inset-y-0 left-2 w-px bg-zinc-900/10 dark:bg-white/5" ] [] + :: activePageMarker navigationGroup + ++ [ Html.ul [ Aria.role "list", Attr.class "border-l border-transparent" ] + (List.map navigationLinkView navigationGroup.links) + ] + ) + ] + + +navigationLinkView : NavigationLink -> Html msg +navigationLinkView navigationLink = + Html.li [ Attr.class "relative" ] + [ Link.view + [ Attr.href navigationLink.href + , Attr.classList + [ ( "flex justify-between gap-2 py-1 pr-3 text-sm transition", True ) + , ( "pl-4", True ) + , ( "text-zinc-900 dark:text-white", navigationLink.active ) + , ( "text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white", not navigationLink.active ) + ] + ] + [ Html.text navigationLink.title + ] + ] + + +activePageMarker : NavigationGroup -> List (Html msg) +activePageMarker group = + let + maybeActivePageIndex = + List.indexedMap Tuple.pair group.links + |> List.filter (\( _, link ) -> link.active) + |> List.head + |> Maybe.map Tuple.first + in + case maybeActivePageIndex of + Nothing -> + [] + + Just activePageIndex -> + let + itemHeight : Float + itemHeight = + 2.0 + + offset : Float + offset = + 0.25 + + top : Float + top = + offset + (toFloat activePageIndex * itemHeight) + in + [ Html.div + [ Attr.class "absolute left-2 h-6 w-px bg-amber-500" + , Attr.style "top" (String.fromFloat top ++ "rem") + ] + [] + ] diff --git a/src/Main.elm b/src/Main.elm index 84e6cf2..c2a1ac8 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -7,8 +7,13 @@ module Main exposing ) import Browser +import Browser.Events import Browser.Navigation as Nav +import Components.ThemeToggle as ThemeToggle import Html +import Page.Community as Community +import Page.Docs as Docs +import Page.Examples as Examples import Page.Home as Home import Page.NotFound as NotFound import Page.Try as Try @@ -29,14 +34,27 @@ type alias Model = type CurrentPage = NotFound - | Home Home.Model + | Home + | Docs + | Community + | Examples | Try Try.Model -init : Int -> Url -> Nav.Key -> ( Model, Cmd Msg ) -init year url navKey = +init : Flags -> Url -> Nav.Key -> ( Model, Cmd Msg ) +init flags url navKey = + let + theme : ThemeToggle.Theme + theme = + case flags.theme of + Just "dark" -> + ThemeToggle.Dark + + _ -> + ThemeToggle.Light + in changeRouteTo (Route.fromUrl url) - { session = Session.init year navKey + { session = Session.init flags.year theme navKey , currentPage = NotFound } @@ -52,22 +70,32 @@ changeRouteTo maybeRoute model = ( { model | currentPage = NotFound }, Cmd.none ) Just Route.Home -> - Home.init - |> updateWith Home HomeMsg model + ( { model | currentPage = Home }, Cmd.none ) - Just Route.Try -> - Try.init Nothing - |> updateWith Try TryMsg model + Just Route.Docs -> + ( { model | currentPage = Docs }, Cmd.none ) + + Just Route.Community -> + ( { model | currentPage = Community }, Cmd.none ) + + Just Route.Examples -> + ( { model | currentPage = Examples }, Cmd.none ) Just (Route.Example example) -> Try.init (Just example) |> updateWith Try TryMsg model + Just Route.Try -> + Try.init Nothing + |> updateWith Try TryMsg model + type Msg = ChangedUrl Url | ClickedLink Browser.UrlRequest - | HomeMsg Home.Msg + -- SESSION + | SessionMsg Session.Msg + -- PAGES | TryMsg Try.Msg @@ -89,10 +117,12 @@ update msg model = , Nav.load url ) - ( HomeMsg subMsg, Home subModel ) -> - Home.update subMsg subModel - |> updateWith Home HomeMsg model + -- SESSION + ( SessionMsg subMsg, _ ) -> + Session.update subMsg model.session + |> Tuple.mapFirst (\session -> { model | session = session }) + -- PAGES ( TryMsg subMsg, Try subModel ) -> Try.update subMsg subModel |> updateWith Try TryMsg model @@ -110,15 +140,38 @@ updateWith toCurrentPage toMsg model = -- SUBSCRIPTIONS +withSidebarBreakpoint : Int +withSidebarBreakpoint = + 1024 + + +withoutSidebarBreakpoint : Int +withoutSidebarBreakpoint = + 768 + + subscriptions : Model -> Sub Msg subscriptions model = - case model.currentPage of - Try subModel -> - Try.subscriptions subModel - |> Sub.map TryMsg + let + breakpoint : Int + breakpoint = + case model.currentPage of + Docs -> + withSidebarBreakpoint - _ -> - Sub.none + _ -> + withoutSidebarBreakpoint + in + Sub.batch + [ Browser.Events.onResize (\w _ -> SessionMsg (Session.OnResize { breakpoint = breakpoint, width = w })) + , case model.currentPage of + Try subModel -> + Try.subscriptions subModel + |> Sub.map TryMsg + + _ -> + Sub.none + ] @@ -131,11 +184,20 @@ view model = NotFound -> viewPage never NotFound.view - Home subModel -> - viewPage HomeMsg (Home.view model.session subModel) + Home -> + Home.view model.session SessionMsg + + Docs -> + Docs.view model.session SessionMsg + + Community -> + Community.view model.session SessionMsg + + Examples -> + Examples.view model.session SessionMsg Try subModel -> - viewPage TryMsg (Try.view subModel) + Try.view model.session SessionMsg TryMsg subModel viewPage : (msg -> Msg) -> Browser.Document msg -> Browser.Document Msg @@ -150,7 +212,9 @@ viewPage toMsg { title, body } = type alias Flags = - Int + { theme : Maybe String + , year : Int + } main : Program Flags Model Msg diff --git a/src/Page/Community.elm b/src/Page/Community.elm new file mode 100644 index 0000000..a91cc68 --- /dev/null +++ b/src/Page/Community.elm @@ -0,0 +1,106 @@ +module Page.Community exposing (view) + +import Browser +import Html +import Html.Attributes as Attr +import Html.Attributes.Aria as Aria +import Layout.Main as Layout +import Session exposing (Session) + + + +-- VIEW + + +view : Session -> (Session.Msg -> msg) -> Browser.Document msg +view session toSessionMsg = + { title = "Guida: Community" + , body = + Layout.view { sidebarNavigation = [] } session toSessionMsg <| + [ Html.main_ [ Aria.role "main" ] + [ Html.section [] + [ Html.h2 [] [ Html.text "Community" ] + , Html.p [] + [ Html.text "Guida is built in the open, and its future depends on the people who use it, contribute to it, and share ideas. Whether you're here to report a bug, propose a feature, improve the compiler, or just explore the language, you're very welcome to join the community." + ] + , Html.h3 [] [ Html.text "Where to Talk" ] + , Html.ul [] + [ Html.li [] + [ Html.strong [] [ Html.text "Discord:" ] + , Html.text " Join us in the " + , Html.a [ Attr.href "https://discord.gg/B6WgPzf5Aa" ] [ Html.text "Guida Discord" ] + , Html.text " to connect with other contributors and users, ask questions, and share ideas." + ] + , Html.li [] + [ Html.strong [] [ Html.text "GitHub Discussions:" ] + , Html.text " On the " + , Html.a [ Attr.href "https://github.com/orgs/guida-lang/discussions" ] [ Html.text "Guida Discussions" ] + , Html.text " you can ask and answer questions, share updates, have open-ended conversations, and follow along on decisions affecting the community's way of working." + ] + , Html.li [] + [ Html.strong [] [ Html.text "GitHub Issues:" ] + , Html.text " Found a bug or missing feature? Report it in the " + , Html.a [ Attr.href "https://github.com/guida-lang/compiler/issues" ] [ Html.text "Guida Compiler issue tracker" ] + , Html.text "." + ] + ] + , Html.h3 [] [ Html.text "How to Contribute" ] + , Html.ul [] + [ Html.li [] + [ Html.text "Read the " + , Html.a [ Attr.href "https://github.com/guida-lang/compiler/blob/master/CONTRIBUTING.md" ] [ Html.text "Contributing Guide" ] + , Html.text " to learn how to get started." + ] + , Html.li [] + [ Html.text "Check out open issues labeled " + , Html.a [ Attr.href "https://github.com/guida-lang/compiler/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22" ] [ Html.text "“good first issue”" ] + , Html.text "." + ] + , Html.li [] + [ Html.text "Improvements to the " + , Html.strong [] [ Html.text "compiler" ] + , Html.text ", " + , Html.strong [] [ Html.text "package registry" ] + , Html.text ", and " + , Html.strong [] [ Html.text "tooling" ] + , Html.text " are always welcome." + ] + ] + , Html.h3 [] [ Html.text "Stay Up to Date" ] + , Html.ul [] + [ Html.li [] + [ Html.text "Follow ongoing work on " + , Html.a [ Attr.href "https://github.com/guida-lang" ] [ Html.text "GitHub" ] + , Html.text "." + ] + , Html.li [] + [ Html.text "See the " + , Html.a [ Attr.href "https://github.com/orgs/guida-lang/discussions" ] [ Html.text "GitHub discussions" ] + , Html.text " for what's next." + ] + , Html.li [] + [ Html.text "Keep an eye on the " + , Html.a [ Attr.href "https://github.com/orgs/guida-lang/discussions/112" ] [ Html.text "Interesting Projects & Tools" ] + , Html.text " list to discover related work in the ecosystem." + ] + ] + , Html.h3 [] [ Html.text "Guiding Principles" ] + , Html.p [] [ Html.strong [] [ Html.text "Guida thrives on:" ] ] + , Html.ul [] + [ Html.li [] + [ Html.strong [] [ Html.text "Backward Compatibility:" ] + , Html.text " Respecting existing code whenever possible." + ] + , Html.li [] + [ Html.strong [] [ Html.text "Self-hosted development:" ] + , Html.text " Building the language in the language itself." + ] + , Html.li [] + [ Html.strong [] [ Html.text "Community-driven innovation:" ] + , Html.text " Growing through shared ideas and contributions." + ] + ] + ] + ] + ] + } diff --git a/src/Page/Docs.elm b/src/Page/Docs.elm new file mode 100644 index 0000000..d791c01 --- /dev/null +++ b/src/Page/Docs.elm @@ -0,0 +1,140 @@ +module Page.Docs exposing (view) + +import Browser +import Html +import Html.Attributes as Attr +import Html.Attributes.Aria as Aria +import Layout.Main as Layout +import Layout.Navigation exposing (Navigation) +import Session exposing (Session) + + +sidebarNavigation : Navigation +sidebarNavigation = + [ { title = "Commands" + , links = + [ { title = "repl", href = "/docs/1.0.0/commands/repl", active = False } + , { title = "init", href = "/docs/1.0.0/commands/init", active = True } + , { title = "make", href = "/docs/1.0.0/commands/make", active = False } + , { title = "install", href = "/docs/1.0.0/commands/install", active = False } + , { title = "uninstall", href = "/docs/1.0.0/commands/uninstall", active = False } + , { title = "bump", href = "/docs/1.0.0/commands/bump", active = False } + , { title = "diff", href = "/docs/1.0.0/commands/diff", active = False } + , { title = "publish", href = "/docs/1.0.0/commands/publish", active = False } + , { title = "format", href = "/docs/1.0.0/commands/format", active = False } + , { title = "test", href = "/docs/1.0.0/commands/test", active = False } + ] + } + , { title = "Hints" + , links = + [ { title = "Bad recursion", href = "/docs/1.0.0/hints/bad-recursion", active = False } + , { title = "Comparing custom types", href = "/docs/1.0.0/hints/comparing-custom-types", active = False } + , { title = "Comparing records", href = "/docs/1.0.0/hints/comparing-records", active = False } + , { title = "Implicit casts", href = "/docs/1.0.0/hints/implicit-casts", active = False } + , { title = "Import cycles", href = "/docs/1.0.0/hints/import-cycles", active = False } + , { title = "Imports", href = "/docs/1.0.0/hints/imports", active = False } + , { title = "Infinite type", href = "/docs/1.0.0/hints/infinite-type", active = False } + , { title = "Missing patterns", href = "/docs/1.0.0/hints/missing-patterns", active = False } + , { title = "Optimize", href = "/docs/1.0.0/hints/optimize", active = False } + , { title = "Port modules", href = "/docs/1.0.0/hints/port-modules", active = False } + , { title = "Recursive alias", href = "/docs/1.0.0/hints/recursive-alias", active = False } + , { title = "Shadowing", href = "/docs/1.0.0/hints/shadowing", active = False } + , { title = "Tuples", href = "/docs/1.0.0/hints/tuples", active = False } + , { title = "Type annotations", href = "/docs/1.0.0/hints/type-annotations", active = False } + ] + } + ] + + + +-- VIEW + + +view : Session -> (Session.Msg -> msg) -> Browser.Document msg +view session toSessionMsg = + { title = "Guida: Documentation" + , body = + Layout.view { sidebarNavigation = sidebarNavigation } session toSessionMsg <| + [ Html.section [] + [ Html.h2 [] [ Html.text "Documentation" ] + , Html.h3 [] [ Html.text "Guida REPL" ] + , Html.p [] [ Html.text "The `repl` command opens up an interactive programming session, sometimes called a **Read-Eval-Print Loop**." ] + , Html.p [] [ Html.text "It lets you try out Guida expressions, explore functions, and learn the language in a quick and interactive way." ] + , Html.code [] [ Html.text "guida repl" ] + , Html.p [] [ Html.text "Once inside the REPL, you can type in Guida expressions and immediately see the results:" ] + , Html.code [] + [ Html.text "> 1 + 2" + , Html.text "3" + , Html.text "> String.toUpper \"hello\"" + , Html.text "\"HELLO\"" + , Html.text "> List.map (\\n -> n * 2) [1, 2, 3]" + , Html.text "[2,4,6]" + ] + ] + ] + } + + + +-- # Guida REPL +-- The `repl` command opens up an interactive programming session, sometimes called a **Read-Eval-Print Loop**. +-- It lets you try out Guida expressions, explore functions, and learn the language in a quick and interactive way. +-- ```bash +-- guida repl +-- ```` +-- Once inside the REPL, you can type in Guida expressions and immediately see the results: +-- ```elm +-- > 1 + 2 +-- 3 +-- > String.toUpper "hello" +-- "HELLO" +-- > List.map (\n -> n * 2) [1, 2, 3] +-- [2,4,6] +-- ``` +-- --- +-- ## Learning with the REPL +-- A great way to get started with Guida is by experimenting in the REPL. +-- Follow along with the [official Guida Guide](https://guida-lang.org/docs) — it includes examples and exercises that you can try directly in the REPL. +-- Exploring small snippets of code interactively can be much faster than editing a file, compiling, and running a project. +-- --- +-- ## Customizing the REPL +-- You can configure how the REPL runs using these flags: +-- ### `--interpreter=` +-- Specify an alternate JavaScript runtime to evaluate Guida code. +-- By default, Guida uses your system’s `node` installation, but you may override it: +-- ```bash +-- guida repl --interpreter=nodejs +-- ``` +-- This can be useful if your system differentiates between `node` and `nodejs` binaries, or if you want to run Guida with a custom JS runtime. +-- --- +-- ### `--no-colors` +-- Disable ANSI colors in the REPL output: +-- ```bash +-- guida repl --no-colors +-- ``` +-- This can help if your terminal’s color scheme makes output hard to read, or if you prefer plain text. +-- --- +-- ## Example Session +-- Here’s a quick demonstration: +-- ```bash +-- $ guida repl +-- ---- +-- Guida REPL 0.19.1 +-- Type :help for available commands. Press Ctrl+D to exit. +-- > 5 * 5 +-- 25 +-- > (\\x -> x + 1) 41 +-- 42 +-- > String.length "Guida" +-- 5 +-- ``` +-- --- +-- ## Tips +-- * Use the REPL for **experimentation and learning** — it’s not meant to replace writing modules and building projects. +-- * REPL does not persist your definitions between sessions. If you discover something useful, copy it into a `.guida` file in your project. +-- * Use `:exit` or press `Ctrl+D` to quit. +-- --- +-- ## Related Topics +-- * [Getting Started with Guida](https://guida-lang.org/docs/getting-started) +-- * [Guida Guide](https://guida-lang.org/docs) +-- * [Community](https://guida-lang.org/community) diff --git a/src/Page/Examples.elm b/src/Page/Examples.elm new file mode 100644 index 0000000..baaf5c4 --- /dev/null +++ b/src/Page/Examples.elm @@ -0,0 +1,119 @@ +module Page.Examples exposing (view) + +import Browser +import Components.Button as Button +import Html exposing (Html) +import Html.Attributes as Attr +import Html.Attributes.Aria as Aria +import Layout.Main as Layout +import Session exposing (Session) + + +type alias Example = + { title : String + , links : List ExampleLink + } + + +type alias ExampleLink = + { title : String + , href : String + } + + +examples : List Example +examples = + [ { title = "HTML" + , links = + [ { title = "Hello", href = "/examples/hello" } + , { title = "Groceries", href = "/examples/groceries" } + , { title = "Shapes", href = "/examples/shapes" } + ] + } + , { title = "User Input" + , links = + [ { title = "Buttons", href = "/examples/buttons" } + , { title = "Text Fields", href = "/examples/text-fields" } + , { title = "Forms", href = "/examples/forms" } + ] + } + , { title = "Random" + , links = + [ { title = "Numbers", href = "/examples/numbers" } + , { title = "Cards", href = "/examples/cards" } + , { title = "Positions", href = "/examples/positions" } + ] + } + , { title = "HTTP" + , links = + [ { title = "Book", href = "/examples/book" } + , { title = "Quotes", href = "/examples/quotes" } + ] + } + , { title = "Time" + , links = + [ { title = "Time", href = "/examples/time" } + , { title = "Clock", href = "/examples/clock" } + ] + } + , { title = "Files" + , links = + [ { title = "Upload", href = "/examples/upload" } + , { title = "Drag-and-Drop", href = "/examples/drag-and-drop" } + , { title = "Image Previews", href = "/examples/image-previews" } + ] + } + , { title = "WebGL" + , links = + [ { title = "Triangle", href = "/examples/triangle" } + , { title = "Cube", href = "/examples/cube" } + , { title = "Crate", href = "/examples/crate" } + , { title = "Thwomp", href = "/examples/thwomp" } + , { title = "First Person", href = "/examples/first-person" } + ] + } + , { title = "Playground" + , links = + [ { title = "Picture", href = "/examples/picture" } + , { title = "Animation", href = "/examples/animation" } + , { title = "Mouse", href = "/examples/mouse" } + , { title = "Keyboard", href = "/examples/keyboard" } + , { title = "Turtle", href = "/examples/turtle" } + , { title = "Mario", href = "/examples/mario" } + ] + } + ] + + + +-- VIEW + + +view : Session -> (Session.Msg -> msg) -> Browser.Document msg +view session toSessionMsg = + { title = "Guida: Examples" + , body = + Layout.view { sidebarNavigation = [] } session toSessionMsg <| + [ Html.h1 [] [ Html.text "Examples" ] + , Html.div [ Attr.class "not-prose mt-4 grid grid-cols-1 gap-8 border-t border-zinc-900/5 pt-10 sm:grid-cols-2 lg:grid-cols-4 dark:border-white/5" ] + (List.map exampleView examples) + ] + } + + +exampleView : Example -> Html msg +exampleView example = + Html.div [] + [ Html.h2 [ Attr.class "text-sm font-semibold text-zinc-900 dark:text-white" ] + [ Html.text example.title + ] + , Html.ul [] + (List.map exampleLinkView example.links) + ] + + +exampleLinkView : ExampleLink -> Html msg +exampleLinkView link = + Html.li [] + [ Button.view (Button.Link link.href) Button.Text Nothing [] [ Html.text link.title ] + ] diff --git a/src/Page/Home.elm b/src/Page/Home.elm index 381e66b..c73714d 100644 --- a/src/Page/Home.elm +++ b/src/Page/Home.elm @@ -1,133 +1,210 @@ -module Page.Home exposing - ( Model - , Msg - , init - , update - , view - ) +module Page.Home exposing (view) import Browser +import Components.Button as Button +import Components.HeroPattern as HeroPattern import Html import Html.Attributes as Attr -import Layout.Footer -import Layout.Header +import Layout.Main as Layout import Session exposing (Session) --- MODEL - - -type alias Model = - { showNavigation : Bool - } - - -init : ( Model, Cmd Msg ) -init = - ( { showNavigation = False - } - , Cmd.none - ) - - - --- UPDATE - - -type Msg - = ToggleNavigation - - -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg model = - case msg of - ToggleNavigation -> - ( { model | showNavigation = not model.showNavigation } - , Cmd.none - ) - - - -- VIEW -view : Session -> Model -> Browser.Document Msg -view session model = - { title = "Guida" +view : Session -> (Session.Msg -> msg) -> Browser.Document msg +view session toSessionMsg = + { title = "Guida: Home" , body = - [ Layout.Header.view headerMode ToggleNavigation model.showNavigation - , Html.main_ [ Attr.class "relative isolate px-6 pt-14 lg:px-8" ] - [ Html.div - [ Attr.class "absolute inset-x-0 -top-10 -z-10 transform-gpu overflow-hidden blur-3xl sm:-top-20" - , Attr.attribute "aria-hidden" "true" + Layout.view { sidebarNavigation = [] } session toSessionMsg <| + [ HeroPattern.view + , Html.h1 [] [ Html.text "What is Guida?" ] + , Html.p [ Attr.class "lead" ] + [ Html.text "Guida is a functional programming language that builds upon the solid foundation of " + , Html.a [ Attr.href "https://elm-lang.org" ] [ Html.text "Elm" ] + , Html.text ", offering backward compatibility with all existing " + , Html.a [ Attr.href "https://github.com/elm/compiler/releases/tag/0.19.1" ] [ Html.text "Elm 0.19.1" ] + , Html.text " projects." ] - [ Html.div - [ Attr.class "relative left-[calc(50%-11rem)] aspect-[1155/678] w-[36.125rem] bg-gradient-to-tr from-[#f54a00] to-[#fcc800] opacity-30 sm:-left-5 sm:w-[72.1875rem]" - , Attr.style "clip-path" "polygon(25% 0%,70% 0%,40% 35%,95% 35%,20% 100%,40% 55%,0% 55%)" - ] - [] + , Html.div [ Attr.class "not-prose mt-6 mb-16 flex gap-3" ] + [ Button.view (Button.Link "/try") Button.Primary Nothing [] <| + [ Html.text "Try" ] + , Button.view (Button.Link "/docs") Button.Outline (Just Button.RightArrow) [] <| + [ Html.text "Documentation" ] + ] + , Html.h2 [ Attr.class "scroll-mt-24" ] [ Html.text "Vision" ] + , Html.p [] + [ Html.text "Guida builds on the foundations of Elm, aiming to advance the future of functional programming. By translating Elm's compiler from Haskell to a self-hosted environment, Guida helps developers to build reliable, maintainable, and performant applications without leaving the language they love." + ] + , Html.p [] + [ Html.strong [] [ Html.text "Continuity and Confidence (Version 0.x):" ] + , Html.text " Guida starts by ensuring full backward compatibility with Elm v0.19.1, allowing developers to migrate effortlessly and explore Guida with complete confidence." ] - , Html.div [ Attr.class "mx-auto max-w-4xl py-32 sm:py-48 lg:py-56" ] - [ Html.div [ Attr.class "hidden sm:mb-8 sm:flex sm:justify-center" ] - [ Html.div [ Attr.class "relative rounded-full px-3 py-1 text-sm/6 text-gray-600 ring-1 ring-gray-900/10 hover:ring-gray-900/20" ] - [ Html.text "Install Guida locally via npm " - , Html.a - [ Attr.href "/docs/install" - , Attr.class "font-semibold text-amber-600" - ] - [ Html.span - [ Attr.class "absolute inset-0" - , Attr.attribute "aria-hidden" "true" - ] - [] - , Html.text "Read more " - , Html.span [ Attr.attribute "aria-hidden" "true" ] - [ Html.text "→" ] - ] - ] + , Html.p [] + [ Html.text "This commitment to continuity means that this version will faithfully replicate not only the features and behaviors of Elm v0.19.1, but also any existing bugs and quirks. By doing so, we provide a stable and predictable environment for developers, ensuring that their existing Elm projects work exactly as expected when migrated to Guida." + ] + , Html.p [] + [ Html.strong [] [ Html.text "Evolution and Innovation (Version 1.x and Beyond):" ] + , Html.text " As Guida evolves, we will introduce new features and improvements. This phase will foster a unified ecosystem that adapts to the needs of its users." + ] + , Html.p [] [ Html.strong [] [ Html.text "Core Principles:" ] ] + , Html.ul [] + [ Html.li [] + [ Html.strong [] [ Html.text "Backward Compatibility:" ] + , Html.text " Respect for existing Elm projects, ensuring a frictionless migration." ] - , Html.div [ Attr.class "text-center" ] - [ Html.h1 [ Attr.class "text-balance text-5xl font-semibold tracking-tight text-gray-900 sm:text-7xl" ] - [ Html.text "Guida: functional programming, evolved!" ] - , Html.p [ Attr.class "mt-8 text-pretty text-lg font-medium text-gray-500 sm:text-xl/8" ] - [ Html.text "Guida is a functional programming language that builds upon the solid foundation of Elm, offering backward compatibility with all existing Elm 0.19.1 projects." ] - , Html.div [ Attr.class "mt-10 flex items-center justify-center gap-x-6" ] - [ Html.a - [ Attr.href "/try" - , Attr.class "rounded-md bg-amber-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-amber-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-600" - ] - [ Html.text "Try Guida" ] - , Html.a - [ Attr.href "/docs" - , Attr.class "text-sm/6 font-semibold text-gray-900" - ] - [ Html.text "Documentation " - , Html.span [ Attr.attribute "aria-hidden" "true" ] - [ Html.text "→" ] - ] - ] + , Html.li [] + [ Html.strong [] [ Html.text "Accessibility:" ] + , Html.text " Lowering barriers for developers by implementing Guida's core in its own syntax." ] ] + , Html.p [] + [ Html.text "Our ultimate goal is to create a language that inherits the best aspects of Elm while adapting and growing to meet the needs of its users." + ] + + -- Html.main_ [ Aria.role "main" ] + -- [ Html.section [] + -- [ Html.h2 [] [ Html.text "What is Guida?" ] + -- , Html.p [] + -- [ Html.text "Guida is a functional programming language that builds upon the solid foundation of " + -- , Html.a [ Attr.href "https://elm-lang.org" ] [ Html.text "Elm" ] + -- , Html.text ", offering backward compatibility with all existing " + -- , Html.a [ Attr.href "https://github.com/elm/compiler/releases/tag/0.19.1" ] [ Html.text "Elm 0.19.1" ] + -- , Html.text " projects." + -- ] + -- , Html.h3 [] [ Html.text "Vision" ] + -- , Html.p [] + -- [ Html.text "Guida builds on the foundations of Elm, aiming to advance the future of functional programming. By translating Elm's compiler from Haskell to a self-hosted environment, Guida helps developers to build reliable, maintainable, and performant applications without leaving the language they love." + -- ] + -- , Html.p [] + -- [ Html.strong [] [ Html.text "Continuity and Confidence (Version 0.x):" ] + -- , Html.text " Guida starts by ensuring full backward compatibility with Elm v0.19.1, allowing developers to migrate effortlessly and explore Guida with complete confidence." + -- ] + -- , Html.p [] + -- [ Html.text "This commitment to continuity means that this version will faithfully replicate not only the features and behaviors of Elm v0.19.1, but also any existing bugs and quirks. By doing so, we provide a stable and predictable environment for developers, ensuring that their existing Elm projects work exactly as expected when migrated to Guida." + -- ] + -- , Html.p [] + -- [ Html.strong [] [ Html.text "Evolution and Innovation (Version 1.x and Beyond):" ] + -- , Html.text " As Guida evolves, we will introduce new features and improvements. This phase will foster a unified ecosystem that adapts to the needs of its users." + -- ] + -- , Html.p [] [ Html.strong [] [ Html.text "Core Principles:" ] ] + -- , Html.ul [] + -- [ Html.li [] + -- [ Html.strong [] [ Html.text "Backward Compatibility:" ] + -- , Html.text " Respect for existing Elm projects, ensuring a frictionless migration." + -- ] + -- , Html.li [] + -- [ Html.strong [] [ Html.text "Accessibility:" ] + -- , Html.text " Lowering barriers for developers by implementing Guida's core in its own syntax." + -- ] + -- ] + -- , Html.p [] + -- [ Html.text "Our ultimate goal is to create a language that inherits the best aspects of Elm while adapting and growing to meet the needs of its users." + -- ] + -- ] + -- , Html.section [] + -- [ Html.h2 [] [ Html.text "Try It" ] + -- , Html.p [] + -- [ Html.text "Experiment with Guida in your browser. Write, run, and explore code instantly with the online Guida playground." + -- ] + -- , Html.p [] + -- [ Html.text "This is the easiest way to experiment with Guida in your browser. " + -- , Html.a [ Attr.href "/try" ] [ Html.text "Try it" ] + -- , Html.text ", no installation required." + -- ] + -- ] + -- , Html.section [] + -- [ Html.h2 [] [ Html.text "Documentation" ] + -- , Html.p [] + -- [ Html.text "The " + -- , Html.a [ Attr.href "/docs" ] [ Html.text "documentation" ] + -- , Html.text " is the best place to start learning about Guida. It will give you a solid foundation for creating applications. Once you have worked through that, the next place to look for documentation is on the " + -- , Html.a [ Attr.href "https://package.guida-lang.org" ] [ Html.text "packages" ] + -- , Html.text " you are using." + -- ] + -- ] + -- , Html.section [] + -- [ Html.h2 [] [ Html.text "Community" ] + -- , Html.p [] + -- [ Html.text "Join us to shape the language together. See our " + -- , Html.a [ Attr.href "/community" ] [ Html.text "Community" ] + -- , Html.text " page for more details on how to get involved. Here is a list of some of the main resources:" + -- , Html.ul [] + -- [ Html.li [] + -- [ Html.a [ Attr.href "https://github.com/guida-lang" ] + -- [ Html.text "Guida source code" + -- ] + -- ] + -- , Html.li [] + -- [ Html.a [ Attr.href "https://github.com/orgs/guida-lang/discussions" ] + -- [ Html.text "Collaborative communication forum" + -- ] + -- ] + -- , Html.li [] + -- [ Html.a [ Attr.href "https://github.com/guida-lang/compiler/blob/main/CONTRIBUTING.md" ] + -- [ Html.text "Contributing Guide" + -- ] + -- ] + -- ] + -- , Html.h3 [] [ Html.text "Contribute" ] + -- , Html.p [] [ Html.text "Guida is open source and thrives with your help: report bugs, improve the compiler and tools, and share your projects." ] + -- , Html.ul [] + -- [ Html.li [] + -- [ Html.a [ Attr.href "https://github.com/guida-lang/compiler/issues" ] [ Html.text "File a bug or feature request" ] + -- ] + -- , Html.li [] + -- [ Html.text "Help triage existing issues" + -- ] + -- , Html.li [] + -- [ Html.text "Submit improvements to the " + -- , Html.a [ Attr.href "https://github.com/guida-lang/compiler" ] [ Html.text "compiler" ] + -- , Html.text ", " + -- , Html.a [ Attr.href "https://github.com/guida-lang/package-registry" ] [ Html.text "registry" ] + -- , Html.text ", or " + -- , Html.a [ Attr.href "https://github.com/guida-lang" ] [ Html.text "tooling" ] + -- ] + -- , Html.li [] + -- [ Html.text "Improve documentation or examples" + -- ] + -- , Html.li [] + -- [ Html.text "Try out Guida and give us feedback" + -- ] + -- , Html.li [] + -- [ Html.text "Look for " + -- , Html.a [ Attr.href "https://github.com/guida-lang/compiler/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22" ] [ Html.text "good first issues" ] + -- , Html.text " if you're just getting started" + -- ] + -- , Html.li [] + -- [ Html.text "Port known issues or improvements from the Elm ecosystem" + -- ] + -- ] + -- ] + -- , Html.p [] + -- [ Html.text "Guida builds on projects like " + -- , Html.a [ Attr.href "https://github.com/elm/compiler" ] [ Html.text "elm/compiler" ] + -- , Html.text ", " + -- , Html.a [ Attr.href "https://github.com/avh4/elm-format" ] [ Html.text "elm-format" ] + -- , Html.text ", " + -- , Html.a [ Attr.href "https://github.com/elm-explorations/test" ] [ Html.text "elm-test" ] + -- , Html.text ", and " + -- , Html.a [ Attr.href "https://github.com/zwilias/elm-json" ] [ Html.text "elm-json" ] + -- , Html.text ". If you've encountered issues or ideas in those tools that feel worth bringing into Guida, feel free to reference them in a new issue or PR." + -- ] + -- ] + -- , Html.section [] + -- [ Html.h2 [] [ Html.text "Packages" ] + -- , Html.p [] + -- [ Html.text "Explore and publish libraries with the " + -- , Html.a [ Attr.href "https://package.guida-lang.org" ] [ Html.text "Guida package registry" ] + -- , Html.text "." + -- ] + -- ] + -- ] + -- , Html.footer [] + -- [ Html.p [] + -- [ Html.text "Guida builds on the work of the Elm community and related projects. We aim to carry the torch forward while staying true to what makes Elm great." + -- ] + -- ] ] - , Layout.Footer.view (Session.year session) - ] } - - -headerMode : Layout.Header.Mode Msg -headerMode = - Layout.Header.Navigation - [ Layout.Header.Link - { label = [ Html.text "Try" ] - , href = "/try" - } - , Layout.Header.Link - { label = [ Html.text "Documentation" ] - , href = "/docs" - } - , Layout.Header.Link - { label = [ Html.text "Packages" ] - , href = "https://package.guida-lang.org" - } - ] diff --git a/src/Page/NotFound.elm b/src/Page/NotFound.elm index 7caf96b..c1ad509 100644 --- a/src/Page/NotFound.elm +++ b/src/Page/NotFound.elm @@ -3,6 +3,7 @@ module Page.NotFound exposing (view) import Browser import Html import Html.Attributes as Attr +import Html.Attributes.Aria as Aria @@ -11,30 +12,15 @@ import Html.Attributes as Attr view : Browser.Document Never view = - { title = "Guida" + { title = "Guida: Page not found" , body = - [ Html.main_ [ Attr.class "grid min-h-full place-items-center bg-white px-6 py-24 sm:py-32 lg:px-8" ] - [ Html.div [ Attr.class "text-center" ] - [ Html.p [ Attr.class "text-base font-semibold text-amber-600" ] - [ Html.text "404" ] - , Html.h1 [ Attr.class "mt-4 text-5xl font-semibold tracking-tight text-balance text-gray-900 sm:text-7xl" ] - [ Html.text "Page not found" ] - , Html.p [ Attr.class "mt-6 text-lg font-medium text-pretty text-gray-500 sm:text-xl/8" ] - [ Html.text "Sorry, we couldn't find the page you're looking for." ] - , Html.div [ Attr.class "mt-10 flex items-center justify-center gap-x-6" ] - [ Html.a - [ Attr.href "/" - , Attr.class "rounded-md bg-amber-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-xs hover:bg-amber-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-600" - ] - [ Html.text "Go back home" ] - , Html.a - [ Attr.href "/" - , Attr.class "text-sm font-semibold text-gray-900" - ] - [ Html.text "Contact support " - , Html.span [ Attr.attribute "aria-hidden" "true" ] - [ Html.text "→" ] - ] + [ Html.main_ [ Aria.role "main" ] + [ Html.section [] + [ Html.h2 [] [ Html.text "Page not found" ] + , Html.p [] [ Html.text "Sorry, we couldn't find the page you're looking for." ] + , Html.ul [] + [ Html.li [] [ Html.a [ Attr.href "/" ] [ Html.text "Go back home" ] ] + , Html.li [] [ Html.a [ Attr.href "https://github.com/guida-lang/guida-lang.org/issues" ] [ Html.text "File a bug" ] ] ] ] ] diff --git a/src/Page/Try.elm b/src/Page/Try.elm index 8cf667e..a4ea182 100644 --- a/src/Page/Try.elm +++ b/src/Page/Try.elm @@ -17,8 +17,9 @@ import Http import Icon import Json.Decode as Decode import Json.Encode as Encode -import Layout.Header +import Layout.Main as Layout import Route +import Session exposing (Session) import Svg.Attributes as SvgAttr @@ -66,7 +67,7 @@ init maybeExample = , case maybeExample of Just example -> Http.get - { url = "/examples/" ++ Route.exampleSrc example ++ ".guida" + { url = "/example-files/" ++ Route.exampleSrc example ++ ".guida" , expect = Http.expectString GotExampleContent } @@ -138,8 +139,8 @@ subscriptions _ = -- VIEW -view : Model -> Browser.Document Msg -view model = +view : Session -> (Session.Msg -> msg) -> (Msg -> msg) -> Model -> Browser.Document msg +view session toSessionMsg toMsg model = { title = case model.example of Just Route.HelloWorld -> @@ -151,26 +152,26 @@ view model = Nothing -> "Try Guida!" , body = - [ Layout.Header.view (headerMode model) TogglePackages model.showPackages - , Html.section [ Attr.class "h-screen pt-20 grid grid-cols-none grid-rows-2 sm:grid-cols-2 sm:grid-rows-none" ] - [ Html.aside - [ Attr.class "overflow-y-auto border-b sm:border-b-0 sm:border-r border-gray-200" - ] - [ Html.node "wc-codemirror" - [ Attr.id "editor" - , Attr.class "h-full" - , Attr.attribute "mode" "javascript" + Layout.view { sidebarNavigation = [] } session toSessionMsg <| + [ Html.section [ Attr.class "h-screen pt-20 grid grid-cols-none grid-rows-2 sm:grid-cols-2 sm:grid-rows-none" ] + [ Html.aside + [ Attr.class "overflow-y-auto border-b sm:border-b-0 sm:border-r border-gray-200" + ] + [ Html.node "wc-codemirror" + [ Attr.id "editor" + , Attr.class "h-full" + , Attr.attribute "mode" "javascript" + ] + [] ] - [] + , Html.main_ [ Attr.class "overflow-y-auto" ] (outputView toMsg model) ] - , Html.main_ [ Attr.class "overflow-y-auto" ] (outputView model) ] - ] } -outputView : Model -> List (Html Msg) -outputView model = +outputView : (Msg -> msg) -> Model -> List (Html msg) +outputView toMsg model = case ( model.example, model.maybeResult ) of ( Nothing, Nothing ) -> [ Html.div [ Attr.class "flex h-full" ] @@ -228,46 +229,42 @@ outputView model = ] -headerMode : Model -> Layout.Header.Mode Msg -headerMode model = - Layout.Header.Custom (Just (rebuildButton model)) [] [] - - -rebuildButton : Model -> Layout.Header.Item Msg -rebuildButton model = - case model.status of - NotAsked -> - Layout.Header.Button - { label = - [ Icon.arrowPath [ SvgAttr.class "-ml-0.5 size-5" ] - , Html.text "Rebuild" - ] - , msg = Layout.Header.Enabled Rebuild - } - - Compiling -> - Layout.Header.Button - { label = - [ Icon.arrowPath [ SvgAttr.class "-ml-0.5 size-5 animate-spin" ] - , Html.text "Compiling..." - ] - , msg = Layout.Header.Disabled - } - - Success -> - Layout.Header.Button - { label = - [ Icon.check [ SvgAttr.class "-ml-0.5 size-5" ] - , Html.text "Success" - ] - , msg = Layout.Header.Enabled Rebuild - } - ProblemsFound -> - Layout.Header.Button - { label = - [ Icon.xMark [ SvgAttr.class "-ml-0.5 size-5" ] - , Html.text "Problems found" - ] - , msg = Layout.Header.Enabled Rebuild - } +-- headerMode : Model -> Layout.Header.Mode Msg +-- headerMode model = +-- Layout.Header.Custom (Just (rebuildButton model)) [] [] +-- rebuildButton : Model -> Layout.Header.Item Msg +-- rebuildButton model = +-- case model.status of +-- NotAsked -> +-- Layout.Header.Button +-- { label = +-- [ Icon.arrowPath [ SvgAttr.class "-ml-0.5 size-5" ] +-- , Html.text "Rebuild" +-- ] +-- , msg = Layout.Header.Enabled Rebuild +-- } +-- Compiling -> +-- Layout.Header.Button +-- { label = +-- [ Icon.arrowPath [ SvgAttr.class "-ml-0.5 size-5 animate-spin" ] +-- , Html.text "Compiling..." +-- ] +-- , msg = Layout.Header.Disabled +-- } +-- Success -> +-- Layout.Header.Button +-- { label = +-- [ Icon.check [ SvgAttr.class "-ml-0.5 size-5" ] +-- , Html.text "Success" +-- ] +-- , msg = Layout.Header.Enabled Rebuild +-- } +-- ProblemsFound -> +-- Layout.Header.Button +-- { label = +-- [ Icon.xMark [ SvgAttr.class "-ml-0.5 size-5" ] +-- , Html.text "Problems found" +-- ] +-- , msg = Layout.Header.Enabled Rebuild +-- } diff --git a/src/Route.elm b/src/Route.elm index ba062bd..0af399e 100644 --- a/src/Route.elm +++ b/src/Route.elm @@ -11,8 +11,11 @@ import Url.Parser as Parser exposing ((), Parser) type Route = Home - | Try + | Docs + | Community + | Examples | Example Example + | Try type Example @@ -34,9 +37,12 @@ parser : Parser (Route -> a) a parser = Parser.oneOf [ Parser.map Home Parser.top - , Parser.map Try (Parser.s "try") + , Parser.map Docs (Parser.s "docs") + , Parser.map Community (Parser.s "community") + , Parser.map Examples (Parser.s "examples") , Parser.map (Example HelloWorld) (Parser.s "examples" Parser.s "hello") , Parser.map (Example Buttons) (Parser.s "examples" Parser.s "buttons") + , Parser.map Try (Parser.s "try") ] diff --git a/src/Session.elm b/src/Session.elm index d10f2dd..1113726 100644 --- a/src/Session.elm +++ b/src/Session.elm @@ -1,22 +1,37 @@ -module Session exposing - ( Session +port module Session exposing + ( Msg(..) + , Session , init + , isMobileNavigationOpen , navKey + , theme + , update , year ) import Browser.Navigation as Nav +import Components.ThemeToggle as ThemeToggle + + + +-- SESSION type alias Session = { year : Int + , theme : ThemeToggle.Theme + , isMobileNavigationOpen : Bool , navKey : Nav.Key } -init : Int -> Nav.Key -> Session -init = - Session +init : Int -> ThemeToggle.Theme -> Nav.Key -> Session +init year_ theme_ navKey_ = + { year = year_ + , theme = theme_ + , isMobileNavigationOpen = False + , navKey = navKey_ + } year : Session -> Int @@ -24,6 +39,46 @@ year = .year +theme : Session -> ThemeToggle.Theme +theme = + .theme + + navKey : Session -> Nav.Key navKey = .navKey + + +isMobileNavigationOpen : Session -> Bool +isMobileNavigationOpen = + .isMobileNavigationOpen + + + +-- UPDATE + + +type Msg + = SetTheme ThemeToggle.Theme + | ToggleMobileNavigation + | OnResize { breakpoint : Int, width : Int } + + +update : Msg -> Session -> ( Session, Cmd msg ) +update msg session = + case msg of + SetTheme value -> + ( { session | theme = value }, setTheme (ThemeToggle.themeToString value) ) + + ToggleMobileNavigation -> + ( { session | isMobileNavigationOpen = not session.isMobileNavigationOpen }, Cmd.none ) + + OnResize { breakpoint, width } -> + if width >= breakpoint then + ( { session | isMobileNavigationOpen = False }, Cmd.none ) + + else + ( session, Cmd.none ) + + +port setTheme : String -> Cmd msg diff --git a/styles/tailwind.css b/styles/tailwind.css index 8e1595f..5a46bcd 100644 --- a/styles/tailwind.css +++ b/styles/tailwind.css @@ -57,4 +57,8 @@ [inert] ::-webkit-scrollbar { display: none; } +} + +body { + @apply flex min-h-full bg-white antialiased dark:bg-zinc-900; } \ No newline at end of file From feeea57fcd8cb6ae3c45e087f48824e56a27271f Mon Sep 17 00:00:00 2001 From: Decio Ferreira Date: Wed, 8 Oct 2025 23:13:50 +0100 Subject: [PATCH 02/11] WIP updated version --- .gitignore | 1 + elm.json | 1 + public/codemirror/mode/guida.js | 207 ++++++++++ public/codemirror/theme/xq-dark.css | 53 +++ public/codemirror/theme/xq-light.css | 43 ++ public/example-files/animation.guida | 26 ++ public/example-files/book.guida | 89 ++++ public/example-files/buttons.guida | 3 +- public/example-files/cards.guida | 162 ++++++++ public/example-files/clock.guida | 134 ++++++ public/example-files/crate.guida | 227 +++++++++++ public/example-files/cube.guida | 200 +++++++++ public/example-files/drag-and-drop.guida | 140 +++++++ public/example-files/first-person.guida | 377 +++++++++++++++++ public/example-files/forms.guida | 86 ++++ public/example-files/groceries.guida | 22 + public/example-files/hello.guida | 2 +- public/example-files/image-previews.guida | 162 ++++++++ public/example-files/keyboard.guida | 25 ++ public/example-files/mario.guida | 102 +++++ public/example-files/mouse.guida | 30 ++ public/example-files/numbers.guida | 84 ++++ public/example-files/picture.guida | 17 + public/example-files/positions.guida | 96 +++++ public/example-files/quotes.guida | 139 +++++++ public/example-files/shapes.guida | 79 ++++ public/example-files/text-fields.guida | 60 +++ public/example-files/thwomp.guida | 304 ++++++++++++++ public/example-files/time.guida | 95 +++++ public/example-files/triangle.guida | 140 +++++++ public/example-files/turtle.guida | 34 ++ public/example-files/upload.guida | 86 ++++ public/index.html | 1 + src/Components/CodeBlock.elm | 12 + src/Components/Properties.elm | 50 +++ src/Components/References.elm | 23 ++ src/Components/ResourcePattern.elm | 50 +++ src/Layout/Global.elm | 1 - src/Layout/Header.elm | 12 - src/Layout/Main.elm | 107 +++-- src/Main.elm | 23 +- src/Page/Docs.elm | 470 ++++++++++++++++------ src/Page/Home.elm | 243 +++++------ src/Page/Try.elm | 172 ++++++-- src/Route.elm | 220 +++++++++- src/Session.elm | 6 + 46 files changed, 4249 insertions(+), 367 deletions(-) create mode 100644 public/codemirror/mode/guida.js create mode 100644 public/codemirror/theme/xq-dark.css create mode 100644 public/codemirror/theme/xq-light.css create mode 100644 public/example-files/animation.guida create mode 100644 public/example-files/book.guida create mode 100644 public/example-files/cards.guida create mode 100644 public/example-files/clock.guida create mode 100644 public/example-files/crate.guida create mode 100644 public/example-files/cube.guida create mode 100644 public/example-files/drag-and-drop.guida create mode 100644 public/example-files/first-person.guida create mode 100644 public/example-files/forms.guida create mode 100644 public/example-files/groceries.guida create mode 100644 public/example-files/image-previews.guida create mode 100644 public/example-files/keyboard.guida create mode 100644 public/example-files/mario.guida create mode 100644 public/example-files/mouse.guida create mode 100644 public/example-files/numbers.guida create mode 100644 public/example-files/picture.guida create mode 100644 public/example-files/positions.guida create mode 100644 public/example-files/quotes.guida create mode 100644 public/example-files/shapes.guida create mode 100644 public/example-files/text-fields.guida create mode 100644 public/example-files/thwomp.guida create mode 100644 public/example-files/time.guida create mode 100644 public/example-files/triangle.guida create mode 100644 public/example-files/turtle.guida create mode 100644 public/example-files/upload.guida create mode 100644 src/Components/CodeBlock.elm create mode 100644 src/Components/Properties.elm create mode 100644 src/Components/References.elm create mode 100644 src/Components/ResourcePattern.elm diff --git a/.gitignore b/.gitignore index c843b9c..eeba1fe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ node_modules elm-stuff +guida-stuff .DS_Store .idea diff --git a/elm.json b/elm.json index 4af5615..dfd8913 100644 --- a/elm.json +++ b/elm.json @@ -6,6 +6,7 @@ "elm-version": "0.19.1", "dependencies": { "direct": { + "debois/elm-dom": "1.3.0", "elm/browser": "1.0.2", "elm/core": "1.0.5", "elm/html": "1.0.0", diff --git a/public/codemirror/mode/guida.js b/public/codemirror/mode/guida.js new file mode 100644 index 0000000..8d54255 --- /dev/null +++ b/public/codemirror/mode/guida.js @@ -0,0 +1,207 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +(function (mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function (CodeMirror) { + "use strict"; + + CodeMirror.defineMode("guida", function () { + + function switchState(source, setState, f) { + setState(f); + return f(source, setState); + } + + var lowerRE = /[a-z]/; + var upperRE = /[A-Z]/; + var innerRE = /[a-zA-Z0-9_]/; + + var digitRE = /[0-9]/; + var hexRE = /[0-9A-Fa-f]/; + var symbolRE = /[-&*+.\\/<>=?^|:]/; + var specialRE = /[(),[\]{}]/; + var spacesRE = /[ \v\f]/; // newlines are handled in tokenizer + + function normal() { + return function (source, setState) { + if (source.eatWhile(spacesRE)) { + return null; + } + + var char = source.next(); + + if (specialRE.test(char)) { + return (char === '{' && source.eat('-')) + ? switchState(source, setState, chompMultiComment(1)) + : (char === '[' && source.match('glsl|')) + ? switchState(source, setState, chompGlsl) + : 'builtin'; + } + + if (char === '\'') { + return switchState(source, setState, chompChar); + } + + if (char === '"') { + return source.eat('"') + ? source.eat('"') + ? switchState(source, setState, chompMultiString) + : 'string' + : switchState(source, setState, chompSingleString); + } + + if (upperRE.test(char)) { + source.eatWhile(innerRE); + return 'variable-2'; + } + + if (lowerRE.test(char)) { + var isDef = source.pos === 1; + source.eatWhile(innerRE); + return isDef ? "def" : "variable"; + } + + if (digitRE.test(char)) { + if (char === '0') { + if (source.eat(/[xX]/)) { + source.eatWhile(hexRE); // should require at least 1 + return "number"; + } + } + else { + source.eatWhile(digitRE); + } + if (source.eat('.')) { + source.eatWhile(digitRE); // should require at least 1 + } + if (source.eat(/[eE]/)) { + source.eat(/[-+]/); + source.eatWhile(digitRE); // should require at least 1 + } + return "number"; + } + + if (symbolRE.test(char)) { + if (char === '-' && source.eat('-')) { + source.skipToEnd(); + return "comment"; + } + source.eatWhile(symbolRE); + return "keyword"; + } + + if (char === '_') { + return "keyword"; + } + + return "error"; + } + } + + function chompMultiComment(nest) { + if (nest == 0) { + return normal(); + } + return function (source, setState) { + while (!source.eol()) { + var char = source.next(); + if (char == '{' && source.eat('-')) { + ++nest; + } + else if (char == '-' && source.eat('}')) { + --nest; + if (nest === 0) { + setState(normal()); + return 'comment'; + } + } + } + setState(chompMultiComment(nest)); + return 'comment'; + } + } + + function chompMultiString(source, setState) { + while (!source.eol()) { + var char = source.next(); + if (char === '"' && source.eat('"') && source.eat('"')) { + setState(normal()); + return 'string'; + } + } + return 'string'; + } + + function chompSingleString(source, setState) { + while (source.skipTo('\\"')) { source.next(); source.next(); } + if (source.skipTo('"')) { + source.next(); + setState(normal()); + return 'string'; + } + source.skipToEnd(); + setState(normal()); + return 'error'; + } + + function chompChar(source, setState) { + while (source.skipTo("\\'")) { source.next(); source.next(); } + if (source.skipTo("'")) { + source.next(); + setState(normal()); + return 'string'; + } + source.skipToEnd(); + setState(normal()); + return 'error'; + } + + function chompGlsl(source, setState) { + while (!source.eol()) { + var char = source.next(); + if (char === '|' && source.eat(']')) { + setState(normal()); + return 'string'; + } + } + return 'string'; + } + + var wellKnownWords = { + case: 1, + of: 1, + as: 1, + if: 1, + then: 1, + else: 1, + let: 1, + in: 1, + type: 1, + alias: 1, + module: 1, + where: 1, + import: 1, + exposing: 1, + port: 1 + }; + + return { + startState: function () { return { f: normal() }; }, + copyState: function (s) { return { f: s.f }; }, + + token: function (stream, state) { + var type = state.f(stream, function (s) { state.f = s; }); + var word = stream.current(); + return (wellKnownWords.hasOwnProperty(word)) ? 'keyword' : type; + } + }; + }); + + CodeMirror.defineMIME("text/x-guida", "guida"); +}); diff --git a/public/codemirror/theme/xq-dark.css b/public/codemirror/theme/xq-dark.css new file mode 100644 index 0000000..7da1a0f --- /dev/null +++ b/public/codemirror/theme/xq-dark.css @@ -0,0 +1,53 @@ +/* +Copyright (C) 2011 by MarkLogic Corporation +Author: Mike Brevoort + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +.cm-s-xq-dark.CodeMirror { background: #0a001f; color: #f8f8f8; } +.cm-s-xq-dark div.CodeMirror-selected { background: #27007A; } +.cm-s-xq-dark .CodeMirror-line::selection, .cm-s-xq-dark .CodeMirror-line > span::selection, .cm-s-xq-dark .CodeMirror-line > span > span::selection { background: rgba(39, 0, 122, 0.99); } +.cm-s-xq-dark .CodeMirror-line::-moz-selection, .cm-s-xq-dark .CodeMirror-line > span::-moz-selection, .cm-s-xq-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(39, 0, 122, 0.99); } +.cm-s-xq-dark .CodeMirror-gutters { background: #0a001f; border-right: 1px solid #aaa; } +.cm-s-xq-dark .CodeMirror-guttermarker { color: #FFBD40; } +.cm-s-xq-dark .CodeMirror-guttermarker-subtle { color: #f8f8f8; } +.cm-s-xq-dark .CodeMirror-linenumber { color: #f8f8f8; } +.cm-s-xq-dark .CodeMirror-cursor { border-left: 1px solid white; } + +.cm-s-xq-dark span.cm-keyword { color: #FFBD40; } +.cm-s-xq-dark span.cm-atom { color: #6C8CD5; } +.cm-s-xq-dark span.cm-number { color: #164; } +.cm-s-xq-dark span.cm-def { color: #FFF; text-decoration:underline; } +.cm-s-xq-dark span.cm-variable { color: #FFF; } +.cm-s-xq-dark span.cm-variable-2 { color: #EEE; } +.cm-s-xq-dark span.cm-variable-3, .cm-s-xq-dark span.cm-type { color: #DDD; } +.cm-s-xq-dark span.cm-property {} +.cm-s-xq-dark span.cm-operator {} +.cm-s-xq-dark span.cm-comment { color: gray; } +.cm-s-xq-dark span.cm-string { color: #9FEE00; } +.cm-s-xq-dark span.cm-meta { color: yellow; } +.cm-s-xq-dark span.cm-qualifier { color: #FFF700; } +.cm-s-xq-dark span.cm-builtin { color: #30a; } +.cm-s-xq-dark span.cm-bracket { color: #cc7; } +.cm-s-xq-dark span.cm-tag { color: #FFBD40; } +.cm-s-xq-dark span.cm-attribute { color: #FFF700; } +.cm-s-xq-dark span.cm-error { color: #f00; } + +.cm-s-xq-dark .CodeMirror-activeline-background { background: #27282E; } +.cm-s-xq-dark .CodeMirror-matchingbracket { outline:1px solid grey; color:white !important; } diff --git a/public/codemirror/theme/xq-light.css b/public/codemirror/theme/xq-light.css new file mode 100644 index 0000000..7b182ea --- /dev/null +++ b/public/codemirror/theme/xq-light.css @@ -0,0 +1,43 @@ +/* +Copyright (C) 2011 by MarkLogic Corporation +Author: Mike Brevoort + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +.cm-s-xq-light span.cm-keyword { line-height: 1em; font-weight: bold; color: #5A5CAD; } +.cm-s-xq-light span.cm-atom { color: #6C8CD5; } +.cm-s-xq-light span.cm-number { color: #164; } +.cm-s-xq-light span.cm-def { text-decoration:underline; } +.cm-s-xq-light span.cm-variable { color: black; } +.cm-s-xq-light span.cm-variable-2 { color:black; } +.cm-s-xq-light span.cm-variable-3, .cm-s-xq-light span.cm-type { color: black; } +.cm-s-xq-light span.cm-property {} +.cm-s-xq-light span.cm-operator {} +.cm-s-xq-light span.cm-comment { color: #0080FF; font-style: italic; } +.cm-s-xq-light span.cm-string { color: red; } +.cm-s-xq-light span.cm-meta { color: yellow; } +.cm-s-xq-light span.cm-qualifier { color: grey; } +.cm-s-xq-light span.cm-builtin { color: #7EA656; } +.cm-s-xq-light span.cm-bracket { color: #cc7; } +.cm-s-xq-light span.cm-tag { color: #3F7F7F; } +.cm-s-xq-light span.cm-attribute { color: #7F007F; } +.cm-s-xq-light span.cm-error { color: #f00; } + +.cm-s-xq-light .CodeMirror-activeline-background { background: #e8f2ff; } +.cm-s-xq-light .CodeMirror-matchingbracket { outline:1px solid grey;color:black !important;background:yellow; } diff --git a/public/example-files/animation.guida b/public/example-files/animation.guida new file mode 100644 index 0000000..7d50def --- /dev/null +++ b/public/example-files/animation.guida @@ -0,0 +1,26 @@ +module Main exposing (main) + +-- Create animations that spin, wave, and zig-zag. +-- This one is a little red wagon bumping along a dirt road. +-- +-- Learn more about the playground here: +-- https://package.elm-lang.org/packages/evancz/elm-playground/latest/ + +import Playground exposing (..) + + +main = + animation view + + +view time = + [ octagon darkGray 36 + |> moveLeft 100 + |> rotate (spin 3 time) + , octagon darkGray 36 + |> moveRight 100 + |> rotate (spin 3 time) + , rectangle red 300 80 + |> moveUp (wave 50 54 2 time) + |> rotate (zigzag -2 2 8 time) + ] diff --git a/public/example-files/book.guida b/public/example-files/book.guida new file mode 100644 index 0000000..a9b61ce --- /dev/null +++ b/public/example-files/book.guida @@ -0,0 +1,89 @@ +module Main exposing (main) + +-- Make a GET request to load a book called "Public Opinion" +-- +-- Read how it works: +-- https://guide.elm-lang.org/effects/http.html + +import Browser +import Html exposing (Html, pre, text) +import Http + + + +-- MAIN + + +main = + Browser.element + { init = init + , update = update + , subscriptions = subscriptions + , view = view + } + + + +-- MODEL + + +type Model + = Failure + | Loading + | Success String + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( Loading + , Http.get + { url = "https://elm-lang.org/assets/public-opinion.txt" + , expect = Http.expectString GotText + } + ) + + + +-- UPDATE + + +type Msg + = GotText (Result Http.Error String) + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + GotText result -> + case result of + Ok fullText -> + ( Success fullText, Cmd.none ) + + Err _ -> + ( Failure, Cmd.none ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : Model -> Html Msg +view model = + case model of + Failure -> + text "I was unable to load your book." + + Loading -> + text "Loading..." + + Success fullText -> + pre [] [ text fullText ] diff --git a/public/example-files/buttons.guida b/public/example-files/buttons.guida index 0e08693..4da4fde 100644 --- a/public/example-files/buttons.guida +++ b/public/example-files/buttons.guida @@ -1,10 +1,9 @@ -module Main exposing (..) +module Main exposing (main) -- Press buttons to increment and decrement a counter. -- -- Read how it works: -- https://guide.elm-lang.org/architecture/buttons.html --- import Browser import Html exposing (Html, button, div, text) diff --git a/public/example-files/cards.guida b/public/example-files/cards.guida new file mode 100644 index 0000000..513ca7f --- /dev/null +++ b/public/example-files/cards.guida @@ -0,0 +1,162 @@ +module Main exposing (main) + +-- Press a button to draw a random card. +-- +-- Dependencies: +-- guida install elm/random + +import Browser +import Html exposing (..) +import Html.Attributes exposing (style) +import Html.Events exposing (..) +import Random + + + +-- MAIN + + +main = + Browser.element + { init = init + , update = update + , subscriptions = subscriptions + , view = view + } + + + +-- MODEL + + +type alias Model = + { card : Card + } + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( Model Three + , Cmd.none + ) + + +type Card + = Ace + | Two + | Three + | Four + | Five + | Six + | Seven + | Eight + | Nine + | Ten + | Jack + | Queen + | King + + + +-- UPDATE + + +type Msg + = Draw + | NewCard Card + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + Draw -> + ( model + , Random.generate NewCard cardGenerator + ) + + NewCard newCard -> + ( Model newCard + , Cmd.none + ) + + +cardGenerator : Random.Generator Card +cardGenerator = + Random.uniform Ace + [ Two + , Three + , Four + , Five + , Six + , Seven + , Eight + , Nine + , Ten + , Jack + , Queen + , King + ] + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : Model -> Html Msg +view model = + div [] + [ button [ onClick Draw ] [ text "Draw" ] + , div [ style "font-size" "12em" ] [ text (viewCard model.card) ] + ] + + +viewCard : Card -> String +viewCard card = + case card of + Ace -> + "🂡" + + Two -> + "🂢" + + Three -> + "🂣" + + Four -> + "🂤" + + Five -> + "🂥" + + Six -> + "🂦" + + Seven -> + "🂧" + + Eight -> + "🂨" + + Nine -> + "🂩" + + Ten -> + "🂪" + + Jack -> + "🂫" + + Queen -> + "🂭" + + King -> + "🂮" diff --git a/public/example-files/clock.guida b/public/example-files/clock.guida new file mode 100644 index 0000000..5969c34 --- /dev/null +++ b/public/example-files/clock.guida @@ -0,0 +1,134 @@ +module Main exposing (main) + +-- Show an analog clock for your time zone. +-- +-- Dependencies: +-- guida install elm/svg +-- guida install elm/time +-- +-- For a simpler version, check out: +-- https://guida-lang.org/examples/time + +import Browser +import Html exposing (Html) +import Svg exposing (..) +import Svg.Attributes exposing (..) +import Task +import Time + + + +-- MAIN + + +main = + Browser.element + { init = init + , view = view + , update = update + , subscriptions = subscriptions + } + + + +-- MODEL + + +type alias Model = + { zone : Time.Zone + , time : Time.Posix + } + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( Model Time.utc (Time.millisToPosix 0) + , Cmd.batch + [ Task.perform AdjustTimeZone Time.here + , Task.perform Tick Time.now + ] + ) + + + +-- UPDATE + + +type Msg + = Tick Time.Posix + | AdjustTimeZone Time.Zone + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + Tick newTime -> + ( { model | time = newTime } + , Cmd.none + ) + + AdjustTimeZone newZone -> + ( { model | zone = newZone } + , Cmd.none + ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Time.every 1000 Tick + + + +-- VIEW + + +view : Model -> Html Msg +view model = + let + hour = + toFloat (Time.toHour model.zone model.time) + + minute = + toFloat (Time.toMinute model.zone model.time) + + second = + toFloat (Time.toSecond model.zone model.time) + in + svg + [ viewBox "0 0 400 400" + , width "400" + , height "400" + ] + [ circle [ cx "200", cy "200", r "120", fill "#1293D8" ] [] + , viewHand 6 60 (hour / 12) + , viewHand 6 90 (minute / 60) + , viewHand 3 90 (second / 60) + ] + + +viewHand : Int -> Float -> Float -> Svg msg +viewHand width length turns = + let + t = + 2 * pi * (turns - 0.25) + + x = + 200 + length * cos t + + y = + 200 + length * sin t + in + line + [ x1 "200" + , y1 "200" + , x2 (String.fromFloat x) + , y2 (String.fromFloat y) + , stroke "white" + , strokeWidth (String.fromInt width) + , strokeLinecap "round" + ] + [] diff --git a/public/example-files/crate.guida b/public/example-files/crate.guida new file mode 100644 index 0000000..20c70ac --- /dev/null +++ b/public/example-files/crate.guida @@ -0,0 +1,227 @@ +module Main exposing (main) + +-- Demonstrate how to load textures and put them on a cube. +-- +-- Dependencies: +-- guida install elm-explorations/linear-algebra +-- guida install elm-explorations/webgl + +import Browser +import Browser.Events as E +import Html exposing (Html) +import Html.Attributes exposing (height, style, width) +import Math.Matrix4 as Mat4 exposing (Mat4) +import Math.Vector2 as Vec2 exposing (Vec2, vec2) +import Math.Vector3 as Vec3 exposing (Vec3, vec3) +import Result +import Task +import WebGL +import WebGL.Texture as Texture + + + +-- MAIN + + +main = + Browser.element + { init = init + , view = view + , update = \msg model -> ( update msg model, Cmd.none ) + , subscriptions = subscriptions + } + + + +-- MODEL + + +type alias Model = + { angle : Float + , texture : Maybe Texture.Texture + } + + +init : () -> ( Model, Cmd Msg ) +init () = + ( { angle = 0 + , texture = Nothing + } + , Task.attempt GotTexture (Texture.load "https://elm-lang.org/images/wood-crate.jpg") + ) + + + +-- UPDATE + + +type Msg + = TimeDelta Float + | GotTexture (Result Texture.Error Texture.Texture) + + +update : Msg -> Model -> Model +update msg model = + case msg of + TimeDelta dt -> + { model | angle = model.angle + dt / 5000 } + + GotTexture result -> + { model | texture = Result.toMaybe result } + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions _ = + E.onAnimationFrameDelta TimeDelta + + + +-- VIEW + + +view : Model -> Html Msg +view model = + case model.texture of + Nothing -> + Html.text "Loading texture..." + + Just texture -> + WebGL.toHtml + [ width 400 + , height 400 + , style "display" "block" + ] + [ WebGL.entity vertexShader fragmentShader crateMesh (toUniforms model.angle texture) + ] + + + +-- UNIFORMS + + +type alias Uniforms = + { rotation : Mat4 + , perspective : Mat4 + , camera : Mat4 + , texture : Texture.Texture + } + + +toUniforms : Float -> Texture.Texture -> Uniforms +toUniforms angle texture = + { rotation = + Mat4.mul + (Mat4.makeRotate (3 * angle) (vec3 0 1 0)) + (Mat4.makeRotate (2 * angle) (vec3 1 0 0)) + , perspective = perspective + , camera = camera + , texture = texture + } + + +perspective : Mat4 +perspective = + Mat4.makePerspective 45 1 0.01 100 + + +camera : Mat4 +camera = + Mat4.makeLookAt (vec3 0 0 5) (vec3 0 0 0) (vec3 0 1 0) + + + +-- MESH + + +type alias Vertex = + { position : Vec3 + , coord : Vec2 + } + + +crateMesh : WebGL.Mesh Vertex +crateMesh = + WebGL.triangles <| + List.concatMap rotatedSquare <| + [ ( 0, 0 ) + , ( 90, 0 ) + , ( 180, 0 ) + , ( 270, 0 ) + , ( 0, 90 ) + , ( 0, 270 ) + ] + + +rotatedSquare : ( Float, Float ) -> List ( Vertex, Vertex, Vertex ) +rotatedSquare ( angleXZ, angleYZ ) = + let + transformMat = + Mat4.mul + (Mat4.makeRotate (degrees angleXZ) Vec3.j) + (Mat4.makeRotate (degrees angleYZ) Vec3.i) + + transform vertex = + { vertex | position = Mat4.transform transformMat vertex.position } + + transformTriangle ( a, b, c ) = + ( transform a, transform b, transform c ) + in + List.map transformTriangle square + + +square : List ( Vertex, Vertex, Vertex ) +square = + let + topLeft = + Vertex (vec3 -1 1 1) (vec2 0 1) + + topRight = + Vertex (vec3 1 1 1) (vec2 1 1) + + bottomLeft = + Vertex (vec3 -1 -1 1) (vec2 0 0) + + bottomRight = + Vertex (vec3 1 -1 1) (vec2 1 0) + in + [ ( topLeft, topRight, bottomLeft ) + , ( bottomLeft, topRight, bottomRight ) + ] + + + +-- SHADERS + + +vertexShader : WebGL.Shader Vertex Uniforms { vcoord : Vec2 } +vertexShader = + [glsl| + attribute vec3 position; + attribute vec2 coord; + uniform mat4 perspective; + uniform mat4 camera; + uniform mat4 rotation; + varying vec2 vcoord; + + void main () { + gl_Position = perspective * camera * rotation * vec4(position, 1.0); + vcoord = coord; + } + |] + + +fragmentShader : WebGL.Shader {} Uniforms { vcoord : Vec2 } +fragmentShader = + [glsl| + precision mediump float; + uniform sampler2D texture; + varying vec2 vcoord; + + void main () { + gl_FragColor = texture2D(texture, vcoord); + } + |] diff --git a/public/example-files/cube.guida b/public/example-files/cube.guida new file mode 100644 index 0000000..23cc4a7 --- /dev/null +++ b/public/example-files/cube.guida @@ -0,0 +1,200 @@ +module Main exposing (main) + +-- Render a spinning cube. +-- +-- Dependencies: +-- guida install elm-explorations/linear-algebra +-- guida install elm-explorations/webgl + +import Browser +import Browser.Events as E +import Html exposing (Html) +import Html.Attributes exposing (height, style, width) +import Math.Matrix4 as Mat4 exposing (Mat4) +import Math.Vector3 as Vec3 exposing (Vec3, vec3) +import WebGL + + + +-- MAIN + + +main = + Browser.element + { init = init + , view = view + , update = update + , subscriptions = subscriptions + } + + + +-- MODEL + + +type alias Model = + Float + + +init : () -> ( Model, Cmd Msg ) +init () = + ( 0, Cmd.none ) + + + +-- UPDATE + + +type Msg + = TimeDelta Float + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg angle = + case msg of + TimeDelta dt -> + ( angle + dt / 5000, Cmd.none ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions _ = + E.onAnimationFrameDelta TimeDelta + + + +-- VIEW + + +view : Model -> Html Msg +view angle = + WebGL.toHtml + [ width 400 + , height 400 + , style "display" "block" + ] + [ WebGL.entity vertexShader fragmentShader cubeMesh (uniforms angle) + ] + + +type alias Uniforms = + { rotation : Mat4 + , perspective : Mat4 + , camera : Mat4 + } + + +uniforms : Float -> Uniforms +uniforms angle = + { rotation = + Mat4.mul + (Mat4.makeRotate (3 * angle) (vec3 0 1 0)) + (Mat4.makeRotate (2 * angle) (vec3 1 0 0)) + , perspective = Mat4.makePerspective 45 1 0.01 100 + , camera = Mat4.makeLookAt (vec3 0 0 5) (vec3 0 0 0) (vec3 0 1 0) + } + + + +-- MESH + + +type alias Vertex = + { color : Vec3 + , position : Vec3 + } + + +cubeMesh : WebGL.Mesh Vertex +cubeMesh = + let + rft = + vec3 1 1 1 + + lft = + vec3 -1 1 1 + + lbt = + vec3 -1 -1 1 + + rbt = + vec3 1 -1 1 + + rbb = + vec3 1 -1 -1 + + rfb = + vec3 1 1 -1 + + lfb = + vec3 -1 1 -1 + + lbb = + vec3 -1 -1 -1 + in + WebGL.triangles <| + List.concat <| + [ face (vec3 115 210 22) rft rfb rbb rbt + + -- green + , face (vec3 52 101 164) rft rfb lfb lft + + -- blue + , face (vec3 237 212 0) rft lft lbt rbt + + -- yellow + , face (vec3 204 0 0) rfb lfb lbb rbb + + -- red + , face (vec3 117 80 123) lft lfb lbb lbt + + -- purple + , face (vec3 245 121 0) rbt rbb lbb lbt + + -- orange + ] + + +face : Vec3 -> Vec3 -> Vec3 -> Vec3 -> Vec3 -> List ( Vertex, Vertex, Vertex ) +face color a b c d = + let + vertex position = + Vertex (Vec3.scale (1 / 255) color) position + in + [ ( vertex a, vertex b, vertex c ) + , ( vertex c, vertex d, vertex a ) + ] + + + +-- SHADERS + + +vertexShader : WebGL.Shader Vertex Uniforms { vcolor : Vec3 } +vertexShader = + [glsl| + attribute vec3 position; + attribute vec3 color; + uniform mat4 perspective; + uniform mat4 camera; + uniform mat4 rotation; + varying vec3 vcolor; + void main () { + gl_Position = perspective * camera * rotation * vec4(position, 1.0); + vcolor = color; + } + |] + + +fragmentShader : WebGL.Shader {} Uniforms { vcolor : Vec3 } +fragmentShader = + [glsl| + precision mediump float; + varying vec3 vcolor; + void main () { + gl_FragColor = 0.8 * vec4(vcolor, 1.0); + } + |] diff --git a/public/example-files/drag-and-drop.guida b/public/example-files/drag-and-drop.guida new file mode 100644 index 0000000..adc5118 --- /dev/null +++ b/public/example-files/drag-and-drop.guida @@ -0,0 +1,140 @@ +module Main exposing (main) + +-- Image upload with a drag and drop zone. +-- +-- Dependencies: +-- guida install elm/file +-- guida install elm/json + +import Browser +import File exposing (File) +import File.Select as Select +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Json.Decode as D + + + +-- MAIN + + +main = + Browser.element + { init = init + , view = view + , update = update + , subscriptions = subscriptions + } + + + +-- MODEL + + +type alias Model = + { hover : Bool + , files : List File + } + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( Model False [], Cmd.none ) + + + +-- UPDATE + + +type Msg + = Pick + | DragEnter + | DragLeave + | GotFiles File (List File) + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + Pick -> + ( model + , Select.files [ "image/*" ] GotFiles + ) + + DragEnter -> + ( { model | hover = True } + , Cmd.none + ) + + DragLeave -> + ( { model | hover = False } + , Cmd.none + ) + + GotFiles file files -> + ( { model + | files = + file :: files + , hover = + False + } + , Cmd.none + ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : Model -> Html Msg +view model = + div + [ style "border" + (if model.hover then + "6px dashed purple" + + else + "6px dashed #ccc" + ) + , style "border-radius" "20px" + , style "width" "480px" + , style "height" "100px" + , style "margin" "100px auto" + , style "padding" "20px" + , style "display" "flex" + , style "flex-direction" "column" + , style "justify-content" "center" + , style "align-items" "center" + , hijackOn "dragenter" (D.succeed DragEnter) + , hijackOn "dragover" (D.succeed DragEnter) + , hijackOn "dragleave" (D.succeed DragLeave) + , hijackOn "drop" dropDecoder + ] + [ button [ onClick Pick ] [ text "Upload Images" ] + , span [ style "color" "#ccc" ] [ text (Debug.toString model) ] + ] + + +dropDecoder : D.Decoder Msg +dropDecoder = + D.at [ "dataTransfer", "files" ] (D.oneOrMore GotFiles File.decoder) + + +hijackOn : String -> D.Decoder msg -> Attribute msg +hijackOn event decoder = + preventDefaultOn event (D.map hijack decoder) + + +hijack : msg -> ( msg, Bool ) +hijack msg = + ( msg, True ) diff --git a/public/example-files/first-person.guida b/public/example-files/first-person.guida new file mode 100644 index 0000000..03a5e69 --- /dev/null +++ b/public/example-files/first-person.guida @@ -0,0 +1,377 @@ +module Main exposing (main) + +-- Walk around in 3D space using the keyboard. +-- +-- Dependencies: +-- guida install elm-explorations/linear-algebra +-- guida install elm-explorations/webgl +-- +-- Try adding the ability to crouch or to land on top of the crate! + +import Browser +import Browser.Dom as Dom +import Browser.Events as E +import Html exposing (Html, div, p, text) +import Html.Attributes exposing (height, style, width) +import Json.Decode as D +import Math.Matrix4 as Mat4 exposing (Mat4) +import Math.Vector2 as Vec2 exposing (Vec2, vec2) +import Math.Vector3 as Vec3 exposing (Vec3, vec3) +import Task +import WebGL +import WebGL.Texture as Texture + + + +-- MAIN + + +main : Program () Model Msg +main = + Browser.element + { init = init + , view = view + , update = \msg model -> ( update msg model, Cmd.none ) + , subscriptions = subscriptions + } + + + +-- MODEL + + +type alias Model = + { keys : Keys + , width : Float + , height : Float + , person : Person + , texture : Maybe Texture.Texture + } + + +type alias Keys = + { up : Bool + , left : Bool + , down : Bool + , right : Bool + , space : Bool + } + + +type alias Person = + { position : Vec3 + , velocity : Vec3 + } + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( { keys = noKeys + , width = 400 + , height = 400 + , person = Person (vec3 0 eyeLevel -10) (vec3 0 0 0) + , texture = Nothing + } + , Cmd.batch + [ Task.attempt GotTexture (Texture.load "https://elm-lang.org/images/wood-crate.jpg") + , Task.perform (\{ viewport } -> Resized viewport.width viewport.height) Dom.getViewport + ] + ) + + +eyeLevel : Float +eyeLevel = + 2 + + +noKeys : Keys +noKeys = + Keys False False False False False + + + +-- UPDATE + + +type Msg + = GotTexture (Result Texture.Error Texture.Texture) + | KeyChanged Bool String + | TimeDelta Float + | Resized Float Float + | VisibilityChanged E.Visibility + + +update : Msg -> Model -> Model +update msg model = + case msg of + GotTexture result -> + { model | texture = Result.toMaybe result } + + KeyChanged isDown key -> + { model | keys = updateKeys isDown key model.keys } + + TimeDelta dt -> + { model | person = updatePerson dt model.keys model.person } + + Resized width height -> + { model + | width = + width + , height = + height + } + + VisibilityChanged _ -> + { model | keys = noKeys } + + +updateKeys : Bool -> String -> Keys -> Keys +updateKeys isDown key keys = + case key of + " " -> + { keys | space = isDown } + + "ArrowUp" -> + { keys | up = isDown } + + "ArrowLeft" -> + { keys | left = isDown } + + "ArrowDown" -> + { keys | down = isDown } + + "ArrowRight" -> + { keys | right = isDown } + + _ -> + keys + + +updatePerson : Float -> Keys -> Person -> Person +updatePerson dt keys person = + let + velocity = + stepVelocity dt keys person + + position = + Vec3.add person.position (Vec3.scale (dt / 500) velocity) + in + if Vec3.getY position < eyeLevel then + { position = Vec3.setY eyeLevel position + , velocity = Vec3.setY 0 velocity + } + + else + { position = position + , velocity = velocity + } + + +stepVelocity : Float -> Keys -> Person -> Vec3 +stepVelocity dt { left, right, up, down, space } person = + if Vec3.getY person.position > eyeLevel then + Vec3.setY (Vec3.getY person.velocity - dt / 250) person.velocity + + else + let + toV positive negative = + (if positive then + 1 + + else + 0 + ) + - (if negative then + 1 + + else + 0 + ) + in + vec3 (toV left right) + (if space then + 2 + + else + 0 + ) + (toV up down) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.batch + [ E.onResize (\w h -> Resized (toFloat w) (toFloat h)) + , E.onKeyUp (D.map (KeyChanged False) (D.field "key" D.string)) + , E.onKeyDown (D.map (KeyChanged True) (D.field "key" D.string)) + , E.onAnimationFrameDelta TimeDelta + , E.onVisibilityChange VisibilityChanged + ] + + + +-- VIEW + + +view : Model -> Html Msg +view model = + let + entities = + case model.texture of + Nothing -> + [] + + Just texture -> + [ viewCrate model.width model.height model.person texture ] + in + div + [ style "position" "absolute" + , style "left" "0" + , style "top" "0" + , style "width" (String.fromFloat model.width ++ "px") + , style "height" (String.fromFloat model.height ++ "px") + ] + [ WebGL.toHtmlWith [ WebGL.depth 1, WebGL.clearColor 1 1 1 1 ] + [ style "display" "block" + , width (round model.width) + , height (round model.height) + ] + entities + , keyboardInstructions model.keys + ] + + +viewCrate : Float -> Float -> Person -> Texture.Texture -> WebGL.Entity +viewCrate width height person texture = + let + perspective = + Mat4.mul + (Mat4.makePerspective 45 (width / height) 0.01 100) + (Mat4.makeLookAt person.position (Vec3.add person.position Vec3.k) Vec3.j) + in + WebGL.entity vertexShader + fragmentShader + crate + { texture = texture + , perspective = perspective + } + + +keyboardInstructions : Keys -> Html msg +keyboardInstructions keys = + div + [ style "position" "absolute" + , style "font-family" "monospace" + , style "text-align" "center" + , style "left" "20px" + , style "right" "20px" + , style "top" "20px" + ] + [ p [] [ text "Walk around with a first person perspective." ] + , p [] [ text "Arrows keys to move, space bar to jump." ] + ] + + + +-- MESH + + +type alias Vertex = + { position : Vec3 + , coord : Vec2 + } + + +crate : WebGL.Mesh Vertex +crate = + WebGL.triangles <| + List.concatMap rotatedSquare <| + [ ( 0, 0 ) + , ( 90, 0 ) + , ( 180, 0 ) + , ( 270, 0 ) + , ( 0, 90 ) + , ( 0, -90 ) + ] + + +rotatedSquare : ( Float, Float ) -> List ( Vertex, Vertex, Vertex ) +rotatedSquare ( angleXZ, angleYZ ) = + let + transformMat = + Mat4.mul + (Mat4.makeRotate (degrees angleXZ) Vec3.j) + (Mat4.makeRotate (degrees angleYZ) Vec3.i) + + transform vertex = + { vertex + | position = + Mat4.transform transformMat vertex.position + } + + transformTriangle ( a, b, c ) = + ( transform a, transform b, transform c ) + in + List.map transformTriangle square + + +square : List ( Vertex, Vertex, Vertex ) +square = + let + topLeft = + Vertex (vec3 -1 1 1) (vec2 0 1) + + topRight = + Vertex (vec3 1 1 1) (vec2 1 1) + + bottomLeft = + Vertex (vec3 -1 -1 1) (vec2 0 0) + + bottomRight = + Vertex (vec3 1 -1 1) (vec2 1 0) + in + [ ( topLeft, topRight, bottomLeft ) + , ( bottomLeft, topRight, bottomRight ) + ] + + + +-- SHADERS + + +type alias Uniforms = + { texture : Texture.Texture + , perspective : Mat4 + } + + +vertexShader : WebGL.Shader Vertex Uniforms { vcoord : Vec2 } +vertexShader = + [glsl| + attribute vec3 position; + attribute vec2 coord; + uniform mat4 perspective; + varying vec2 vcoord; + + void main () { + gl_Position = perspective * vec4(position, 1.0); + vcoord = coord; + } + |] + + +fragmentShader : WebGL.Shader {} Uniforms { vcoord : Vec2 } +fragmentShader = + [glsl| + precision mediump float; + uniform sampler2D texture; + varying vec2 vcoord; + + void main () { + gl_FragColor = texture2D(texture, vcoord); + } + |] diff --git a/public/example-files/forms.guida b/public/example-files/forms.guida new file mode 100644 index 0000000..b135513 --- /dev/null +++ b/public/example-files/forms.guida @@ -0,0 +1,86 @@ +module Main exposing (main) + +-- Input a user name and password. Make sure the password matches. +-- +-- Read how it works: +-- https://guide.elm-lang.org/architecture/forms.html + +import Browser +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (onInput) + + + +-- MAIN + + +main = + Browser.sandbox { init = init, update = update, view = view } + + + +-- MODEL + + +type alias Model = + { name : String + , password : String + , passwordAgain : String + } + + +init : Model +init = + Model "" "" "" + + + +-- UPDATE + + +type Msg + = Name String + | Password String + | PasswordAgain String + + +update : Msg -> Model -> Model +update msg model = + case msg of + Name name -> + { model | name = name } + + Password password -> + { model | password = password } + + PasswordAgain password -> + { model | passwordAgain = password } + + + +-- VIEW + + +view : Model -> Html Msg +view model = + div [] + [ viewInput "text" "Name" model.name Name + , viewInput "password" "Password" model.password Password + , viewInput "password" "Re-enter Password" model.passwordAgain PasswordAgain + , viewValidation model + ] + + +viewInput : String -> String -> String -> (String -> msg) -> Html msg +viewInput t p v toMsg = + input [ type_ t, placeholder p, value v, onInput toMsg ] [] + + +viewValidation : Model -> Html msg +viewValidation model = + if model.password == model.passwordAgain then + div [ style "color" "green" ] [ text "OK" ] + + else + div [ style "color" "red" ] [ text "Passwords do not match!" ] diff --git a/public/example-files/groceries.guida b/public/example-files/groceries.guida new file mode 100644 index 0000000..9624a71 --- /dev/null +++ b/public/example-files/groceries.guida @@ -0,0 +1,22 @@ +module Main exposing (main) + +-- Show a list of items I need to buy at the grocery store. + +import Html exposing (..) + + +main = + div [] + [ h1 [] [ text "My Grocery List" ] + , ul [] + [ li [] [ text "Black Beans" ] + , li [] [ text "Limes" ] + , li [] [ text "Greek Yogurt" ] + , li [] [ text "Cilantro" ] + , li [] [ text "Honey" ] + , li [] [ text "Sweet Potatoes" ] + , li [] [ text "Cumin" ] + , li [] [ text "Chili Powder" ] + , li [] [ text "Quinoa" ] + ] + ] diff --git a/public/example-files/hello.guida b/public/example-files/hello.guida index 0eb0eea..e7b9c6a 100644 --- a/public/example-files/hello.guida +++ b/public/example-files/hello.guida @@ -1,4 +1,4 @@ -module Main exposing (..) +module Main exposing (main) import Html exposing (text) diff --git a/public/example-files/image-previews.guida b/public/example-files/image-previews.guida new file mode 100644 index 0000000..6087161 --- /dev/null +++ b/public/example-files/image-previews.guida @@ -0,0 +1,162 @@ +module Main exposing (main) + +-- Image upload with a drag and drop zone. See image previews! +-- +-- Dependencies: +-- guida install elm/file +-- guida install elm/json + +import Browser +import File exposing (File) +import File.Select as Select +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Json.Decode as D +import Task + + + +-- MAIN + + +main = + Browser.element + { init = init + , view = view + , update = update + , subscriptions = subscriptions + } + + + +-- MODEL + + +type alias Model = + { hover : Bool + , previews : List String + } + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( Model False [], Cmd.none ) + + + +-- UPDATE + + +type Msg + = Pick + | DragEnter + | DragLeave + | GotFiles File (List File) + | GotPreviews (List String) + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + Pick -> + ( model + , Select.files [ "image/*" ] GotFiles + ) + + DragEnter -> + ( { model | hover = True } + , Cmd.none + ) + + DragLeave -> + ( { model | hover = False } + , Cmd.none + ) + + GotFiles file files -> + ( { model | hover = False } + , Task.perform GotPreviews <| + Task.sequence <| + List.map File.toUrl (file :: files) + ) + + GotPreviews urls -> + ( { model | previews = urls } + , Cmd.none + ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : Model -> Html Msg +view model = + div + [ style "border" + (if model.hover then + "6px dashed purple" + + else + "6px dashed #ccc" + ) + , style "border-radius" "20px" + , style "width" "480px" + , style "margin" "100px auto" + , style "padding" "40px" + , style "display" "flex" + , style "flex-direction" "column" + , style "justify-content" "center" + , style "align-items" "center" + , hijackOn "dragenter" (D.succeed DragEnter) + , hijackOn "dragover" (D.succeed DragEnter) + , hijackOn "dragleave" (D.succeed DragLeave) + , hijackOn "drop" dropDecoder + ] + [ button [ onClick Pick ] [ text "Upload Images" ] + , div + [ style "display" "flex" + , style "align-items" "center" + , style "height" "60px" + , style "padding" "20px" + ] + (List.map viewPreview model.previews) + ] + + +viewPreview : String -> Html msg +viewPreview url = + div + [ style "width" "60px" + , style "height" "60px" + , style "background-image" ("url('" ++ url ++ "')") + , style "background-position" "center" + , style "background-repeat" "no-repeat" + , style "background-size" "contain" + ] + [] + + +dropDecoder : D.Decoder Msg +dropDecoder = + D.at [ "dataTransfer", "files" ] (D.oneOrMore GotFiles File.decoder) + + +hijackOn : String -> D.Decoder msg -> Attribute msg +hijackOn event decoder = + preventDefaultOn event (D.map hijack decoder) + + +hijack : msg -> ( msg, Bool ) +hijack msg = + ( msg, True ) diff --git a/public/example-files/keyboard.guida b/public/example-files/keyboard.guida new file mode 100644 index 0000000..bd059ee --- /dev/null +++ b/public/example-files/keyboard.guida @@ -0,0 +1,25 @@ +module Main exposing (main) + +-- Move a square around with the arrow keys: UP, DOWN, LEFT, RIGHT +-- Try making it move around more quickly! +-- +-- Learn more about the playground here: +-- https://package.elm-lang.org/packages/evancz/elm-playground/latest/ + +import Playground exposing (..) + + +main = + game view update ( 0, 0 ) + + +view computer ( x, y ) = + [ square blue 40 + |> move x y + ] + + +update computer ( x, y ) = + ( x + toX computer.keyboard + , y + toY computer.keyboard + ) diff --git a/public/example-files/mario.guida b/public/example-files/mario.guida new file mode 100644 index 0000000..979f56c --- /dev/null +++ b/public/example-files/mario.guida @@ -0,0 +1,102 @@ +module Main exposing (main) + +-- Walk around with the arrow keys. Press the UP arrow to jump! +-- +-- Learn more about the playground here: +-- https://package.elm-lang.org/packages/evancz/elm-playground/latest/ + +import Playground exposing (..) + + + +-- MAIN + + +main = + game view + update + { x = 0 + , y = 0 + , vx = 0 + , vy = 0 + , dir = "right" + } + + + +-- VIEW + + +view computer mario = + let + w = + computer.screen.width + + h = + computer.screen.height + + b = + computer.screen.bottom + in + [ rectangle (rgb 174 238 238) w h + , rectangle (rgb 74 163 41) w 100 + |> moveY b + , image 70 70 (toGif mario) + |> move mario.x (b + 76 + mario.y) + ] + + +toGif mario = + if mario.y > 0 then + "https://elm-lang.org/images/mario/jump/" ++ mario.dir ++ ".gif" + + else if mario.vx /= 0 then + "https://elm-lang.org/images/mario/walk/" ++ mario.dir ++ ".gif" + + else + "https://elm-lang.org/images/mario/stand/" ++ mario.dir ++ ".gif" + + + +-- UPDATE + + +update computer mario = + let + dt = + 1.666 + + vx = + toX computer.keyboard + + vy = + if mario.y == 0 then + if computer.keyboard.up then + 5 + + else + 0 + + else + mario.vy - dt / 8 + + x = + mario.x + dt * vx + + y = + mario.y + dt * vy + in + { x = x + , y = max 0 y + , vx = vx + , vy = vy + , dir = + if vx == 0 then + mario.dir + + else if vx < 0 then + "left" + + else + "right" + } diff --git a/public/example-files/mouse.guida b/public/example-files/mouse.guida new file mode 100644 index 0000000..24e9239 --- /dev/null +++ b/public/example-files/mouse.guida @@ -0,0 +1,30 @@ +module Main exposing (main) + +-- Draw a cicle around the mouse. Change its color by pressing down. +-- +-- Learn more about the playground here: +-- https://package.elm-lang.org/packages/evancz/elm-playground/latest/ + +import Playground exposing (..) + + +main = + game view update () + + +view computer memory = + [ circle lightPurple 30 + |> moveX computer.mouse.x + |> moveY computer.mouse.y + |> fade + (if computer.mouse.down then + 0.2 + + else + 1 + ) + ] + + +update computer memory = + memory diff --git a/public/example-files/numbers.guida b/public/example-files/numbers.guida new file mode 100644 index 0000000..af58601 --- /dev/null +++ b/public/example-files/numbers.guida @@ -0,0 +1,84 @@ +module Main exposing (main) + +-- Press a button to generate a random number between 1 and 6. +-- +-- Read how it works: +-- https://guide.elm-lang.org/effects/random.html + +import Browser +import Html exposing (..) +import Html.Events exposing (..) +import Random + + + +-- MAIN + + +main = + Browser.element + { init = init + , update = update + , subscriptions = subscriptions + , view = view + } + + + +-- MODEL + + +type alias Model = + { dieFace : Int + } + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( Model 1 + , Cmd.none + ) + + + +-- UPDATE + + +type Msg + = Roll + | NewFace Int + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + Roll -> + ( model + , Random.generate NewFace (Random.int 1 6) + ) + + NewFace newFace -> + ( Model newFace + , Cmd.none + ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : Model -> Html Msg +view model = + div [] + [ h1 [] [ text (String.fromInt model.dieFace) ] + , button [ onClick Roll ] [ text "Roll" ] + ] diff --git a/public/example-files/picture.guida b/public/example-files/picture.guida new file mode 100644 index 0000000..1841450 --- /dev/null +++ b/public/example-files/picture.guida @@ -0,0 +1,17 @@ +module Main exposing (main) + +-- Create pictures from simple shapes. Like a tree! +-- +-- Learn more about the playground here: +-- https://package.elm-lang.org/packages/evancz/elm-playground/latest/ + +import Playground exposing (..) + + +main = + picture + [ rectangle brown 40 200 + |> moveDown 80 + , circle green 100 + |> moveUp 100 + ] diff --git a/public/example-files/positions.guida b/public/example-files/positions.guida new file mode 100644 index 0000000..cec24a9 --- /dev/null +++ b/public/example-files/positions.guida @@ -0,0 +1,96 @@ +module Main exposing (main) + +-- A button that moves to random positions when pressed. +-- +-- Dependencies: +-- guida install elm/random + +import Browser +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Random + + + +-- MAIN + + +main = + Browser.element + { init = init + , update = update + , subscriptions = subscriptions + , view = view + } + + + +-- MODEL + + +type alias Model = + { x : Int + , y : Int + } + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( Model 100 100 + , Cmd.none + ) + + + +-- UPDATE + + +type Msg + = Clicked + | NewPosition ( Int, Int ) + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + Clicked -> + ( model + , Random.generate NewPosition positionGenerator + ) + + NewPosition ( x, y ) -> + ( Model x y + , Cmd.none + ) + + +positionGenerator : Random.Generator ( Int, Int ) +positionGenerator = + Random.map2 Tuple.pair + (Random.int 50 350) + (Random.int 50 350) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : Model -> Html Msg +view model = + button + [ style "position" "absolute" + , style "top" (String.fromInt model.x ++ "px") + , style "left" (String.fromInt model.y ++ "px") + , onClick Clicked + ] + [ text "Click me!" ] diff --git a/public/example-files/quotes.guida b/public/example-files/quotes.guida new file mode 100644 index 0000000..cbb09b7 --- /dev/null +++ b/public/example-files/quotes.guida @@ -0,0 +1,139 @@ +module Main exposing (main) + +-- Press a button to send a GET request for random quotes. +-- +-- Read how it works: +-- https://guide.elm-lang.org/effects/json.html + +import Browser +import Html exposing (..) +import Html.Attributes exposing (style) +import Html.Events exposing (..) +import Http +import Json.Decode exposing (Decoder, field, int, map4, string) + + + +-- MAIN + + +main = + Browser.element + { init = init + , update = update + , subscriptions = subscriptions + , view = view + } + + + +-- MODEL + + +type Model + = Failure + | Loading + | Success Quote + + +type alias Quote = + { quote : String + , source : String + , author : String + , year : Int + } + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( Loading, getRandomQuote ) + + + +-- UPDATE + + +type Msg + = MorePlease + | GotQuote (Result Http.Error Quote) + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + MorePlease -> + ( Loading, getRandomQuote ) + + GotQuote result -> + case result of + Ok quote -> + ( Success quote, Cmd.none ) + + Err _ -> + ( Failure, Cmd.none ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : Model -> Html Msg +view model = + div [] + [ h2 [] [ text "Random Quotes" ] + , viewQuote model + ] + + +viewQuote : Model -> Html Msg +viewQuote model = + case model of + Failure -> + div [] + [ text "I could not load a random quote for some reason. " + , button [ onClick MorePlease ] [ text "Try Again!" ] + ] + + Loading -> + text "Loading..." + + Success quote -> + div [] + [ button [ onClick MorePlease, style "display" "block" ] [ text "More Please!" ] + , blockquote [] [ text quote.quote ] + , p [ style "text-align" "right" ] + [ text "— " + , cite [] [ text quote.source ] + , text (" by " ++ quote.author ++ " (" ++ String.fromInt quote.year ++ ")") + ] + ] + + + +-- HTTP + + +getRandomQuote : Cmd Msg +getRandomQuote = + Http.get + { url = "https://elm-lang.org/api/random-quotes" + , expect = Http.expectJson GotQuote quoteDecoder + } + + +quoteDecoder : Decoder Quote +quoteDecoder = + map4 Quote + (field "quote" string) + (field "source" string) + (field "author" string) + (field "year" int) diff --git a/public/example-files/shapes.guida b/public/example-files/shapes.guida new file mode 100644 index 0000000..2fc2ca1 --- /dev/null +++ b/public/example-files/shapes.guida @@ -0,0 +1,79 @@ +module Main exposing (main) + +-- Scalable Vector Graphics (SVG) can be a nice way to draw things in 2D. +-- Here are some common SVG shapes. +-- +-- Dependencies: +-- guida install elm/svg + +import Html exposing (Html) +import Svg exposing (..) +import Svg.Attributes exposing (..) + + +main : Html msg +main = + svg + [ viewBox "0 0 400 400" + , width "400" + , height "400" + ] + [ circle + [ cx "50" + , cy "50" + , r "40" + , fill "red" + , stroke "black" + , strokeWidth "3" + ] + [] + , rect + [ x "100" + , y "10" + , width "40" + , height "40" + , fill "green" + , stroke "black" + , strokeWidth "2" + ] + [] + , line + [ x1 "20" + , y1 "200" + , x2 "200" + , y2 "20" + , stroke "blue" + , strokeWidth "10" + , strokeLinecap "round" + ] + [] + , polyline + [ points "200,40 240,40 240,80 280,80 280,120 320,120 320,160" + , fill "none" + , stroke "red" + , strokeWidth "4" + , strokeDasharray "20,2" + ] + [] + , text_ + [ x "130" + , y "130" + , fill "black" + , textAnchor "middle" + , dominantBaseline "central" + , transform "rotate(-45 130,130)" + ] + [ text "Welcome to Shapes Club" + ] + ] + + + +-- There are a lot of odd things about SVG, so always try to find examples +-- to help you understand the weird stuff. Like these: +-- +-- https://www.w3schools.com/graphics/svg_examples.asp +-- https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d +-- +-- If you cannot find relevant examples, make an experiment. If you push +-- through the weirdness, you can do a lot with SVG. diff --git a/public/example-files/text-fields.guida b/public/example-files/text-fields.guida new file mode 100644 index 0000000..9d75ea8 --- /dev/null +++ b/public/example-files/text-fields.guida @@ -0,0 +1,60 @@ +module Main exposing (main) + +-- A text input for reversing text. Very useful! +-- +-- Read how it works: +-- https://guide.elm-lang.org/architecture/text_fields.html + +import Browser +import Html exposing (Attribute, Html, div, input, text) +import Html.Attributes exposing (..) +import Html.Events exposing (onInput) + + + +-- MAIN + + +main = + Browser.sandbox { init = init, update = update, view = view } + + + +-- MODEL + + +type alias Model = + { content : String + } + + +init : Model +init = + { content = "" } + + + +-- UPDATE + + +type Msg + = Change String + + +update : Msg -> Model -> Model +update msg model = + case msg of + Change newContent -> + { model | content = newContent } + + + +-- VIEW + + +view : Model -> Html Msg +view model = + div [] + [ input [ placeholder "Text to reverse", value model.content, onInput Change ] [] + , div [] [ text (String.reverse model.content) ] + ] diff --git a/public/example-files/thwomp.guida b/public/example-files/thwomp.guida new file mode 100644 index 0000000..2701b44 --- /dev/null +++ b/public/example-files/thwomp.guida @@ -0,0 +1,304 @@ +module Main exposing (main) + +-- Thwomp looks at your mouse. What is it up to? +-- +-- Dependencies: +-- guida install elm/json +-- guida install elm-explorations/linear-algebra +-- guida install elm-explorations/webgl +-- +-- Thanks to The PaperNES Guy for the texture: +-- https://the-papernes-guy.deviantart.com/art/Thwomps-Thwomps-Thwomps-186879685 + +import Browser +import Browser.Dom as Dom +import Browser.Events as E +import Html exposing (Html) +import Html.Attributes exposing (height, style, width) +import Json.Decode as D +import Math.Matrix4 as Mat4 exposing (Mat4) +import Math.Vector2 as Vec2 exposing (Vec2, vec2) +import Math.Vector3 as Vec3 exposing (Vec3, vec3) +import Result +import Task +import WebGL +import WebGL.Texture as Texture + + + +-- MAIN + + +main : Program () Model Msg +main = + Browser.element + { init = init + , view = view + , update = \msg model -> ( update msg model, Cmd.none ) + , subscriptions = subscriptions + } + + + +-- MODEL + + +type alias Model = + { width : Float + , height : Float + , x : Float + , y : Float + , side : Maybe Texture.Texture + , face : Maybe Texture.Texture + } + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( { width = 0 + , height = 0 + , x = 0 + , y = 0 + , face = Nothing + , side = Nothing + } + , Cmd.batch + [ Task.perform GotViewport Dom.getViewport + , Task.attempt GotFace (Texture.loadWith options "https://elm-lang.org/images/thwomp-face.jpg") + , Task.attempt GotSide (Texture.loadWith options "https://elm-lang.org/images/thwomp-side.jpg") + ] + ) + + +options : Texture.Options +options = + { magnify = Texture.nearest + , minify = Texture.nearest + , horizontalWrap = Texture.repeat + , verticalWrap = Texture.repeat + , flipY = True + } + + + +-- UPDATE + + +type Msg + = GotFace (Result Texture.Error Texture.Texture) + | GotSide (Result Texture.Error Texture.Texture) + | GotViewport Dom.Viewport + | Resized Int Int + | MouseMoved Float Float + + +update : Msg -> Model -> Model +update msg model = + case msg of + GotFace result -> + { model + | face = + Result.toMaybe result + } + + GotSide result -> + { model + | side = + Result.toMaybe result + } + + GotViewport { viewport } -> + { model + | width = + viewport.width + , height = + viewport.height + } + + Resized width height -> + { model + | width = + toFloat width + , height = + toFloat height + } + + MouseMoved x y -> + { model + | x = + x + , y = + y + } + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions _ = + Sub.batch + [ E.onResize Resized + , E.onMouseMove decodeMovement + ] + + +decodeMovement : D.Decoder Msg +decodeMovement = + D.map2 MouseMoved + (D.field "pageX" D.float) + (D.field "pageY" D.float) + + + +-- VIEW + + +view : Model -> Html Msg +view model = + case Maybe.map2 Tuple.pair model.face model.side of + Nothing -> + Html.text "Loading textures..." + + Just ( face, side ) -> + let + perspective = + toPerspective model.x model.y model.width model.height + in + WebGL.toHtml + [ style "display" "block" + , style "position" "absolute" + , style "left" "0" + , style "top" "0" + , width (round model.width) + , height (round model.height) + ] + [ WebGL.entity vertexShader + fragmentShader + faceMesh + { perspective = perspective + , texture = face + } + , WebGL.entity vertexShader + fragmentShader + sidesMesh + { perspective = perspective + , texture = side + } + ] + + +toPerspective : Float -> Float -> Float -> Float -> Mat4 +toPerspective x y width height = + let + eye = + Vec3.scale 6 <| + Vec3.normalize <| + vec3 (0.5 - x / width) (y / height - 0.5) 1 + in + Mat4.mul + (Mat4.makePerspective 45 (width / height) 0.01 100) + (Mat4.makeLookAt eye (vec3 0 0 0) Vec3.j) + + + +-- MESHES + + +type alias Vertex = + { position : Vec3 + , coord : Vec2 + } + + +faceMesh : WebGL.Mesh Vertex +faceMesh = + WebGL.triangles square + + +sidesMesh : WebGL.Mesh Vertex +sidesMesh = + WebGL.triangles <| + List.concatMap rotatedSquare <| + [ ( 90, 0 ) + , ( 180, 0 ) + , ( 270, 0 ) + , ( 0, 90 ) + , ( 0, 270 ) + ] + + +rotatedSquare : ( Float, Float ) -> List ( Vertex, Vertex, Vertex ) +rotatedSquare ( angleXZ, angleYZ ) = + let + transformMat = + Mat4.mul + (Mat4.makeRotate (degrees angleXZ) Vec3.j) + (Mat4.makeRotate (degrees angleYZ) Vec3.i) + + transform vertex = + { vertex | position = Mat4.transform transformMat vertex.position } + + transformTriangle ( a, b, c ) = + ( transform a, transform b, transform c ) + in + List.map transformTriangle square + + +square : List ( Vertex, Vertex, Vertex ) +square = + let + topLeft = + Vertex (vec3 -1 1 1) (vec2 0 1) + + topRight = + Vertex (vec3 1 1 1) (vec2 1 1) + + bottomLeft = + Vertex (vec3 -1 -1 1) (vec2 0 0) + + bottomRight = + Vertex (vec3 1 -1 1) (vec2 1 0) + in + [ ( topLeft, topRight, bottomLeft ) + , ( bottomLeft, topRight, bottomRight ) + ] + + + +-- SHADERS + + +type alias Uniforms = + { perspective : Mat4 + , texture : Texture.Texture + } + + +vertexShader : WebGL.Shader Vertex Uniforms { vcoord : Vec2 } +vertexShader = + [glsl| + attribute vec3 position; + attribute vec2 coord; + uniform mat4 perspective; + varying vec2 vcoord; + + void main () { + gl_Position = perspective * vec4(position, 1.0); + vcoord = coord.xy; + } + |] + + +fragmentShader : WebGL.Shader {} Uniforms { vcoord : Vec2 } +fragmentShader = + [glsl| + precision mediump float; + uniform sampler2D texture; + varying vec2 vcoord; + + void main () { + gl_FragColor = texture2D(texture, vcoord); + } + |] diff --git a/public/example-files/time.guida b/public/example-files/time.guida new file mode 100644 index 0000000..dc17c36 --- /dev/null +++ b/public/example-files/time.guida @@ -0,0 +1,95 @@ +module Main exposing (main) + +-- Show the current time in your time zone. +-- +-- Read how it works: +-- https://guide.elm-lang.org/effects/time.html +-- +-- For an analog clock, check out this SVG example: +-- https://elm-lang.org/examples/clock + +import Browser +import Html exposing (..) +import Task +import Time + + + +-- MAIN + + +main = + Browser.element + { init = init + , view = view + , update = update + , subscriptions = subscriptions + } + + + +-- MODEL + + +type alias Model = + { zone : Time.Zone + , time : Time.Posix + } + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( Model Time.utc (Time.millisToPosix 0) + , Task.perform AdjustTimeZone Time.here + ) + + + +-- UPDATE + + +type Msg + = Tick Time.Posix + | AdjustTimeZone Time.Zone + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + Tick newTime -> + ( { model | time = newTime } + , Cmd.none + ) + + AdjustTimeZone newZone -> + ( { model | zone = newZone } + , Cmd.none + ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Time.every 1000 Tick + + + +-- VIEW + + +view : Model -> Html Msg +view model = + let + hour = + String.fromInt (Time.toHour model.zone model.time) + + minute = + String.fromInt (Time.toMinute model.zone model.time) + + second = + String.fromInt (Time.toSecond model.zone model.time) + in + h1 [] [ text (hour ++ ":" ++ minute ++ ":" ++ second) ] diff --git a/public/example-files/triangle.guida b/public/example-files/triangle.guida new file mode 100644 index 0000000..0a3d4f8 --- /dev/null +++ b/public/example-files/triangle.guida @@ -0,0 +1,140 @@ +module Main exposing (main) + +-- guida install elm-explorations/linear-algebra +-- guida install elm-explorations/webgl + +import Browser +import Browser.Events as E +import Html exposing (Html) +import Html.Attributes exposing (height, style, width) +import Math.Matrix4 as Mat4 exposing (Mat4) +import Math.Vector3 as Vec3 exposing (Vec3, vec3) +import WebGL + + + +-- MAIN + + +main = + Browser.element + { init = init + , view = view + , update = update + , subscriptions = subscriptions + } + + + +-- MODEL + + +type alias Model = + Float + + +init : () -> ( Model, Cmd Msg ) +init () = + ( 0, Cmd.none ) + + + +-- UPDATE + + +type Msg + = TimeDelta Float + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg currentTime = + case msg of + TimeDelta delta -> + ( delta + currentTime, Cmd.none ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions _ = + E.onAnimationFrameDelta TimeDelta + + + +-- VIEW + + +view : Model -> Html msg +view t = + WebGL.toHtml + [ width 400 + , height 400 + , style "display" "block" + ] + [ WebGL.entity vertexShader fragmentShader mesh { perspective = perspective (t / 1000) } + ] + + +perspective : Float -> Mat4 +perspective t = + Mat4.mul + (Mat4.makePerspective 45 1 0.01 100) + (Mat4.makeLookAt (vec3 (4 * cos t) 0 (4 * sin t)) (vec3 0 0 0) (vec3 0 1 0)) + + + +-- MESH + + +type alias Vertex = + { position : Vec3 + , color : Vec3 + } + + +mesh : WebGL.Mesh Vertex +mesh = + WebGL.triangles + [ ( Vertex (vec3 0 0 0) (vec3 1 0 0) + , Vertex (vec3 1 1 0) (vec3 0 1 0) + , Vertex (vec3 1 -1 0) (vec3 0 0 1) + ) + ] + + + +-- SHADERS + + +type alias Uniforms = + { perspective : Mat4 + } + + +vertexShader : WebGL.Shader Vertex Uniforms { vcolor : Vec3 } +vertexShader = + [glsl| + attribute vec3 position; + attribute vec3 color; + uniform mat4 perspective; + varying vec3 vcolor; + + void main () { + gl_Position = perspective * vec4(position, 1.0); + vcolor = color; + } + |] + + +fragmentShader : WebGL.Shader {} Uniforms { vcolor : Vec3 } +fragmentShader = + [glsl| + precision mediump float; + varying vec3 vcolor; + + void main () { + gl_FragColor = vec4(vcolor, 1.0); + } + |] diff --git a/public/example-files/turtle.guida b/public/example-files/turtle.guida new file mode 100644 index 0000000..413da1d --- /dev/null +++ b/public/example-files/turtle.guida @@ -0,0 +1,34 @@ +module Main exposing (main) + +-- Use arrow keys to move the turtle around. +-- +-- Forward with UP and turn with LEFT and RIGHT. +-- +-- Learn more about the playground here: +-- https://package.elm-lang.org/packages/evancz/elm-playground/latest/ + +import Playground exposing (..) + + +main = + game view + update + { x = 0 + , y = 0 + , angle = 0 + } + + +view computer turtle = + [ rectangle blue computer.screen.width computer.screen.height + , image 96 96 "https://elm-lang.org/images/turtle.gif" + |> move turtle.x turtle.y + |> rotate turtle.angle + ] + + +update computer turtle = + { x = turtle.x + toY computer.keyboard * cos (degrees turtle.angle) + , y = turtle.y + toY computer.keyboard * sin (degrees turtle.angle) + , angle = turtle.angle - toX computer.keyboard + } diff --git a/public/example-files/upload.guida b/public/example-files/upload.guida new file mode 100644 index 0000000..7f13da0 --- /dev/null +++ b/public/example-files/upload.guida @@ -0,0 +1,86 @@ +module Main exposing (main) + +-- File upload with the node. +-- +-- Dependencies: +-- guida install elm/file +-- guida install elm/json + +import Browser +import File exposing (File) +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Json.Decode as D + + + +-- MAIN + + +main = + Browser.element + { init = init + , view = view + , update = update + , subscriptions = subscriptions + } + + + +-- MODEL + + +type alias Model = + List File + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( [], Cmd.none ) + + + +-- UPDATE + + +type Msg + = GotFiles (List File) + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + GotFiles files -> + ( files, Cmd.none ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : Model -> Html Msg +view model = + div [] + [ input + [ type_ "file" + , multiple True + , on "change" (D.map GotFiles filesDecoder) + ] + [] + , div [] [ text (Debug.toString model) ] + ] + + +filesDecoder : D.Decoder (List File) +filesDecoder = + D.at [ "target", "files" ] (D.list File.decoder) diff --git a/public/index.html b/public/index.html index 663bf4f..14ddb7f 100644 --- a/public/index.html +++ b/public/index.html @@ -8,6 +8,7 @@ + diff --git a/src/Components/CodeBlock.elm b/src/Components/CodeBlock.elm new file mode 100644 index 0000000..d851359 --- /dev/null +++ b/src/Components/CodeBlock.elm @@ -0,0 +1,12 @@ +module Components.CodeBlock exposing (..) + +import Html exposing (Html) +import Html.Attributes as Attr + + +view : String -> Html msg +view content = + Html.pre [ Attr.class "overflow-auto p-4 bg-neutral-100" ] + [ Html.code [ Attr.class "shadow-none p-0" ] + [ Html.text content ] + ] diff --git a/src/Components/Properties.elm b/src/Components/Properties.elm new file mode 100644 index 0000000..86a429f --- /dev/null +++ b/src/Components/Properties.elm @@ -0,0 +1,50 @@ +module Components.Properties exposing (view) + +import Html exposing (Html) +import Html.Attributes as Attr +import Html.Attributes.Aria as Aria + + +type alias Property msg = + { name : String + , type_ : Maybe String + , children : List (Html msg) + } + + +view : List (Property msg) -> Html msg +view properties = + Html.div [ Attr.class "my-6" ] + [ Html.ul + [ Aria.role "list" + , Attr.class "m-0 list-none divide-y divide-zinc-900/5 p-0 dark:divide-white/5" + ] + (List.map propertyView properties) + ] + + +propertyView : Property msg -> Html msg +propertyView property = + let + typeDescriptionTerm : List (Html msg) + typeDescriptionTerm = + property.type_ + |> Maybe.map + (\type_ -> + [ Html.dt [ Attr.class "sr-only" ] [ Html.text "Type" ] + , Html.dd [ Attr.class "font-mono text-xs text-zinc-400 dark:text-zinc-500" ] [ Html.text type_ ] + ] + ) + |> Maybe.withDefault [] + in + Html.li [ Attr.class "m-0 px-0 py-4 first:pt-0 last:pb-0" ] + [ Html.dl [ Attr.class "m-0 flex flex-wrap items-center gap-x-3 gap-y-2" ] + (Html.dt [ Attr.class "sr-only" ] [ Html.text "Name" ] + :: Html.dd [] [ Html.code [] [ Html.text property.name ] ] + :: typeDescriptionTerm + ++ [ Html.dt [ Attr.class "sr-only" ] [ Html.text "Description" ] + , Html.dd [ Attr.class "w-full flex-none [&>:first-child]:mt-0 [&>:last-child]:mb-0" ] + property.children + ] + ) + ] diff --git a/src/Components/References.elm b/src/Components/References.elm new file mode 100644 index 0000000..347ab1a --- /dev/null +++ b/src/Components/References.elm @@ -0,0 +1,23 @@ +module Components.References exposing (view) + +import Components.Button as Button +import Html exposing (Html) +import Html.Attributes as Attr +import Html.Attributes.Aria as Aria + + +view : List String -> Html msg +view links = + Html.div [ Attr.class "my-6" ] + [ Html.hr [] [] + , Html.h2 [ Attr.class "scroll-mt-24" ] [ Html.text "References" ] + , Html.ul [ Aria.role "list" ] + (List.map linkView links) + ] + + +linkView : String -> Html msg +linkView href = + Html.li [] + [ Button.view (Button.Link href) Button.Text Nothing [] [ Html.text href ] + ] diff --git a/src/Components/ResourcePattern.elm b/src/Components/ResourcePattern.elm new file mode 100644 index 0000000..896bddd --- /dev/null +++ b/src/Components/ResourcePattern.elm @@ -0,0 +1,50 @@ +module Components.ResourcePattern exposing (view) + +import Components.GridPattern as GridPattern +import Html exposing (Html) +import Html.Attributes as Attr +import Svg.Attributes as SvgAttr + + +view : { mouseX : Int, mouseY : Int } -> Html msg +view { mouseX, mouseY } = + let + styleAttrs : List (Html.Attribute msg) + styleAttrs = + [ Attr.style "mask-image" ("radial-gradient(180px at " ++ String.fromInt mouseX ++ "px " ++ String.fromInt mouseY ++ "px, white, transparent)") + ] + in + Html.div [ Attr.class "pointer-events-none" ] + [ Html.div [ Attr.class "absolute inset-0 rounded-2xl transition duration-300 [mask-image:linear-gradient(white,transparent)] group-hover:opacity-50" ] + [ GridPattern.view [ SvgAttr.class "absolute inset-x-0 inset-y-[-30%] h-[160%] w-full skew-y-[-18deg] fill-black/[0.02] stroke-black/5 dark:fill-white/1 dark:stroke-white/2.5" ] + { width = 72 + , height = 56 + , x = 28 + , y = 16 + , squares = + [ ( 0, 1 ) + , ( 1, 3 ) + ] + } + ] + , Html.div + (Attr.class "absolute inset-0 rounded-2xl bg-linear-to-r from-[#FFFBEB] to-[#FEE685] opacity-0 transition duration-300 group-hover:opacity-100 dark:from-[#202D2E] dark:to-[#303428]" + :: styleAttrs + ) + [] + , Html.div + (Attr.class "absolute inset-0 rounded-2xl opacity-0 mix-blend-overlay transition duration-300 group-hover:opacity-100" + :: styleAttrs + ) + [ GridPattern.view [ SvgAttr.class "absolute inset-x-0 inset-y-[-30%] h-[160%] w-full skew-y-[-18deg] fill-black/50 stroke-black/70 dark:fill-white/2.5 dark:stroke-white/10" ] + { width = 72 + , height = 56 + , x = 28 + , y = 16 + , squares = + [ ( 0, 1 ) + , ( 1, 3 ) + ] + } + ] + ] diff --git a/src/Layout/Global.elm b/src/Layout/Global.elm index 99a758f..e773026 100644 --- a/src/Layout/Global.elm +++ b/src/Layout/Global.elm @@ -17,7 +17,6 @@ type alias NavItem msg = topLevelNavItems : List (NavItem msg) topLevelNavItems = [ { href = "/examples", children = [ Html.text "Examples" ] } - , { href = "/try", children = [ Html.text "Try" ] } , { href = "/docs", children = [ Html.text "Docs" ] } , { href = "/community", children = [ Html.text "Community" ] } , { href = "https://package.guida-lang.org", children = [ Html.text "Packages" ] } diff --git a/src/Layout/Header.elm b/src/Layout/Header.elm index ae18a34..63b3ae2 100644 --- a/src/Layout/Header.elm +++ b/src/Layout/Header.elm @@ -98,16 +98,4 @@ view config = , socialLink Global.discordLink Icon.discord [ Html.text "Join our Discord server" ] ] ] - - -- , Html.h1 [ Attr.class "title" ] [ Html.a [ Attr.href "/" ] [ Html.text "Guida" ] ] - -- , Html.p [ Attr.class "subtitle" ] [ Html.text "Functional programming, evolved!" ] - -- , Html.nav [ Aria.role "navigation" ] - -- [ Html.ul [] - -- [ Html.li [] [ Html.a [ Attr.href "/examples" ] [ Html.text "Examples" ] ] - -- , Html.li [] [ Html.a [ Attr.href "/try" ] [ Html.text "Try" ] ] - -- , Html.li [] [ Html.a [ Attr.href "/docs" ] [ Html.text "Documentation" ] ] - -- , Html.li [] [ Html.a [ Attr.href "/community" ] [ Html.text "Community" ] ] - -- , Html.li [] [ Html.a [ Attr.href "https://package.guida-lang.org" ] [ Html.text "Packages" ] ] - -- ] - -- ] ] diff --git a/src/Layout/Main.elm b/src/Layout/Main.elm index 06b67b8..b5416f9 100644 --- a/src/Layout/Main.elm +++ b/src/Layout/Main.elm @@ -1,10 +1,14 @@ -module Layout.Main exposing (view) +module Layout.Main exposing + ( fullscreenView + , view + ) import Components.Link as Link import Components.Logo as Logo import Html exposing (Html) import Html.Attributes as Attr import Html.Attributes.Aria as Aria +import Html.Events as Events import Layout.Footer as Footer import Layout.Header as Header import Layout.Navigation as Navigation exposing (Navigation) @@ -24,8 +28,8 @@ type alias Config = -- VIEW -view : Config -> Session -> (Session.Msg -> msg) -> List (Html msg) -> List (Html msg) -view config session toSessionMsg children = +sidebarView : Config -> Session -> (Session.Msg -> msg) -> Html msg +sidebarView config session toSessionMsg = let header : Html msg header = @@ -37,23 +41,25 @@ view config session toSessionMsg children = , setThemeMsg = toSessionMsg << Session.SetTheme , toggleNavigationMsg = toSessionMsg Session.ToggleMobileNavigation } - - sidebar : Html msg - sidebar = - if List.isEmpty config.sidebarNavigation then - Html.div [ Attr.class "contents lg:pointer-events-auto" ] [ header ] - - else - Html.div [ Attr.class "contents lg:pointer-events-auto lg:block lg:w-72 lg:overflow-y-auto lg:border-r lg:border-zinc-900/10 lg:px-6 lg:pt-4 lg:pb-8 xl:w-80 lg:dark:border-white/10" ] - [ Html.div [ Attr.class "hidden lg:flex" ] - [ Link.view [ Attr.href "/", Aria.ariaLabel "Home" ] - [ Logo.view "h-6" - ] - ] - , header - , Navigation.view [ Attr.class "hidden lg:mt-10 lg:block" ] config.sidebarNavigation + in + if List.isEmpty config.sidebarNavigation then + Html.div [ Attr.class "contents lg:pointer-events-auto" ] [ header ] + + else + Html.div [ Attr.class "contents lg:pointer-events-auto lg:block lg:w-72 lg:overflow-y-auto lg:border-r lg:border-zinc-900/10 lg:px-6 lg:pt-4 lg:pb-8 xl:w-80 lg:dark:border-white/10" ] + [ Html.div [ Attr.class "hidden lg:flex" ] + [ Link.view [ Attr.href "/", Aria.ariaLabel "Home" ] + [ Logo.view "h-6" ] + ] + , header + , Navigation.view [ Attr.class "hidden lg:mt-10 lg:block" ] config.sidebarNavigation + ] + +dialogView : Config -> Session -> (Session.Msg -> msg) -> Html msg +dialogView config session toSessionMsg = + let openAttrs : List (Html.Attribute msg) openAttrs = if Session.isMobileNavigationOpen session then @@ -62,6 +68,31 @@ view config session toSessionMsg children = else [] in + Html.node "dialog" + (Attr.class "fixed inset-0 z-50 lg:hidden" + :: openAttrs + ) + [ Html.div + [ Attr.class "fixed inset-0 top-14 bg-zinc-400/20 backdrop-blur-xs data-closed:opacity-0 data-enter:duration-300 data-enter:ease-out data-leave:duration-200 data-leave:ease-in dark:bg-black/40" + , Events.onClick (toSessionMsg Session.ToggleMobileNavigation) + ] + [] + , Header.view + { session = session + , className = Just "data-closed:opacity-0 data-enter:duration-300 data-enter:ease-out data-leave:duration-200 data-leave:ease-in" + , hasSidebar = not (List.isEmpty config.sidebarNavigation) + , isInsideMobileNavigation = True + , setThemeMsg = toSessionMsg << Session.SetTheme + , toggleNavigationMsg = toSessionMsg Session.ToggleMobileNavigation + } + , Html.div [ Attr.class "fixed top-14 bottom-0 left-0 w-full overflow-y-auto bg-white px-4 pt-6 pb-4 ring-1 shadow-lg shadow-zinc-900/10 ring-zinc-900/7.5 duration-500 ease-in-out data-closed:-translate-x-full min-[416px]:max-w-sm sm:px-6 sm:pb-10 dark:bg-zinc-900 dark:ring-zinc-800" ] + [ Navigation.view [] config.sidebarNavigation + ] + ] + + +view : Config -> Session -> (Session.Msg -> msg) -> List (Html msg) -> List (Html msg) +view config session toSessionMsg children = [ Html.div [ Attr.class "contents" ] [ Html.div [ Attr.class "w-full" ] [ Html.div @@ -71,7 +102,7 @@ view config session toSessionMsg children = ] ] [ Html.header [ Attr.class "contents lg:pointer-events-none lg:fixed lg:inset-0 lg:z-40 lg:flex" ] - [ sidebar + [ sidebarView config session toSessionMsg ] , Html.div [ Attr.class "relative flex h-full flex-col px-4 pt-14 sm:px-6 lg:px-8" ] [ Html.main_ [ Attr.class "flex-auto" ] @@ -93,21 +124,29 @@ view config session toSessionMsg children = ] ] ] - , Html.node "dialog" - (Attr.class "fixed inset-0 z-50 lg:hidden" - :: openAttrs - ) - [ Html.div [ Attr.class "fixed inset-0 top-14 bg-zinc-400/20 backdrop-blur-xs data-closed:opacity-0 data-enter:duration-300 data-enter:ease-out data-leave:duration-200 data-leave:ease-in dark:bg-black/40" ] [] - , Header.view - { session = session - , className = Just "data-closed:opacity-0 data-enter:duration-300 data-enter:ease-out data-leave:duration-200 data-leave:ease-in" - , hasSidebar = not (List.isEmpty config.sidebarNavigation) - , isInsideMobileNavigation = True - , setThemeMsg = toSessionMsg << Session.SetTheme - , toggleNavigationMsg = toSessionMsg Session.ToggleMobileNavigation - } - , Html.div [ Attr.class "fixed top-14 bottom-0 left-0 w-full overflow-y-auto bg-white px-4 pt-6 pb-4 ring-1 shadow-lg shadow-zinc-900/10 ring-zinc-900/7.5 duration-500 ease-in-out data-closed:-translate-x-full min-[416px]:max-w-sm sm:px-6 sm:pb-10 dark:bg-zinc-900 dark:ring-zinc-800" ] - [ Navigation.view [] config.sidebarNavigation + , dialogView config session toSessionMsg + ] + + +fullscreenView : Session -> (Session.Msg -> msg) -> List (Html msg) -> List (Html msg) +fullscreenView session toSessionMsg children = + let + config : Config + config = + { sidebarNavigation = [] } + in + [ Html.div [ Attr.class "contents" ] + [ Html.div [ Attr.class "w-full" ] + [ Html.div [ Attr.classList [ ( "h-full", True ) ] ] + [ Html.header [ Attr.class "contents lg:pointer-events-none lg:fixed lg:inset-0 lg:z-40 lg:flex" ] + [ sidebarView config session toSessionMsg + ] + , Html.div [ Attr.class "relative flex h-full flex-col pt-14" ] + [ Html.main_ [ Attr.class "flex-auto prose dark:prose-invert" ] + children + ] + ] ] ] + , dialogView config session toSessionMsg ] diff --git a/src/Main.elm b/src/Main.elm index c2a1ac8..34e97e6 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -1,10 +1,4 @@ -module Main exposing - ( CurrentPage - , Flags - , Model - , Msg - , main - ) +module Main exposing (main) import Browser import Browser.Events @@ -35,7 +29,7 @@ type alias Model = type CurrentPage = NotFound | Home - | Docs + | Docs Docs.Model | Community | Examples | Try Try.Model @@ -72,8 +66,9 @@ changeRouteTo maybeRoute model = Just Route.Home -> ( { model | currentPage = Home }, Cmd.none ) - Just Route.Docs -> - ( { model | currentPage = Docs }, Cmd.none ) + Just (Route.Docs subRoute) -> + Docs.init subRoute + |> updateWith Docs identity model Just Route.Community -> ( { model | currentPage = Community }, Cmd.none ) @@ -108,7 +103,7 @@ update msg model = ( ClickedLink urlRequest, _ ) -> case urlRequest of Browser.Internal url -> - ( model + ( { model | session = Session.closeMobileNavigation model.session } , Nav.pushUrl (Session.navKey model.session) (Url.toString url) ) @@ -156,7 +151,7 @@ subscriptions model = breakpoint : Int breakpoint = case model.currentPage of - Docs -> + Docs _ -> withSidebarBreakpoint _ -> @@ -187,8 +182,8 @@ view model = Home -> Home.view model.session SessionMsg - Docs -> - Docs.view model.session SessionMsg + Docs subModel -> + Docs.view model.session SessionMsg subModel Community -> Community.view model.session SessionMsg diff --git a/src/Page/Docs.elm b/src/Page/Docs.elm index d791c01..138e9a3 100644 --- a/src/Page/Docs.elm +++ b/src/Page/Docs.elm @@ -1,140 +1,374 @@ -module Page.Docs exposing (view) +module Page.Docs exposing + ( Model + , init + , view + ) import Browser -import Html +import Components.Button as Button exposing (Type(..)) +import Components.CodeBlock as CodeBlock +import Components.Properties as Properties +import Components.References as References +import Html exposing (Html) import Html.Attributes as Attr -import Html.Attributes.Aria as Aria import Layout.Main as Layout import Layout.Navigation exposing (Navigation) +import Route import Session exposing (Session) -sidebarNavigation : Navigation -sidebarNavigation = - [ { title = "Commands" - , links = - [ { title = "repl", href = "/docs/1.0.0/commands/repl", active = False } - , { title = "init", href = "/docs/1.0.0/commands/init", active = True } - , { title = "make", href = "/docs/1.0.0/commands/make", active = False } - , { title = "install", href = "/docs/1.0.0/commands/install", active = False } - , { title = "uninstall", href = "/docs/1.0.0/commands/uninstall", active = False } - , { title = "bump", href = "/docs/1.0.0/commands/bump", active = False } - , { title = "diff", href = "/docs/1.0.0/commands/diff", active = False } - , { title = "publish", href = "/docs/1.0.0/commands/publish", active = False } - , { title = "format", href = "/docs/1.0.0/commands/format", active = False } - , { title = "test", href = "/docs/1.0.0/commands/test", active = False } - ] - } - , { title = "Hints" - , links = - [ { title = "Bad recursion", href = "/docs/1.0.0/hints/bad-recursion", active = False } - , { title = "Comparing custom types", href = "/docs/1.0.0/hints/comparing-custom-types", active = False } - , { title = "Comparing records", href = "/docs/1.0.0/hints/comparing-records", active = False } - , { title = "Implicit casts", href = "/docs/1.0.0/hints/implicit-casts", active = False } - , { title = "Import cycles", href = "/docs/1.0.0/hints/import-cycles", active = False } - , { title = "Imports", href = "/docs/1.0.0/hints/imports", active = False } - , { title = "Infinite type", href = "/docs/1.0.0/hints/infinite-type", active = False } - , { title = "Missing patterns", href = "/docs/1.0.0/hints/missing-patterns", active = False } - , { title = "Optimize", href = "/docs/1.0.0/hints/optimize", active = False } - , { title = "Port modules", href = "/docs/1.0.0/hints/port-modules", active = False } - , { title = "Recursive alias", href = "/docs/1.0.0/hints/recursive-alias", active = False } - , { title = "Shadowing", href = "/docs/1.0.0/hints/shadowing", active = False } - , { title = "Tuples", href = "/docs/1.0.0/hints/tuples", active = False } - , { title = "Type annotations", href = "/docs/1.0.0/hints/type-annotations", active = False } - ] - } - ] + +-- MODEL + + +type alias Model = + { section : Route.DocumentationSection + } + + +init : Route.DocumentationSection -> ( Model, Cmd msg ) +init section = + ( { section = section }, Cmd.none ) -- VIEW -view : Session -> (Session.Msg -> msg) -> Browser.Document msg -view session toSessionMsg = +view : Session -> (Session.Msg -> msg) -> Model -> Browser.Document msg +view session toSessionMsg model = { title = "Guida: Documentation" , body = - Layout.view { sidebarNavigation = sidebarNavigation } session toSessionMsg <| - [ Html.section [] - [ Html.h2 [] [ Html.text "Documentation" ] - , Html.h3 [] [ Html.text "Guida REPL" ] - , Html.p [] [ Html.text "The `repl` command opens up an interactive programming session, sometimes called a **Read-Eval-Print Loop**." ] - , Html.p [] [ Html.text "It lets you try out Guida expressions, explore functions, and learn the language in a quick and interactive way." ] - , Html.code [] [ Html.text "guida repl" ] - , Html.p [] [ Html.text "Once inside the REPL, you can type in Guida expressions and immediately see the results:" ] - , Html.code [] - [ Html.text "> 1 + 2" - , Html.text "3" - , Html.text "> String.toUpper \"hello\"" - , Html.text "\"HELLO\"" - , Html.text "> List.map (\\n -> n * 2) [1, 2, 3]" - , Html.text "[2,4,6]" + Layout.view { sidebarNavigation = sidebarNavigation model } session toSessionMsg <| + case model.section of + Route.Introduction -> + [] + + Route.Elm -> + [] + + Route.GuidaJson -> + [ Html.h1 [] [ Html.text "guida.json" ] + , Html.p [] + [ Html.text "The " + , Html.code [] [ Html.text "guida.json" ] + , Html.text " describes your project." + ] + , Html.p [] [ Html.text "There are two different types of project: applications and packages." ] + , Html.p [] + [ Html.text "Depending on the type of project, the " + , Html.code [] [ Html.text "guida.json" ] + , Html.text " looks slightly different." + ] + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Application" ] + , Html.p [] + [ Html.text "When running " + , Html.code [] [ Html.text "guida init" ] + , Html.text " a " + , Html.code [] [ Html.text "guida.json" ] + , Html.text " file is generated with the following content:" + ] + , CodeBlock.view """{ + "type": "application", + "source-directories": [ + "src" + ], + "guida-version": "1.0.0", + "dependencies": { + "direct": { + "elm/browser": "1.0.2", + "elm/core": "1.0.5", + "elm/html": "1.0.0" + }, + "indirect": { + "elm/json": "1.1.4", + "elm/time": "1.0.0", + "elm/url": "1.0.0", + "elm/virtual-dom": "1.0.4" + } + }, + "test-dependencies": { + "direct": { + "elm-explorations/test": "2.2.0" + }, + "indirect": { + "elm/bytes": "1.0.8", + "elm/random": "1.0.0" + } + } +}""" + , Properties.view + [ { name = "\"type\"" + , type_ = Nothing + , children = + [ Html.text "Either " + , Html.code [] [ Html.text "\"application\"" ] + , Html.text " or " + , Html.code [] [ Html.text "\"package\"" ] + , Html.text ". All the other fields are based on this choice!" + ] + } + , { name = "\"source-directories\"" + , type_ = Nothing + , children = + [ Html.text "A list of directories where Guida code lives. Most projects just use " + , Html.code [] [ Html.text "\"src\"" ] + , Html.text " for everything." + ] + } + , { name = "\"guida-version\"" + , type_ = Nothing + , children = + [ Html.text "The exact version of Guida this builds with. Should be " + , Html.code [] [ Html.text "\"1.0.0\"" ] + , Html.text " for most people!" + ] + } + , { name = "\"dependencies\"" + , type_ = Nothing + , children = + [ Html.text "All the packages you depend upon. We use exact versions, so your " + , Html.code [] [ Html.text "guida.json" ] + , Html.text " file doubles as a \"lock file\" that ensures reliable builds." + , Html.br [] [] + , Html.text "You can use modules from any `\"direct\"` dependency in your code. Some `\"direct\"` dependencies have their own dependencies that folks typically do not care about. These are the `\"indirect\"` dependencies. They are listed explicitly so that (1) builds are reproducible and (2) you can easily review the quantity and quality of dependencies." + , Html.br [] [] + , Html.text "**Note:** We plan to eventually have a screen in `reactor` that helps add, remove, and upgrade packages. It can sometimes be tricky to keep all of the constraints happy, so we think having a UI will help a lot. If you get into trouble in the meantime, adding things back one-by-one often helps, and I hope you do not get into trouble!" + ] + } + , { name = "\"test-dependencies\"" + , type_ = Nothing + , children = + [ Html.text "All the packages that you use in `tests/` with `guida test` but not in the application you actually want to ship. This also uses exact versions to make tests more reliable." + ] + } + ] + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Package" ] + , Html.p [] + [ Html.text "When running " + , Html.code [] [ Html.text "guida init --package" ] + , Html.text " a " + , Html.code [] [ Html.text "guida.json" ] + , Html.text " file is generated with the following content:" + ] + , CodeBlock.view """{ + "type": "package", + "name": "author/project", + "summary": "helpful summary of your project, less than 80 characters", + "license": "BSD-3-Clause", + "version": "1.0.0", + "exposed-modules": [], + "guida-version": "1.0.0 <= v < 2.0.0", + "dependencies": { + "elm/core": "1.0.5 <= v < 2.0.0" + }, + "test-dependencies": { + "elm-explorations/test": "2.2.0 <= v < 3.0.0" + } +}""" + , References.view + [ "https://github.com/elm/compiler/blob/0.19.1/docs/elm.json/application.md" + , "https://github.com/elm/compiler/blob/0.19.1/docs/elm.json/package.md" + , "https://gren-lang.org/book/appendix/gren_json/" + ] ] + + Route.Commands command -> + commandView command + + Route.Hints hint -> + hintView hint + } + + +commandView : Route.Command -> List (Html msg) +commandView command = + case command of + Route.Repl -> + [ Html.h1 [] [ Html.text "guida repl" ] + , Html.p [] [ Html.text "The REPL lets you interact with Guida values and functions in your terminal." ] + , Html.p [] [ Html.text "You can type in expressions, definitions, custom types, and module imports using normal Guida syntax." ] + , CodeBlock.view """> 1 + 1 +2 : number + +> "hello" ++ "world" +"helloworld" : String""" + , Html.p [] [ Html.text "The same can be done with definitions and custom types:" ] + , CodeBlock.view """> fortyTwo = 42 +42 : number + +> increment n = n + 1 + : number -> number + +> increment 41 +42 : number + +> factorial n = +| if n < 1 then +| 1 +| else +| n * factorial (n-1) +| + : number -> number + +> factorial 5 +120 : number + +> type User +| = Regular String +| | Visitor String +| + +> case Regular "Tom" of +| Regular name -> "Hey again!" +| Visitor name -> "Nice to meet you!" +| +"Hey again!" : String""" + , Html.p [] + [ Html.text "When you run " + , Html.code [] [ Html.text "guida repl" ] + , Html.text " in a project with an " + , Button.view (Button.Link "/docs/guida-json") Button.Text Nothing [] [ Html.text "guida.json" ] + , Html.text " file, you can import any module available in the project. So if your project has an " + , Html.code [] [ Html.text "elm/html" ] + , Html.text " dependency, you could say:" + ] + , CodeBlock.view """> import Html exposing (Html) + +> Html.text "hello" + : Html msg + +> Html.text + : String -> Html msg""" + , Html.p [] + [ Html.text "If you create a module in your project named " + , Html.code [] [ Html.text "MyThing" ] + , Html.text " in your project, you can say " + , Html.code [] [ Html.text "import MyThing" ] + , Html.text " in the REPL as well. Any module that is accessible in your project should be accessible in the REPL." + ] + , Html.hr [] [] + , Html.h2 [ Attr.class "scroll-mt-24" ] [ Html.text "Exit" ] + , Html.p [] + [ Html.text "To exit the REPL, you can type " + , Html.code [] [ Html.text ":exit" ] + , Html.text "." + ] + , Html.p [] + [ Html.text "You can also press " + , Html.code [] [ Html.text "ctrl-d" ] + , Html.text " or " + , Html.code [] [ Html.text "ctrl-c" ] + , Html.text " on some platforms." + ] + , Html.hr [] [] + , Html.h2 [ Attr.class "scroll-mt-24" ] [ Html.text "Flags" ] + , Html.p [] [ Html.text "You can customize this command with the following flags:" ] + , Properties.view + [ { name = "--interpreter=" + , type_ = Nothing + , children = [ Html.text "Path to a alternate JS interpreter, like node or nodejs." ] + } + , { name = "--no-colors" + , type_ = Nothing + , children = [ Html.text "Turn off the colors in the REPL. This can help if you are having trouble reading the values. Some terminals use a custom color scheme that diverges significantly from the standard ANSI colors, so another path may be to pick a more standard color scheme." ] + } + ] + , References.view + [ "https://github.com/elm/compiler/blob/0.19.1/hints/repl.md" ] ] - } + Route.Init -> + [ Html.h1 [] [ Html.text "guida init" ] + ] + + Route.Make -> + [ Html.h1 [] [ Html.text "guida make" ] + ] + + Route.Install -> + [ Html.h1 [] [ Html.text "guida install" ] + ] + + Route.Uninstall -> + [ Html.h1 [] [ Html.text "guida uninstall" ] + ] + Route.Bump -> + [ Html.h1 [] [ Html.text "guida bump" ] + ] + + Route.Diff -> + [ Html.h1 [] [ Html.text "guida diff" ] + ] + + Route.Publish -> + [ Html.h1 [] [ Html.text "guida publish" ] + ] + + Route.Format -> + [ Html.h1 [] [ Html.text "guida format" ] + ] --- # Guida REPL --- The `repl` command opens up an interactive programming session, sometimes called a **Read-Eval-Print Loop**. --- It lets you try out Guida expressions, explore functions, and learn the language in a quick and interactive way. --- ```bash --- guida repl --- ```` --- Once inside the REPL, you can type in Guida expressions and immediately see the results: --- ```elm --- > 1 + 2 --- 3 --- > String.toUpper "hello" --- "HELLO" --- > List.map (\n -> n * 2) [1, 2, 3] --- [2,4,6] --- ``` --- --- --- ## Learning with the REPL --- A great way to get started with Guida is by experimenting in the REPL. --- Follow along with the [official Guida Guide](https://guida-lang.org/docs) — it includes examples and exercises that you can try directly in the REPL. --- Exploring small snippets of code interactively can be much faster than editing a file, compiling, and running a project. --- --- --- ## Customizing the REPL --- You can configure how the REPL runs using these flags: --- ### `--interpreter=` --- Specify an alternate JavaScript runtime to evaluate Guida code. --- By default, Guida uses your system’s `node` installation, but you may override it: --- ```bash --- guida repl --interpreter=nodejs --- ``` --- This can be useful if your system differentiates between `node` and `nodejs` binaries, or if you want to run Guida with a custom JS runtime. --- --- --- ### `--no-colors` --- Disable ANSI colors in the REPL output: --- ```bash --- guida repl --no-colors --- ``` --- This can help if your terminal’s color scheme makes output hard to read, or if you prefer plain text. --- --- --- ## Example Session --- Here’s a quick demonstration: --- ```bash --- $ guida repl --- ---- --- Guida REPL 0.19.1 --- Type :help for available commands. Press Ctrl+D to exit. --- > 5 * 5 --- 25 --- > (\\x -> x + 1) 41 --- 42 --- > String.length "Guida" --- 5 --- ``` --- --- --- ## Tips --- * Use the REPL for **experimentation and learning** — it’s not meant to replace writing modules and building projects. --- * REPL does not persist your definitions between sessions. If you discover something useful, copy it into a `.guida` file in your project. --- * Use `:exit` or press `Ctrl+D` to quit. --- --- --- ## Related Topics --- * [Getting Started with Guida](https://guida-lang.org/docs/getting-started) --- * [Guida Guide](https://guida-lang.org/docs) --- * [Community](https://guida-lang.org/community) + Route.Test -> + [ Html.h1 [] [ Html.text "guida test" ] + ] + + +hintView : Route.Hint -> List (Html msg) +hintView hint = + case hint of + _ -> + [] + + +sidebarNavigation : Model -> Navigation +sidebarNavigation model = + let + activeCommand cmd = + model.section == Route.Commands cmd + + activeHint hint = + model.section == Route.Hints hint + in + [ { title = "Guide" + , links = + [ { title = "Introduction", href = "/docs", active = model.section == Route.Introduction } + , { title = "Elm", href = "/docs/elm", active = model.section == Route.Elm } + , { title = "guida.json", href = "/docs/guida-json", active = model.section == Route.GuidaJson } + ] + } + , { title = "Commands" + , links = + [ { title = "repl", href = "/docs/1.0.0/commands/repl", active = activeCommand Route.Repl } + , { title = "init", href = "/docs/1.0.0/commands/init", active = activeCommand Route.Init } + , { title = "make", href = "/docs/1.0.0/commands/make", active = activeCommand Route.Make } + , { title = "install", href = "/docs/1.0.0/commands/install", active = activeCommand Route.Install } + , { title = "uninstall", href = "/docs/1.0.0/commands/uninstall", active = activeCommand Route.Uninstall } + , { title = "bump", href = "/docs/1.0.0/commands/bump", active = activeCommand Route.Bump } + , { title = "diff", href = "/docs/1.0.0/commands/diff", active = activeCommand Route.Diff } + , { title = "publish", href = "/docs/1.0.0/commands/publish", active = activeCommand Route.Publish } + , { title = "format", href = "/docs/1.0.0/commands/format", active = activeCommand Route.Format } + , { title = "test", href = "/docs/1.0.0/commands/test", active = activeCommand Route.Test } + ] + } + , { title = "Hints" + , links = + [ { title = "Bad recursion", href = "/docs/1.0.0/hints/bad-recursion", active = activeHint Route.BadRecursion } + , { title = "Comparing custom types", href = "/docs/1.0.0/hints/comparing-custom-types", active = activeHint Route.ComparingCustomTypes } + , { title = "Comparing records", href = "/docs/1.0.0/hints/comparing-records", active = activeHint Route.ComparingRecords } + , { title = "Implicit casts", href = "/docs/1.0.0/hints/implicit-casts", active = activeHint Route.ImplicitCasts } + , { title = "Import cycles", href = "/docs/1.0.0/hints/import-cycles", active = activeHint Route.ImportCycles } + , { title = "Imports", href = "/docs/1.0.0/hints/imports", active = activeHint Route.Imports } + , { title = "Infinite type", href = "/docs/1.0.0/hints/infinite-type", active = activeHint Route.InfiniteType } + , { title = "Missing patterns", href = "/docs/1.0.0/hints/missing-patterns", active = activeHint Route.MissingPatterns } + , { title = "Optimize", href = "/docs/1.0.0/hints/optimize", active = activeHint Route.Optimize } + , { title = "Port modules", href = "/docs/1.0.0/hints/port-modules", active = activeHint Route.PortModules } + , { title = "Recursive alias", href = "/docs/1.0.0/hints/recursive-alias", active = activeHint Route.RecursiveAlias } + , { title = "Shadowing", href = "/docs/1.0.0/hints/shadowing", active = activeHint Route.Shadowing } + , { title = "Tuples", href = "/docs/1.0.0/hints/tuples", active = activeHint Route.Tuples } + , { title = "Type annotations", href = "/docs/1.0.0/hints/type-annotations", active = activeHint Route.TypeAnnotations } + ] + } + ] diff --git a/src/Page/Home.elm b/src/Page/Home.elm index c73714d..74c6e71 100644 --- a/src/Page/Home.elm +++ b/src/Page/Home.elm @@ -62,149 +62,104 @@ view session toSessionMsg = , Html.p [] [ Html.text "Our ultimate goal is to create a language that inherits the best aspects of Elm while adapting and growing to meet the needs of its users." ] - - -- Html.main_ [ Aria.role "main" ] - -- [ Html.section [] - -- [ Html.h2 [] [ Html.text "What is Guida?" ] - -- , Html.p [] - -- [ Html.text "Guida is a functional programming language that builds upon the solid foundation of " - -- , Html.a [ Attr.href "https://elm-lang.org" ] [ Html.text "Elm" ] - -- , Html.text ", offering backward compatibility with all existing " - -- , Html.a [ Attr.href "https://github.com/elm/compiler/releases/tag/0.19.1" ] [ Html.text "Elm 0.19.1" ] - -- , Html.text " projects." - -- ] - -- , Html.h3 [] [ Html.text "Vision" ] - -- , Html.p [] - -- [ Html.text "Guida builds on the foundations of Elm, aiming to advance the future of functional programming. By translating Elm's compiler from Haskell to a self-hosted environment, Guida helps developers to build reliable, maintainable, and performant applications without leaving the language they love." - -- ] - -- , Html.p [] - -- [ Html.strong [] [ Html.text "Continuity and Confidence (Version 0.x):" ] - -- , Html.text " Guida starts by ensuring full backward compatibility with Elm v0.19.1, allowing developers to migrate effortlessly and explore Guida with complete confidence." - -- ] - -- , Html.p [] - -- [ Html.text "This commitment to continuity means that this version will faithfully replicate not only the features and behaviors of Elm v0.19.1, but also any existing bugs and quirks. By doing so, we provide a stable and predictable environment for developers, ensuring that their existing Elm projects work exactly as expected when migrated to Guida." - -- ] - -- , Html.p [] - -- [ Html.strong [] [ Html.text "Evolution and Innovation (Version 1.x and Beyond):" ] - -- , Html.text " As Guida evolves, we will introduce new features and improvements. This phase will foster a unified ecosystem that adapts to the needs of its users." - -- ] - -- , Html.p [] [ Html.strong [] [ Html.text "Core Principles:" ] ] - -- , Html.ul [] - -- [ Html.li [] - -- [ Html.strong [] [ Html.text "Backward Compatibility:" ] - -- , Html.text " Respect for existing Elm projects, ensuring a frictionless migration." - -- ] - -- , Html.li [] - -- [ Html.strong [] [ Html.text "Accessibility:" ] - -- , Html.text " Lowering barriers for developers by implementing Guida's core in its own syntax." - -- ] - -- ] - -- , Html.p [] - -- [ Html.text "Our ultimate goal is to create a language that inherits the best aspects of Elm while adapting and growing to meet the needs of its users." - -- ] - -- ] - -- , Html.section [] - -- [ Html.h2 [] [ Html.text "Try It" ] - -- , Html.p [] - -- [ Html.text "Experiment with Guida in your browser. Write, run, and explore code instantly with the online Guida playground." - -- ] - -- , Html.p [] - -- [ Html.text "This is the easiest way to experiment with Guida in your browser. " - -- , Html.a [ Attr.href "/try" ] [ Html.text "Try it" ] - -- , Html.text ", no installation required." - -- ] - -- ] - -- , Html.section [] - -- [ Html.h2 [] [ Html.text "Documentation" ] - -- , Html.p [] - -- [ Html.text "The " - -- , Html.a [ Attr.href "/docs" ] [ Html.text "documentation" ] - -- , Html.text " is the best place to start learning about Guida. It will give you a solid foundation for creating applications. Once you have worked through that, the next place to look for documentation is on the " - -- , Html.a [ Attr.href "https://package.guida-lang.org" ] [ Html.text "packages" ] - -- , Html.text " you are using." - -- ] - -- ] - -- , Html.section [] - -- [ Html.h2 [] [ Html.text "Community" ] - -- , Html.p [] - -- [ Html.text "Join us to shape the language together. See our " - -- , Html.a [ Attr.href "/community" ] [ Html.text "Community" ] - -- , Html.text " page for more details on how to get involved. Here is a list of some of the main resources:" - -- , Html.ul [] - -- [ Html.li [] - -- [ Html.a [ Attr.href "https://github.com/guida-lang" ] - -- [ Html.text "Guida source code" - -- ] - -- ] - -- , Html.li [] - -- [ Html.a [ Attr.href "https://github.com/orgs/guida-lang/discussions" ] - -- [ Html.text "Collaborative communication forum" - -- ] - -- ] - -- , Html.li [] - -- [ Html.a [ Attr.href "https://github.com/guida-lang/compiler/blob/main/CONTRIBUTING.md" ] - -- [ Html.text "Contributing Guide" - -- ] - -- ] - -- ] - -- , Html.h3 [] [ Html.text "Contribute" ] - -- , Html.p [] [ Html.text "Guida is open source and thrives with your help: report bugs, improve the compiler and tools, and share your projects." ] - -- , Html.ul [] - -- [ Html.li [] - -- [ Html.a [ Attr.href "https://github.com/guida-lang/compiler/issues" ] [ Html.text "File a bug or feature request" ] - -- ] - -- , Html.li [] - -- [ Html.text "Help triage existing issues" - -- ] - -- , Html.li [] - -- [ Html.text "Submit improvements to the " - -- , Html.a [ Attr.href "https://github.com/guida-lang/compiler" ] [ Html.text "compiler" ] - -- , Html.text ", " - -- , Html.a [ Attr.href "https://github.com/guida-lang/package-registry" ] [ Html.text "registry" ] - -- , Html.text ", or " - -- , Html.a [ Attr.href "https://github.com/guida-lang" ] [ Html.text "tooling" ] - -- ] - -- , Html.li [] - -- [ Html.text "Improve documentation or examples" - -- ] - -- , Html.li [] - -- [ Html.text "Try out Guida and give us feedback" - -- ] - -- , Html.li [] - -- [ Html.text "Look for " - -- , Html.a [ Attr.href "https://github.com/guida-lang/compiler/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22" ] [ Html.text "good first issues" ] - -- , Html.text " if you're just getting started" - -- ] - -- , Html.li [] - -- [ Html.text "Port known issues or improvements from the Elm ecosystem" - -- ] - -- ] - -- ] - -- , Html.p [] - -- [ Html.text "Guida builds on projects like " - -- , Html.a [ Attr.href "https://github.com/elm/compiler" ] [ Html.text "elm/compiler" ] - -- , Html.text ", " - -- , Html.a [ Attr.href "https://github.com/avh4/elm-format" ] [ Html.text "elm-format" ] - -- , Html.text ", " - -- , Html.a [ Attr.href "https://github.com/elm-explorations/test" ] [ Html.text "elm-test" ] - -- , Html.text ", and " - -- , Html.a [ Attr.href "https://github.com/zwilias/elm-json" ] [ Html.text "elm-json" ] - -- , Html.text ". If you've encountered issues or ideas in those tools that feel worth bringing into Guida, feel free to reference them in a new issue or PR." - -- ] - -- ] - -- , Html.section [] - -- [ Html.h2 [] [ Html.text "Packages" ] - -- , Html.p [] - -- [ Html.text "Explore and publish libraries with the " - -- , Html.a [ Attr.href "https://package.guida-lang.org" ] [ Html.text "Guida package registry" ] - -- , Html.text "." - -- ] - -- ] - -- ] - -- , Html.footer [] - -- [ Html.p [] - -- [ Html.text "Guida builds on the work of the Elm community and related projects. We aim to carry the torch forward while staying true to what makes Elm great." - -- ] - -- ] + , Html.section [] + [ Html.h2 [ Attr.class "scroll-mt-24" ] [ Html.text "Try It" ] + , Html.p [] + [ Html.text "Experiment with Guida in your browser. Write, run, and explore code instantly with the " + , Html.a [ Attr.href "/try" ] [ Html.text "online Guida playground" ] + , Html.text "." + ] + , Html.p [] + [ Html.text "This is the easiest way to experiment with Guida in your browser. " + , Html.a [ Attr.href "/try" ] [ Html.text "Try it" ] + , Html.text ", no installation required." + ] + ] + , Html.section [] + [ Html.h2 [ Attr.class "scroll-mt-24" ] [ Html.text "Documentation" ] + , Html.p [] + [ Html.text "The " + , Html.a [ Attr.href "/docs" ] [ Html.text "documentation" ] + , Html.text " is the best place to start learning about Guida. It will give you a solid foundation for creating applications. Once you have worked through that, the next place to look for documentation is on the " + , Html.a [ Attr.href "https://package.guida-lang.org" ] [ Html.text "packages" ] + , Html.text " you are using." + ] + ] + , Html.section [] + [ Html.h2 [ Attr.class "scroll-mt-24" ] [ Html.text "Community" ] + , Html.p [] + [ Html.text "Join us to shape the language together. See our " + , Html.a [ Attr.href "/community" ] [ Html.text "Community" ] + , Html.text " page for more details on how to get involved. Here is a list of some of the main resources:" + , Html.ul [] + [ Html.li [] + [ Html.a [ Attr.href "https://github.com/guida-lang" ] + [ Html.text "Guida source code" + ] + ] + , Html.li [] + [ Html.a [ Attr.href "https://github.com/orgs/guida-lang/discussions" ] + [ Html.text "Collaborative communication forum" + ] + ] + , Html.li [] + [ Html.a [ Attr.href "https://github.com/guida-lang/compiler/blob/main/CONTRIBUTING.md" ] + [ Html.text "Contributing Guide" + ] + ] + ] + , Html.h3 [] [ Html.text "Contribute" ] + , Html.p [] [ Html.text "Guida is open source and thrives with your help: report bugs, improve the compiler and tools, and share your projects." ] + , Html.ul [] + [ Html.li [] + [ Html.a [ Attr.href "https://github.com/guida-lang/compiler/issues" ] [ Html.text "File a bug or feature request" ] + ] + , Html.li [] + [ Html.text "Help triage existing issues" + ] + , Html.li [] + [ Html.text "Submit improvements to the " + , Html.a [ Attr.href "https://github.com/guida-lang/compiler" ] [ Html.text "compiler" ] + , Html.text ", " + , Html.a [ Attr.href "https://github.com/guida-lang/package-registry" ] [ Html.text "registry" ] + , Html.text ", or " + , Html.a [ Attr.href "https://github.com/guida-lang" ] [ Html.text "tooling" ] + ] + , Html.li [] + [ Html.text "Improve documentation or examples" + ] + , Html.li [] + [ Html.text "Try out Guida and give us feedback" + ] + , Html.li [] + [ Html.text "Look for " + , Html.a [ Attr.href "https://github.com/guida-lang/compiler/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22" ] [ Html.text "good first issues" ] + , Html.text " if you're just getting started" + ] + , Html.li [] + [ Html.text "Port known issues or improvements from the Elm ecosystem" + ] + ] + ] + , Html.p [] + [ Html.text "Guida builds on projects like " + , Html.a [ Attr.href "https://github.com/elm/compiler" ] [ Html.text "elm/compiler" ] + , Html.text ", " + , Html.a [ Attr.href "https://github.com/avh4/elm-format" ] [ Html.text "elm-format" ] + , Html.text ", " + , Html.a [ Attr.href "https://github.com/elm-explorations/test" ] [ Html.text "elm-test" ] + , Html.text ", and " + , Html.a [ Attr.href "https://github.com/zwilias/elm-json" ] [ Html.text "elm-json" ] + , Html.text ". If you've encountered issues or ideas in those tools that feel worth bringing into Guida, feel free to reference them in a new issue or PR." + ] + ] + , Html.section [] + [ Html.h2 [ Attr.class "scroll-mt-24" ] [ Html.text "Packages" ] + , Html.p [] + [ Html.text "Explore and publish libraries with the " + , Html.a [ Attr.href "https://package.guida-lang.org" ] [ Html.text "Guida package registry" ] + , Html.text "." + ] + ] ] } diff --git a/src/Page/Try.elm b/src/Page/Try.elm index a4ea182..1786852 100644 --- a/src/Page/Try.elm +++ b/src/Page/Try.elm @@ -9,18 +9,21 @@ port module Page.Try exposing ) import Browser +import Components.Button as Button +import Components.ResourcePattern as ResourcePattern +import Components.ThemeToggle as ThemeToggle +import DOM import Elm.Error import Errors import Html exposing (Html) import Html.Attributes as Attr +import Html.Events as Events import Http -import Icon import Json.Decode as Decode import Json.Encode as Encode import Layout.Main as Layout import Route import Session exposing (Session) -import Svg.Attributes as SvgAttr port setEditorContentAndRebuild : String -> Cmd msg @@ -41,6 +44,8 @@ type alias Model = , status : Status , maybeResult : Maybe (Result Elm.Error.Error String) , example : Maybe Route.Example + , mouseX : Int + , mouseY : Int } @@ -63,6 +68,8 @@ init maybeExample = NotAsked , maybeResult = Nothing , example = maybeExample + , mouseX = 0 + , mouseY = 0 } , case maybeExample of Just example -> @@ -85,6 +92,7 @@ type Msg | GotExampleContent (Result Http.Error String) | Rebuild | RebuildResult Encode.Value + | OnMouseOver DOM.Rectangle Int Int update : Msg -> Model -> ( Model, Cmd Msg ) @@ -117,6 +125,9 @@ update msg model = Err _ -> ( { model | status = ProblemsFound, maybeResult = Nothing }, Cmd.none ) + OnMouseOver { left, top } clientX clientY -> + ( { model | mouseX = clientX - round left, mouseY = clientY - round top }, Cmd.none ) + decodeResult : Decode.Decoder (Result Elm.Error.Error String) decodeResult = @@ -143,28 +154,116 @@ view : Session -> (Session.Msg -> msg) -> (Msg -> msg) -> Model -> Browser.Docum view session toSessionMsg toMsg model = { title = case model.example of - Just Route.HelloWorld -> - "Try Guida - Hello World!" + Just Route.Animation -> + "Try Guida - Animation" + + Just Route.Book -> + "Try Guida - Book" Just Route.Buttons -> "Try Guida - Buttons" + Just Route.Cards -> + "Try Guida - Cards" + + Just Route.Clock -> + "Try Guida - Clock" + + Just Route.Crate -> + "Try Guida - Crate" + + Just Route.Cube -> + "Try Guida - Cube" + + Just Route.DragAndDrop -> + "Try Guida - Drag-and-drop" + + Just Route.FirstPerson -> + "Try Guida - First person" + + Just Route.Forms -> + "Try Guida - Forms" + + Just Route.Groceries -> + "Try Guida - Groceries" + + Just Route.Hello -> + "Try Guida - Hello World!" + + Just Route.ImagePreviews -> + "Try Guida - Image previews" + + Just Route.Keyboard -> + "Try Guida - Keyboard" + + Just Route.Mario -> + "Try Guida - Mario" + + Just Route.Mouse -> + "Try Guida - Mouse" + + Just Route.Numbers -> + "Try Guida - Numbers" + + Just Route.Picture -> + "Try Guida - Picture" + + Just Route.Positions -> + "Try Guida - Positions" + + Just Route.Quotes -> + "Try Guida - Quotes" + + Just Route.Shapes -> + "Try Guida - Shapes" + + Just Route.TextFields -> + "Try Guida - Text fields" + + Just Route.Thwomp -> + "Try Guida - Thwomp" + + Just Route.Time -> + "Try Guida - Time" + + Just Route.Triangle -> + "Try Guida - Triangle" + + Just Route.Turtle -> + "Try Guida - Turtle" + + Just Route.Upload -> + "Try Guida - Upload" + Nothing -> "Try Guida!" , body = - Layout.view { sidebarNavigation = [] } session toSessionMsg <| - [ Html.section [ Attr.class "h-screen pt-20 grid grid-cols-none grid-rows-2 sm:grid-cols-2 sm:grid-rows-none" ] + Layout.fullscreenView session toSessionMsg <| + [ Html.section [ Attr.class "h-full grid grid-cols-none grid-rows-2 sm:grid-cols-2 sm:grid-rows-none" ] [ Html.aside - [ Attr.class "overflow-y-auto border-b sm:border-b-0 sm:border-r border-gray-200" + [ Attr.class "grid grid-cols-none grid-rows-2 overflow-y-hidden border-b sm:border-b-0 sm:border-r border-gray-200" ] [ Html.node "wc-codemirror" [ Attr.id "editor" , Attr.class "h-full" - , Attr.attribute "mode" "javascript" + , Attr.attribute "mode" "guida" + , Attr.attribute "theme" + (case Session.theme session of + ThemeToggle.Dark -> + "xq-dark" + + ThemeToggle.Light -> + "xq-light" + ) + ] + [ Html.node "link" [ Attr.rel "stylesheet", Attr.href "/codemirror/theme/xq-dark.css" ] [] + , Html.node "link" [ Attr.rel "stylesheet", Attr.href "/codemirror/theme/xq-light.css" ] [] + ] + , Html.div [] + [ Button.view Button.Button Button.Primary Nothing [ Events.onClick (toMsg Rebuild) ] [ Html.text "rebuild" ] ] - [] ] - , Html.main_ [ Attr.class "overflow-y-auto" ] (outputView toMsg model) + , Html.main_ [ Attr.class "overflow-y-auto bg-white" ] (outputView toMsg model) ] ] } @@ -177,35 +276,40 @@ outputView toMsg model = [ Html.div [ Attr.class "flex h-full" ] [ Html.div [ Attr.class "m-auto w-sm items-center" ] [ Html.div - [ Attr.class "relative rounded-3xl bg-white p-8 ring-1 shadow-2xl ring-gray-900/10 sm:p-10" + [ Attr.class "my-4 group relative flex rounded-2xl bg-zinc-50 transition-shadow hover:shadow-md hover:shadow-zinc-900/5 dark:bg-white/2.5 dark:hover:shadow-black/5" + , Events.on "mouseover" (Decode.map3 (\rectangle clientX clientY -> toMsg (OnMouseOver rectangle clientX clientY)) (DOM.currentTarget DOM.boundingClientRect) (Decode.field "clientX" Decode.int) (Decode.field "clientY" Decode.int)) ] - [ Html.h3 [ Attr.class "text-base/7 font-semibold text-amber-600" ] - [ Html.text "Online Editor" ] - , Html.p - [ Attr.class "mt-6 text-base/7 text-gray-600" - ] - [ Html.text "Write and compile code online!" ] - , Html.ul - [ Attr.attribute "role" "list" - , Attr.class "mt-8 space-y-1" - ] - [ Html.li [] - [ Html.a - [ Attr.href "/examples/hello" - , Attr.class "group w-full flex gap-x-3 rounded-md p-2 text-sm/6 font-semibold text-gray-700 hover:bg-gray-50 hover:text-amber-600" + [ ResourcePattern.view { mouseX = model.mouseX, mouseY = model.mouseY } + , Html.div [ Attr.class "absolute inset-0 rounded-2xl ring-1 ring-zinc-900/7.5 ring-inset group-hover:ring-zinc-900/10 dark:ring-white/10 dark:group-hover:ring-white/20" ] [] + , Html.div [ Attr.class "relative rounded-2xl px-4 pt-8 pb-4" ] + [ Html.h2 [ Attr.class "mt-4 text-sm/7 font-semibold text-zinc-900 dark:text-white" ] + [ Html.text "Online Editor" ] + , Html.p [ Attr.class "mt-1 text-sm text-zinc-600 dark:text-zinc-400" ] + [ Html.text "Write and compile code online!" ] + , Html.ul [] + [ Html.li [] + [ Button.view (Button.Link "/examples/hello") Button.Text Nothing [] [ Html.text "Hello World!" ] ] - [ Icon.logo [ SvgAttr.class "size-6 shrink-0 text-gray-400 group-hover:text-amber-600" ] - , Html.text "Hello World!" + , Html.li [] + [ Button.view (Button.Link "/examples/buttons") Button.Text Nothing [] [ Html.text "Buttons" ] ] - ] - , Html.li [] - [ Html.a - [ Attr.href "/examples/buttons" - , Attr.class "group w-full flex gap-x-3 rounded-md p-2 text-sm/6 font-semibold text-gray-700 hover:bg-gray-50 hover:text-amber-600" + , Html.li [] + [ Button.view (Button.Link "/examples/clock") Button.Text Nothing [] [ Html.text "Clock" ] + ] + , Html.li [] + [ Button.view (Button.Link "/examples/quotes") Button.Text Nothing [] [ Html.text "HTTP" ] ] - [ Icon.logo [ SvgAttr.class "size-6 shrink-0 text-gray-400 group-hover:text-amber-600" ] - , Html.text "Buttons" + , Html.li [] + [ Button.view (Button.Link "/examples/cards") Button.Text Nothing [] [ Html.text "Cards" ] ] + , Html.li [] + [ Button.view (Button.Link "/examples") Button.Text Nothing [] [ Html.text "More!" ] + ] + ] + , Html.p [ Attr.class "mt-1 text-sm text-zinc-600 dark:text-zinc-400" ] + [ Html.text "Explore the " + , Button.view (Button.Link "/docs") Button.Text Nothing [] [ Html.text "official documentation" ] + , Html.text " to learn how to get started with Guida." ] ] ] diff --git a/src/Route.elm b/src/Route.elm index 0af399e..a663c86 100644 --- a/src/Route.elm +++ b/src/Route.elm @@ -1,5 +1,8 @@ module Route exposing - ( Example(..) + ( Command(..) + , DocumentationSection(..) + , Example(..) + , Hint(..) , Route(..) , exampleSrc , fromUrl @@ -11,41 +14,244 @@ import Url.Parser as Parser exposing ((), Parser) type Route = Home - | Docs + | Docs DocumentationSection | Community | Examples | Example Example | Try +type DocumentationSection + = Introduction + | Elm + | GuidaJson + | Commands Command + | Hints Hint + + +type Command + = Repl + | Init + | Make + | Install + | Uninstall + | Bump + | Diff + | Publish + | Format + | Test + + +type Hint + = BadRecursion + | ComparingCustomTypes + | ComparingRecords + | ImplicitCasts + | ImportCycles + | Imports + | InfiniteType + | MissingPatterns + | Optimize + | PortModules + | RecursiveAlias + | Shadowing + | Tuples + | TypeAnnotations + + type Example - = HelloWorld + = Animation + | Book | Buttons + | Cards + | Clock + | Crate + | Cube + | DragAndDrop + | FirstPerson + | Forms + | Groceries + | Hello + | ImagePreviews + | Keyboard + | Mario + | Mouse + | Numbers + | Picture + | Positions + | Quotes + | Shapes + | TextFields + | Thwomp + | Time + | Triangle + | Turtle + | Upload exampleSrc : Example -> String exampleSrc example = case example of - HelloWorld -> - "hello" + Animation -> + "animation" + + Book -> + "book" Buttons -> "buttons" + Cards -> + "cards" + + Clock -> + "clock" + + Crate -> + "crate" + + Cube -> + "cube" + + DragAndDrop -> + "drag-and-drop" + + FirstPerson -> + "first-person" + + Forms -> + "forms" + + Groceries -> + "groceries" + + Hello -> + "hello" + + ImagePreviews -> + "image-previews" + + Keyboard -> + "keyboard" + + Mario -> + "mario" + + Mouse -> + "mouse" + + Numbers -> + "numbers" + + Picture -> + "picture" + + Positions -> + "positions" + + Quotes -> + "quotes" + + Shapes -> + "shapes" + + TextFields -> + "text-fields" + + Thwomp -> + "thwomp" + + Time -> + "time" + + Triangle -> + "triangle" + + Turtle -> + "turtle" + + Upload -> + "upload" + parser : Parser (Route -> a) a parser = Parser.oneOf [ Parser.map Home Parser.top - , Parser.map Docs (Parser.s "docs") + , Parser.map (Docs Introduction) (Parser.s "docs") + , Parser.map (Docs Elm) (Parser.s "docs" Parser.s "elm") + , Parser.map (Docs GuidaJson) (Parser.s "docs" Parser.s "guida-json") + , Parser.map (Docs << Commands) (Parser.s "docs" Parser.s "1.0.0" Parser.s "commands" commandParser) + , Parser.map (Docs << Hints) (Parser.s "docs" Parser.s "1.0.0" Parser.s "hints" hintParser) , Parser.map Community (Parser.s "community") , Parser.map Examples (Parser.s "examples") - , Parser.map (Example HelloWorld) (Parser.s "examples" Parser.s "hello") + , Parser.map (Example Animation) (Parser.s "examples" Parser.s "animation") + , Parser.map (Example Book) (Parser.s "examples" Parser.s "book") , Parser.map (Example Buttons) (Parser.s "examples" Parser.s "buttons") + , Parser.map (Example Cards) (Parser.s "examples" Parser.s "cards") + , Parser.map (Example Clock) (Parser.s "examples" Parser.s "clock") + , Parser.map (Example Crate) (Parser.s "examples" Parser.s "crate") + , Parser.map (Example Cube) (Parser.s "examples" Parser.s "cube") + , Parser.map (Example DragAndDrop) (Parser.s "examples" Parser.s "drag-and-drop") + , Parser.map (Example FirstPerson) (Parser.s "examples" Parser.s "first-person") + , Parser.map (Example Forms) (Parser.s "examples" Parser.s "forms") + , Parser.map (Example Groceries) (Parser.s "examples" Parser.s "groceries") + , Parser.map (Example Hello) (Parser.s "examples" Parser.s "hello") + , Parser.map (Example ImagePreviews) (Parser.s "examples" Parser.s "image-previews") + , Parser.map (Example Keyboard) (Parser.s "examples" Parser.s "keyboard") + , Parser.map (Example Mario) (Parser.s "examples" Parser.s "mario") + , Parser.map (Example Mouse) (Parser.s "examples" Parser.s "mouse") + , Parser.map (Example Numbers) (Parser.s "examples" Parser.s "numbers") + , Parser.map (Example Picture) (Parser.s "examples" Parser.s "picture") + , Parser.map (Example Positions) (Parser.s "examples" Parser.s "positions") + , Parser.map (Example Quotes) (Parser.s "examples" Parser.s "quotes") + , Parser.map (Example Shapes) (Parser.s "examples" Parser.s "shapes") + , Parser.map (Example TextFields) (Parser.s "examples" Parser.s "text-fields") + , Parser.map (Example Thwomp) (Parser.s "examples" Parser.s "thwomp") + , Parser.map (Example Time) (Parser.s "examples" Parser.s "time") + , Parser.map (Example Triangle) (Parser.s "examples" Parser.s "triangle") + , Parser.map (Example Turtle) (Parser.s "examples" Parser.s "turtle") + , Parser.map (Example Upload) (Parser.s "examples" Parser.s "upload") , Parser.map Try (Parser.s "try") ] +commandParser : Parser (Command -> a) a +commandParser = + Parser.oneOf + [ Parser.map Repl (Parser.s "repl") + , Parser.map Init (Parser.s "init") + , Parser.map Make (Parser.s "make") + , Parser.map Install (Parser.s "install") + , Parser.map Uninstall (Parser.s "uninstall") + , Parser.map Bump (Parser.s "bump") + , Parser.map Diff (Parser.s "diff") + , Parser.map Publish (Parser.s "publish") + , Parser.map Format (Parser.s "format") + , Parser.map Test (Parser.s "test") + ] + + +hintParser : Parser (Hint -> a) a +hintParser = + Parser.oneOf + [ Parser.map BadRecursion (Parser.s "bad-recursion") + , Parser.map ComparingCustomTypes (Parser.s "comparing-custom-types") + , Parser.map ComparingRecords (Parser.s "comparing-records") + , Parser.map ImplicitCasts (Parser.s "implicit-casts") + , Parser.map ImportCycles (Parser.s "import-cycles") + , Parser.map Imports (Parser.s "imports") + , Parser.map InfiniteType (Parser.s "infinite-type") + , Parser.map MissingPatterns (Parser.s "missing-patterns") + , Parser.map Optimize (Parser.s "optimize") + , Parser.map PortModules (Parser.s "port-modules") + , Parser.map RecursiveAlias (Parser.s "recursive-alias") + , Parser.map Shadowing (Parser.s "shadowing") + , Parser.map Tuples (Parser.s "tuples") + , Parser.map TypeAnnotations (Parser.s "type-annotations") + ] + + fromUrl : Url -> Maybe Route fromUrl = Parser.parse parser diff --git a/src/Session.elm b/src/Session.elm index 1113726..ed7940b 100644 --- a/src/Session.elm +++ b/src/Session.elm @@ -1,6 +1,7 @@ port module Session exposing ( Msg(..) , Session + , closeMobileNavigation , init , isMobileNavigationOpen , navKey @@ -54,6 +55,11 @@ isMobileNavigationOpen = .isMobileNavigationOpen +closeMobileNavigation : Session -> Session +closeMobileNavigation session = + { session | isMobileNavigationOpen = False } + + -- UPDATE From f3694284af09d65d6ed9b8e3a50e9f9a78caf84f Mon Sep 17 00:00:00 2001 From: Decio Ferreira Date: Wed, 8 Oct 2025 23:16:02 +0100 Subject: [PATCH 03/11] fix npm vulnerabilities --- package-lock.json | 175 ++++++++++++++++++++++++---------------------- 1 file changed, 91 insertions(+), 84 deletions(-) diff --git a/package-lock.json b/package-lock.json index ceba7e0..ffd28ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -576,9 +576,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", - "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "license": "MIT", "dependencies": { @@ -594,19 +594,6 @@ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/@eslint-community/regexpp": { "version": "4.12.1", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", @@ -618,9 +605,9 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", - "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -633,9 +620,9 @@ } }, "node_modules/@eslint/config-array/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { @@ -664,19 +651,22 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz", - "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.0.tgz", + "integrity": "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.16.0" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz", + "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -755,13 +745,16 @@ } }, "node_modules/@eslint/js": { - "version": "9.23.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz", - "integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==", + "version": "9.37.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.37.0.tgz", + "integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { @@ -775,32 +768,19 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz", + "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.13.0", + "@eslint/core": "^0.16.0", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1502,9 +1482,9 @@ "license": "MIT" }, "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", "bin": { @@ -1758,9 +1738,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -2553,20 +2533,20 @@ } }, "node_modules/eslint": { - "version": "9.23.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz", - "integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==", + "version": "9.37.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.37.0.tgz", + "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.2", - "@eslint/config-helpers": "^0.2.0", - "@eslint/core": "^0.12.0", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.4.0", + "@eslint/core": "^0.16.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.23.0", - "@eslint/plugin-kit": "^0.2.7", + "@eslint/js": "9.37.0", + "@eslint/plugin-kit": "^0.4.0", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -2577,9 +2557,9 @@ "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.3.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -2614,9 +2594,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", - "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -2631,13 +2611,13 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -2674,6 +2654,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/eslint/node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2701,15 +2694,15 @@ } }, "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2718,6 +2711,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -2937,14 +2943,15 @@ } }, "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -5892,9 +5899,9 @@ } }, "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", "license": "MIT", "engines": { "node": ">=14.14" From 6c0fe5998cd24e549d0a21134ab2b209d755c365 Mon Sep 17 00:00:00 2001 From: Decio Ferreira Date: Thu, 9 Oct 2025 19:54:14 +0100 Subject: [PATCH 04/11] WIP updated version --- src/Components/Note.elm | 15 + src/Components/Table.elm | 49 +++ src/Icon.elm | 33 ++ src/Main.elm | 2 +- src/Page/Docs.elm | 877 +++++++++++++++++++++++++++++++++++---- src/Page/Examples.elm | 1 - src/Page/NotFound.elm | 26 +- src/Route.elm | 8 +- styles/typography.js | 2 - 9 files changed, 906 insertions(+), 107 deletions(-) create mode 100644 src/Components/Note.elm create mode 100644 src/Components/Table.elm diff --git a/src/Components/Note.elm b/src/Components/Note.elm new file mode 100644 index 0000000..488ca83 --- /dev/null +++ b/src/Components/Note.elm @@ -0,0 +1,15 @@ +module Components.Note exposing (..) + +import Html exposing (Html) +import Html.Attributes as Attr +import Icon +import Svg.Attributes as SvgAttr + + +view : List (Html msg) -> Html msg +view children = + Html.div [ Attr.class "my-6 flex gap-2.5 rounded-2xl border border-amber-500/20 bg-amber-50/50 p-4 text-sm/6 text-amber-900 dark:border-amber-500/30 dark:bg-amber-500/5 dark:text-amber-200 dark:[--tw-prose-links-hover:var(--color-amber-300)] dark:[--tw-prose-links:var(--color-white)]" ] + [ Icon.info [ SvgAttr.class "mt-1 h-4 w-4 flex-none fill-amber-500 stroke-white dark:fill-amber-200/20 dark:stroke-amber-200" ] + , Html.div [ Attr.class "[&>:first-child]:mt-0 [&>:last-child]:mb-0" ] + children + ] diff --git a/src/Components/Table.elm b/src/Components/Table.elm new file mode 100644 index 0000000..66e4d35 --- /dev/null +++ b/src/Components/Table.elm @@ -0,0 +1,49 @@ +module Components.Table exposing (view) + +import Html exposing (Html) +import Html.Attributes as Attr + + +view : List (List (Html msg)) -> List (List (List (Html msg))) -> Html msg +view headers rows = + Html.div [ Attr.class "overflow-hidden shadow-sm outline-1 outline-black/5 sm:rounded-lg dark:shadow-none dark:-outline-offset-1 dark:outline-white/10" ] + [ Html.table [ Attr.class "relative min-w-full divide-y divide-gray-300 dark:divide-white/15" ] + [ Html.thead [ Attr.class "bg-gray-50 dark:bg-gray-800/75" ] [ Html.tr [] (List.indexedMap headerView headers) ] + , Html.tbody [ Attr.class "divide-y divide-gray-200 bg-white dark:divide-white/10 dark:bg-gray-800/50" ] + (List.map rowView rows) + ] + ] + + +headerView : Int -> List (Html msg) -> Html msg +headerView index children = + Html.th + [ Attr.scope "col" + , Attr.class + (if index == 0 then + "px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-white" + + else + "px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-200" + ) + ] + children + + +rowView : List (List (Html msg)) -> Html msg +rowView row = + Html.tr [] (List.indexedMap cellView row) + + +cellView : Int -> List (Html msg) -> Html msg +cellView index children = + Html.td + [ Attr.class + (if index == 0 then + "py-4 pr-3 pl-4 text-sm whitespace-nowrap text-gray-500 sm:pl-6 dark:text-white" + + else + "px-3 py-4 text-sm whitespace-nowrap text-gray-500 dark:text-gray-400" + ) + ] + children diff --git a/src/Icon.elm b/src/Icon.elm index 6a5c33b..13899e4 100644 --- a/src/Icon.elm +++ b/src/Icon.elm @@ -5,6 +5,7 @@ module Icon exposing , cross , discord , github + , info , logo , xMark ) @@ -108,6 +109,38 @@ github attrs = ] +info : List (Html.Attribute msg) -> Svg msg +info attrs = + Svg.svg + (SvgAttr.viewBox "0 0 16 16" + :: Aria.ariaHidden True + :: attrs + ) + [ Svg.circle + [ SvgAttr.cx "8" + , SvgAttr.cy "8" + , SvgAttr.r "8" + , SvgAttr.strokeWidth "0" + ] + [] + , Svg.path + [ SvgAttr.fill "none" + , SvgAttr.strokeLinecap "round" + , SvgAttr.strokeLinejoin "round" + , SvgAttr.strokeWidth "1.5" + , SvgAttr.d "M6.75 7.75h1.5v3.5" + ] + [] + , Svg.circle + [ SvgAttr.cx "8" + , SvgAttr.cy "4" + , SvgAttr.r ".5" + , SvgAttr.fill "none" + ] + [] + ] + + discord : List (Html.Attribute msg) -> Svg msg discord attrs = Svg.svg diff --git a/src/Main.elm b/src/Main.elm index 34e97e6..8246674 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -177,7 +177,7 @@ view : Model -> Browser.Document Msg view model = case model.currentPage of NotFound -> - viewPage never NotFound.view + NotFound.view model.session SessionMsg Home -> Home.view model.session SessionMsg diff --git a/src/Page/Docs.elm b/src/Page/Docs.elm index 138e9a3..7a5577b 100644 --- a/src/Page/Docs.elm +++ b/src/Page/Docs.elm @@ -7,8 +7,10 @@ module Page.Docs exposing import Browser import Components.Button as Button exposing (Type(..)) import Components.CodeBlock as CodeBlock +import Components.Note as Note import Components.Properties as Properties import Components.References as References +import Components.Table as Table import Html exposing (Html) import Html.Attributes as Attr import Layout.Main as Layout @@ -42,33 +44,588 @@ view session toSessionMsg model = Layout.view { sidebarNavigation = sidebarNavigation model } session toSessionMsg <| case model.section of Route.Introduction -> - [] + introductionView - Route.Elm -> - [] + Route.Syntax -> + syntaxView + + Route.FromJavaScriptOrElm -> + fromJavaScriptOrElmView Route.GuidaJson -> - [ Html.h1 [] [ Html.text "guida.json" ] - , Html.p [] - [ Html.text "The " - , Html.code [] [ Html.text "guida.json" ] - , Html.text " describes your project." - ] - , Html.p [] [ Html.text "There are two different types of project: applications and packages." ] - , Html.p [] - [ Html.text "Depending on the type of project, the " - , Html.code [] [ Html.text "guida.json" ] - , Html.text " looks slightly different." - ] - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Application" ] - , Html.p [] - [ Html.text "When running " - , Html.code [] [ Html.text "guida init" ] - , Html.text " a " - , Html.code [] [ Html.text "guida.json" ] - , Html.text " file is generated with the following content:" - ] - , CodeBlock.view """{ + guidaJsonView + + Route.Records -> + recordsView + + Route.Commands command -> + commandView command + + Route.Hints hint -> + hintView hint + } + + +introductionView : List (Html msg) +introductionView = + [ Html.h1 [] [ Html.text "An Introduction to Guida" ] + , Html.p [] + [ Html.strong [] [ Html.text "Guida is a functional language that compiles to JavaScript." ] + , Html.text " It helps you make websites and web apps. It has a strong emphasis on simplicity and quality tooling." + ] + , Html.p [] [ Html.text "This guide will:" ] + , Html.ul [] + [ Html.li [] [ Html.text "Teach you the fundamentals of programming in Guida." ] + , Html.li [] + [ Html.text "Show you how to make interactive apps with " + , Html.strong [] [ Button.view (Button.Link "https://guide.elm-lang.org/architecture") Button.Text Nothing [] [ Html.text "The Elm Architecture" ] ] + , Html.text "." + ] + , Html.li [] [ Html.text "Emphasize principles and patterns that generalize to programming in any language." ] + ] + , Html.p [] + [ Html.text "By the end we hope you will not only be able to create great web apps in Guida, but also understand the core ideas and patterns that make Guida nice to use." + ] + , Html.p [] + [ Html.text "If you are on the fence, we can safely guarantee that if you give Guida a shot and actually make a project in it, you will end up writing better JavaScript code. The ideas transfer pretty easily!" + ] + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "A Quick Sample" ] + , Html.p [] + [ Html.text "Here is a little program that lets you increment and decrement a number:" + ] + , CodeBlock.view """module Main exposing (main) + +import Browser +import Html exposing (Html, button, div, text) +import Html.Events exposing (onClick) + + +main = + Browser.sandbox { init = init, update = update, view = view } + + +type alias Model = + Int + + +init : Model +init = + 0 + + +type Msg + = Increment + | Decrement + + +update : Msg -> Model -> Model +update msg model = + case msg of + Increment -> + model + 1 + + Decrement -> + model - 1 + + +view : Model -> Html Msg +view model = + div [] + [ button [ onClick Decrement ] [ text "-" ] + , div [] [ text (String.fromInt model) ] + , button [ onClick Increment ] [ text "+" ] + ]""" + , Html.p [] + [ Html.text "Try it out in the online editor " + , Button.view (Button.Link "/examples/buttons") Button.Text Nothing [] [ Html.text "here" ] + , Html.text "." + ] + , Html.p [] + [ Html.text "The code can definitely look unfamiliar at first, so we will get into how this example works soon!" + ] + , Html.h2 [ Attr.class "scroll-mt-12" ] + [ Html.text "Why a functional " + , Html.em [] [ Html.text "language" ] + , Html.text "?" + ] + , Html.p [] + [ Html.text "You can get some benefits from programming in a functional style, but there are some things you can only get from a functional language like Guida:" + ] + , Html.ul [] + [ Html.li [] [ Html.text "No runtime errors in practice." ] + , Html.li [] [ Html.text "Friendly error messages." ] + , Html.li [] [ Html.text "Reliable refactoring." ] + , Html.li [] [ Html.text "Automatically enforced semantic versioning for all Guida packages." ] + ] + , Html.p [] + [ Html.text "No combination of JS libraries can give you all of these guarantees. They come from the design of the language itself! And thanks to these guarantees, it is quite common for Guida programmers to say they never felt so " + , Html.strong [] [ Html.text "confident" ] + , Html.text " while programming. Confident to add features quickly. Confident to refactor thousands of lines. But without the background anxiety that you missed something important!" + ] + , Html.p [] + [ Html.text "Guida has a huge emphasis on making it easy to learn and use, so give Guida a shot and see what you think. We hope you will be pleasantly surprised!" + ] + , References.view + [ "https://guide.elm-lang.org" + ] + ] + + +syntaxView : List (Html msg) +syntaxView = + [ Html.h1 [] [ Html.text "Syntax" ] + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Comments" ] + , CodeBlock.view """-- a single line comment + +{- a multiline comment + {- can be nested -} +-}""" + , Html.p [] [ Html.text "Here's a handy trick that every Guida programmer should know:" ] + , CodeBlock.view """{--} +add x y = x + y +--}""" + , Html.p [] + [ Html.text "Just add or remove the " + , Html.code [] [ Html.text "}" ] + , Html.text " on the first line and you'll toggle between commented and uncommented!" + ] + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Literals" ] + , CodeBlock.view """-- Boolean +True : Bool +False : Bool + +42 : number -- Int or Float depending on usage +3.14 : Float + +'a' : Char +"abc" : String + +-- multi-line String +\"\"\" +This is useful for holding JSON or other +content that has "quotation marks". +\"\"\"""" + , Html.p [] [ Html.text "Typical manipulation of literals:" ] + , CodeBlock.view """True && not (True || False) +(2 + 4) * (4^2 - 9) +"abc" ++ "def\"""" + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Lists" ] + , Html.p [] [ Html.text "Here are three things that are equivalent:" ] + , CodeBlock.view """[ 1, 2, 3, 4 ] +1 :: [ 2, 3, 4 ] +1 :: 2 :: 3 :: 4 :: []""" + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Conditionals" ] + , CodeBlock.view "if powerLevel > 9000 then \"OVER 9000!!!\" else \"meh\"" + , Html.p [] [ Html.text "If you need to branch on many different conditions, you just chain this construct together." ] + , CodeBlock.view """if key == 40 then + n + 1 + + else if key == 38 then + n - 1 + + else + n""" + , Html.p [] [ Html.text "You can also have conditional behavior based on the structure of custom types and literals:" ] + , CodeBlock.view """ case maybeList of + Just xs -> xs + Nothing -> [] + + case xs of + [] -> + Nothing + first :: rest -> + Just (first, rest) + + case n of + 0 -> 1 + 1 -> 1 + _ -> fib (n-1) + fib (n-2)""" + , Html.p [] [ Html.text "Each pattern is indentation sensitive, meaning that you have to align all of your patterns." ] + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Records" ] + , Html.p [] + [ Html.text "For more explanation of Guida's record system, see " + , Button.view (Button.Link "/docs/records") Button.Text Nothing [] [ Html.text "this overview" ] + , Html.text ", or " + , Button.view (Button.Link "https://elm-lang.org/news/0.7") Button.Text Nothing [] [ Html.text "Elm's initial announcement." ] + ] + , CodeBlock.view """-- create records +origin = { x = 0, y = 0 } +point = { x = 3, y = 4 } + +-- access fields +origin.x == 0 +point.x == 3 + +-- field access function +List.map .x [ origin, point ] == [ 0, 3 ] + +-- update a field +{ point | x = 6 } == { x = 6, y = 4 } + +-- update many fields +{ point | x = point.x + 1, y = point.y + 1 }""" + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Functions" ] + , CodeBlock.view """square n = + n^2 + +hypotenuse a b = + sqrt (square a + square b) + +distance (a,b) (x,y) = + hypotenuse (a - x) (b - y)""" + , Html.p [] [ Html.text "Anonymous functions:" ] + , CodeBlock.view """square = + \\n -> n^2 + +squares = + List.map (\\n -> n^2) (List.range 1 100)""" + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Operators" ] + , Html.p [] + [ Html.text "In addition to the normal math operations for addition and subtraction, we have the " + , Button.view (Button.Link "https://package.elm-lang.org/packages/elm/core/latest/Basics#(%3C|)") Button.Text Nothing [] [ Html.text "(<|)" ] + , Html.text " and " + , Button.view (Button.Link "https://package.elm-lang.org/packages/elm/core/latest/Basics#(|%3E)") Button.Text Nothing [] [ Html.text "(|>)" ] + , Html.text " operators. They are aliases for function application, allowing you to write fewer parentheses." + ] + , CodeBlock.view """viewNames1 names = + String.join ", " (List.sort names) + +viewNames2 names = + names + |> List.sort + |> String.join ", " + +-- (arg |> func) is the same as (func arg) +-- Just keep repeating that transformation!""" + , Html.p [] [ Html.text "Historical note: this is borrowed from F#, inspired by Unix pipes." ] + , Html.p [] + [ Html.text "Relatedly, " + , Button.view (Button.Link "https://package.elm-lang.org/packages/elm/core/latest/Basics#(%3C%3C)") Button.Text Nothing [] [ Html.text "(<<)" ] + , Html.text " and " + , Button.view (Button.Link "https://package.elm-lang.org/packages/elm/core/latest/Basics#(%3E%3E)") Button.Text Nothing [] [ Html.text "(>>)" ] + , Html.text " are function composition operators." + ] + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Let Expressions" ] + , Html.p [] + [ Html.code [] [ Html.text "let" ] + , Html.text " these values be defined " + , Html.code [] [ Html.text "in" ] + , Html.text " this specific expression." + ] + , CodeBlock.view """ let + twentyFour = + 3 * 8 + + sixteen = + 4 ^ 2 + in + twentyFour + sixteen""" + , Html.p [] + [ Html.text "This is useful when an expression is getting large. You can make a " + , Html.code [] [ Html.text "let" ] + , Html.text " to break it into smaller definitions and put them all together " + , Html.code [] [ Html.text "in" ] + , Html.text " a smaller expression." + ] + , Html.p [] [ Html.text "You can define functions and use \"destructuring assignment\" in let expressions too." ] + , CodeBlock.view """ let + ( three, four ) = + ( 3, 4 ) + + hypotenuse a b = + sqrt (a^2 + b^2) + in + hypotenuse three four""" + , Html.p [] [ Html.text "Let-expressions are indentation sensitive, so each definition must align with the one above it." ] + , Html.p [] [ Html.text "Finally, you can add type annotations in let-expressions." ] + , CodeBlock.view """ let + name : String + name = + "Hermann" + + increment : Int -> Int + increment n = + n + 1 + in + increment 10""" + , Html.p [] [ Html.text "It is best to only do this on concrete types. Break generic functions into their own top-level definitions." ] + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Applying Functions" ] + , CodeBlock.view """-- alias for appending lists and two lists +append xs ys = xs ++ ys +xs = [1,2,3] +ys = [4,5,6] + +-- All of the following expressions are equivalent: +a1 = append xs ys +a2 = xs ++ ys + +b2 = (++) xs ys + +c1 = (append xs) ys +c2 = ((++) xs) ys""" + , Html.p [] [ Html.text "The basic arithmetic infix operators all figure out what type they should have automatically." ] + , CodeBlock.view """23 + 19 : number +2.0 + 1 : Float + +6 * 7 : number +10 * 4.2 : Float + +100 // 2 : Int +1 / 2 : Float""" + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Modules" ] + , CodeBlock.view """module MyModule exposing (..) + +-- qualified imports +import List -- List.map, List.foldl +import List as L -- L.map, L.foldl + +-- open imports +import List exposing (..) -- map, foldl, concat, ... +import List exposing ( map, foldl ) -- map, foldl + +import Maybe exposing ( Maybe ) -- Maybe +import Maybe exposing ( Maybe(..) ) -- Maybe, Just, Nothing""" + , Html.p [] + [ Html.text "Qualified imports are preferred. Module names must match their file name, so module " + , Html.code [] [ Html.text "Parser.Utils" ] + , Html.text " needs to be in file " + , Html.code [] [ Html.text "Parser/Utils.guida" ] + , Html.text "." + ] + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Type Annotations" ] + , CodeBlock.view """answer : Int +answer = + 42 + +factorial : Int -> Int +factorial n = + List.product (List.range 1 n) + +distance : { x : Float, y : Float } -> Float +distance {x,y} = + sqrt (x^2 + y^2)""" + , Html.p [] + [ Html.text "Learn how to read types and use type annotations " + , Button.view (Button.Link "/docs/types/reading-types") Button.Text Nothing [] [ Html.text "here" ] + , Html.text "." + ] + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Type Aliases" ] + , CodeBlock.view """type alias Name = String +type alias Age = Int + +info : (Name,Age) +info = + ("Steve", 28) + +type alias Point = { x:Float, y:Float } + +origin : Point +origin = + { x = 0, y = 0 }""" + , Html.p [] + [ Html.text "Learn more about type aliases " + , Button.view (Button.Link "/docs/types/type-aliases") Button.Text Nothing [] [ Html.text "here" ] + , Html.text "." + ] + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Custom Types" ] + , CodeBlock.view """type User + = Regular String Int + | Visitor String""" + , Html.p [] + [ Html.text "Not sure what this means? Read " + , Button.view (Button.Link "/docs/types/custom-types") Button.Text Nothing [] [ Html.text "this" ] + , Html.text "!" + ] + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "JavaScript Interop" ] + , CodeBlock.view """-- incoming values +port prices : (Float -> msg) -> Sub msg + +-- outgoing values +port time : Float -> Cmd msg""" + , Html.p [] [ Html.text "From JS, you talk to these ports like this:" ] + , CodeBlock.view """var app = Elm.Example.init(); + +app.ports.prices.send(42); +app.ports.prices.send(13); + +app.ports.time.subscribe(callback); +app.ports.time.unsubscribe(callback);""" + , Html.p [] + [ Html.text "Read more about JavaScript interop " + , Button.view (Button.Link "/docs/interop") Button.Text Nothing [] [ Html.text "here" ] + , Html.text "." + ] + , References.view + [ "https://elm-lang.org/docs/syntax" + , "https://guide.elm-lang.org/core_language" + ] + ] + + +fromJavaScriptOrElmView : List (Html msg) +fromJavaScriptOrElmView = + [ Html.h1 [] [ Html.text "From JavaScript or Elm?" ] + , Html.p [] [ Html.text "The following tables show side-by-side mappings between JavaScript, Elm and Guida. A lot of things are very similar, especially once you get used to the relatively minor syntactic difference." ] + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Literals" ] + , Table.view + [ [ Html.text "JavaScript" ] + , [ Html.text "Elm" ] + , [ Html.text "Guida" ] + ] + [ [ [ Html.code [] [ Html.text "3" ] ] + , [ Html.code [] [ Html.text "3" ] ] + , [ Html.code [] [ Html.text "3" ] ] + ] + , [ [ Html.code [] [ Html.text "3.1415" ] ] + , [ Html.code [] [ Html.text "3.1415" ] ] + , [ Html.code [] [ Html.text "3.1415" ] ] + ] + , [ [ Html.code [] [ Html.text "\"Hello world!\"" ] ] + , [ Html.code [] [ Html.text "\"Hello world!\"" ] ] + , [ Html.code [] [ Html.text "\"Hello world!\"" ] ] + ] + , [ [ Html.code [] [ Html.text "`multiline string`" ] ] + , [ Html.code [] [ Html.text "\"\"\"multiline string\"\"\"" ] ] + , [ Html.code [] [ Html.text "\"\"\"multiline string\"\"\"" ] ] + ] + , [ [ Html.code [] [ Html.text "'Hello world!'" ] ] + , [ Html.text "Cannot use single quotes for strings" ] + , [ Html.text "Cannot use single quotes for strings" ] + ] + , [ [ Html.text "No distinction between characters and strings" ] + , [ Html.code [] [ Html.text "'a'" ] ] + , [ Html.code [] [ Html.text "'a'" ] ] + ] + , [ [ Html.code [] [ Html.text "true" ] ] + , [ Html.code [] [ Html.text "True" ] ] + , [ Html.code [] [ Html.text "True" ] ] + ] + ] + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Objects / Records" ] + , Table.view + [ [ Html.text "JavaScript" ] + , [ Html.text "Elm" ] + , [ Html.text "Guida" ] + ] + [ [ [ Html.code [] [ Html.text "{ x: 3, y: 4 }" ] ] + , [ Html.code [] [ Html.text "{ x = 3, y = 4 }" ] ] + , [ Html.code [] [ Html.text "{ x = 3, y = 4 }" ] ] + ] + , [ [ Html.code [] [ Html.text "point.x" ] ] + , [ Html.code [] [ Html.text "point.x" ] ] + , [ Html.code [] [ Html.text "point.x" ] ] + ] + , [ [ Html.code [] [ Html.text "point.x = 42" ] ] + , [ Html.code [] [ Html.text "{ point | x = 42 }" ] ] + , [ Html.code [] [ Html.text "{ point | x = 42 }" ] ] + ] + ] + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Functions" ] + , Table.view + [ [ Html.text "JavaScript" ] + , [ Html.text "Elm" ] + , [ Html.text "Guida" ] + ] + [ [ [ Html.code [] [ Html.text "function(x, y) { return x + y; }" ] ] + , [ Html.code [] [ Html.text "\\x y -> x + y" ] ] + , [ Html.code [] [ Html.text "\\x y -> x + y" ] ] + ] + , [ [ Html.code [] [ Html.text "Math.max(3, 4)" ] ] + , [ Html.code [] [ Html.text "max 3 4" ] ] + , [ Html.code [] [ Html.text "max 3 4" ] ] + ] + , [ [ Html.code [] [ Html.text "Math.min(1, Math.pow(2, 4))" ] ] + , [ Html.code [] [ Html.text "min 1 (2^4)" ] ] + , [ Html.code [] [ Html.text "min 1 (2^4)" ] ] + ] + , [ [ Html.code [] [ Html.text "numbers.map(Math.sqrt)" ] ] + , [ Html.code [] [ Html.text "List.map sqrt numbers" ] ] + , [ Html.code [] [ Html.text "List.map sqrt numbers" ] ] + ] + , [ [ Html.code [] [ Html.text "points.map(function(p) { return p.x })" ] ] + , [ Html.code [] [ Html.text "List.map .x points" ] ] + , [ Html.code [] [ Html.text "List.map .x points" ] ] + ] + ] + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Control Flow" ] + , Table.view + [ [ Html.text "JavaScript" ] + , [ Html.text "Elm" ] + , [ Html.text "Guida" ] + ] + [ [ [ Html.code [] [ Html.text "3 > 2 ? 'cat' : 'dog'" ] ] + , [ Html.code [] [ Html.text "if 3 > 2 then \"cat\" else \"dog\"" ] ] + , [ Html.code [] [ Html.text "if 3 > 2 then \"cat\" else \"dog\"" ] ] + ] + , [ [ Html.code [] [ Html.text "var x = 42; ..." ] ] + , [ Html.code [] [ Html.text "let x = 42 in ..." ] ] + , [ Html.code [] [ Html.text "let x = 42 in ..." ] ] + ] + , [ [ Html.code [] [ Html.text "return 42" ] ] + , [ Html.text "Everything is an expression, no need for return" ] + , [ Html.text "Everything is an expression, no need for return" ] + ] + ] + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Strings" ] + , Table.view + [ [ Html.text "JavaScript" ] + , [ Html.text "Elm" ] + , [ Html.text "Guida" ] + ] + [ [ [ Html.code [] [ Html.text "'abc' + '123'" ] ] + , [ Html.code [] [ Html.text "\"abc\" ++ \"123\"" ] ] + , [ Html.code [] [ Html.text "\"abc\" ++ \"123\"" ] ] + ] + , [ [ Html.code [] [ Html.text "'abc'.length" ] ] + , [ Html.code [] [ Html.text "String.length \"abc\"" ] ] + , [ Html.code [] [ Html.text "String.length \"abc\"" ] ] + ] + , [ [ Html.code [] [ Html.text "'abc'.toUpperCase()" ] ] + , [ Html.code [] [ Html.text "String.toUpper \"abc\"" ] ] + , [ Html.code [] [ Html.text "String.toUpper \"abc\"" ] ] + ] + , [ [ Html.code [] [ Html.text "'abc' + 123" ] ] + , [ Html.code [] [ Html.text "\"abc\" ++ String.fromInt 123" ] ] + , [ Html.code [] [ Html.text "\"abc\" ++ String.fromInt 123" ] ] + ] + ] + , References.view + [ "https://elm-lang.org/docs/from-javascript" + ] + ] + + +guidaJsonView : List (Html msg) +guidaJsonView = + [ Html.h1 [] [ Html.text "guida.json" ] + , Html.p [] + [ Html.text "The " + , Html.code [] [ Html.text "guida.json" ] + , Html.text " describes your project. There are two different types of project: applications and packages." + ] + , Html.p [] + [ Html.text "This file is generated by the " + , Button.view (Button.Link "/docs/1.0.0/commands/init") Button.Text Nothing [] [ Html.text "guida init" ] + , Html.text " command, to simplify setup. To generate an application, run " + , Html.code [] [ Html.text "guida init" ] + , Html.text ". To generate a package, run " + , Html.code [] [ Html.text "guida init --package" ] + , Html.text "." + ] + , Html.p [] + [ Html.text "Depending on the type of project, the " + , Html.code [] [ Html.text "guida.json" ] + , Html.text " looks slightly different." + ] + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Application" ] + , Html.p [] + [ Html.text "When running " + , Html.code [] [ Html.text "guida init" ] + , Html.text " a " + , Html.code [] [ Html.text "guida.json" ] + , Html.text " file is generated with the following content:" + ] + , CodeBlock.view """{ "type": "application", "source-directories": [ "src" @@ -97,61 +654,74 @@ view session toSessionMsg model = } } }""" - , Properties.view - [ { name = "\"type\"" - , type_ = Nothing - , children = - [ Html.text "Either " - , Html.code [] [ Html.text "\"application\"" ] - , Html.text " or " - , Html.code [] [ Html.text "\"package\"" ] - , Html.text ". All the other fields are based on this choice!" - ] - } - , { name = "\"source-directories\"" - , type_ = Nothing - , children = - [ Html.text "A list of directories where Guida code lives. Most projects just use " - , Html.code [] [ Html.text "\"src\"" ] - , Html.text " for everything." - ] - } - , { name = "\"guida-version\"" - , type_ = Nothing - , children = - [ Html.text "The exact version of Guida this builds with. Should be " - , Html.code [] [ Html.text "\"1.0.0\"" ] - , Html.text " for most people!" - ] - } - , { name = "\"dependencies\"" - , type_ = Nothing - , children = - [ Html.text "All the packages you depend upon. We use exact versions, so your " - , Html.code [] [ Html.text "guida.json" ] - , Html.text " file doubles as a \"lock file\" that ensures reliable builds." - , Html.br [] [] - , Html.text "You can use modules from any `\"direct\"` dependency in your code. Some `\"direct\"` dependencies have their own dependencies that folks typically do not care about. These are the `\"indirect\"` dependencies. They are listed explicitly so that (1) builds are reproducible and (2) you can easily review the quantity and quality of dependencies." - , Html.br [] [] - , Html.text "**Note:** We plan to eventually have a screen in `reactor` that helps add, remove, and upgrade packages. It can sometimes be tricky to keep all of the constraints happy, so we think having a UI will help a lot. If you get into trouble in the meantime, adding things back one-by-one often helps, and I hope you do not get into trouble!" - ] - } - , { name = "\"test-dependencies\"" - , type_ = Nothing - , children = - [ Html.text "All the packages that you use in `tests/` with `guida test` but not in the application you actually want to ship. This also uses exact versions to make tests more reliable." - ] - } - ] - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Package" ] - , Html.p [] - [ Html.text "When running " - , Html.code [] [ Html.text "guida init --package" ] - , Html.text " a " - , Html.code [] [ Html.text "guida.json" ] - , Html.text " file is generated with the following content:" - ] - , CodeBlock.view """{ + , Properties.view + [ { name = "\"type\"" + , type_ = Nothing + , children = + [ Html.text "Either " + , Html.code [] [ Html.text "\"application\"" ] + , Html.text " or " + , Html.code [] [ Html.text "\"package\"" ] + , Html.text ". All the other fields are based on this choice." + ] + } + , { name = "\"source-directories\"" + , type_ = Nothing + , children = + [ Html.text "A list of directories where Guida code lives. Most projects just use " + , Html.code [] [ Html.text "\"src\"" ] + , Html.text " for everything." + ] + } + , { name = "\"guida-version\"" + , type_ = Nothing + , children = + [ Html.text "The exact version of Guida this builds with. Should be " + , Html.code [] [ Html.text "\"1.0.0\"" ] + , Html.text " for most people!" + ] + } + , { name = "\"dependencies\"" + , type_ = Nothing + , children = + [ Html.p [] + [ Html.text "All the packages you depend upon. We use exact versions, so your " + , Html.code [] [ Html.text "guida.json" ] + , Html.text " file doubles as a \"lock file\" that ensures reliable builds." + ] + , Html.p [] + [ Html.text "You can use modules from any " + , Html.code [] [ Html.text "\"direct\"" ] + , Html.text " dependency in your code. Some " + , Html.code [] [ Html.text "\"direct\"" ] + , Html.text " dependencies have their own dependencies that folks typically do not care about. These are the " + , Html.code [] [ Html.text "\"indirect\"" ] + , Html.text " dependencies. They are listed explicitly so that (1) builds are reproducible and (2) you can easily review the quantity and quality of dependencies." + ] + ] + } + , { name = "\"test-dependencies\"" + , type_ = Nothing + , children = + [ Html.p [] + [ Html.text "All the packages that you use in " + , Html.code [] [ Html.text "tests/" ] + , Html.text " with " + , Html.code [] [ Html.text "guida test" ] + , Html.text " but not in the application you actually want to ship. This also uses exact versions to make tests more reliable." + ] + ] + } + ] + , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Package" ] + , Html.p [] + [ Html.text "When running " + , Html.code [] [ Html.text "guida init --package" ] + , Html.text " a " + , Html.code [] [ Html.text "guida.json" ] + , Html.text " file is generated with the following content:" + ] + , CodeBlock.view """{ "type": "package", "name": "author/project", "summary": "helpful summary of your project, less than 80 characters", @@ -166,19 +736,144 @@ view session toSessionMsg model = "elm-explorations/test": "2.2.0 <= v < 3.0.0" } }""" - , References.view - [ "https://github.com/elm/compiler/blob/0.19.1/docs/elm.json/application.md" - , "https://github.com/elm/compiler/blob/0.19.1/docs/elm.json/package.md" - , "https://gren-lang.org/book/appendix/gren_json/" - ] + , Properties.view + [ { name = "\"type\"" + , type_ = Nothing + , children = + [ Html.p [] + [ Html.text "Either " + , Html.code [] [ Html.text "\"application\"" ] + , Html.text " or " + , Html.code [] [ Html.text "\"package\"" ] + , Html.text ". All the other fields are based on this choice." ] + ] + } + , { name = "\"name\"" + , type_ = Nothing + , children = + [ Html.p [] + [ Html.text "The name of a GitHub repo like " + , Html.code [] [ Html.text "\"elm/core\"" ] + , Html.text " or " + , Html.code [] [ Html.text "\"rtfeldman/elm-css\"" ] + , Html.text "." + ] + , Note.view + [ Html.strong [] [ Html.text "Note:" ] + , Html.text " We currently only support GitHub repos to ensure that there are no author name collisions. This seems like a pretty tricky problem to solve in a pleasant way. For example, do we have to keep an author name registry and give them out as we see them? But if someone is the same person on two platforms? And how to make this all happen in a way this is really nice for typical Elm users? Etc. So adding other hosting endpoints is harder than it sounds." + ] + ] + } + , { name = "\"summary\"" + , type_ = Nothing + , children = + [ Html.p [] + [ Html.text "A short summary that will appear on " + , Button.view (Button.Link "https://package.guida-lang.org") Button.Text Nothing [] [ Html.text "package.guida-lang.org" ] + , Html.text " that describes what the package is for. Must be under 80 characters." + ] + ] + } + , { name = "\"license\"" + , type_ = Nothing + , children = + [ Html.p [] + [ Html.text "An OSI approved SPDX code like " + , Html.code [] [ Html.text "\"BSD-3-Clause\"" ] + , Html.text " or " + , Html.code [] [ Html.text "\"MIT\"" ] + , Html.text ". These are the two most common licenses in the Elm ecosystem, but you can see the full list of options " + , Button.view (Button.Link "https://spdx.org/licenses") Button.Text Nothing [] [ Html.text "here" ] + , Html.text "." + ] + ] + } + , { name = "\"version\"" + , type_ = Nothing + , children = + [ Html.p [] + [ Html.text "All packages start at " + , Html.code [] [ Html.text "\"1.0.0\"" ] + , Html.text " and from there, Guida automatically enforces semantic versioning by comparing API changes." + ] + , Html.p [] + [ Html.text "So if you make a PATCH change and call " + , Html.code [] [ Html.text "guida bump" ] + , Html.text " it will update you to " + , Html.code [] [ Html.text "\"1.0.1\"" ] + , Html.text ". And if you then decide to remove a function (a MAJOR change) and call " + , Html.code [] [ Html.text "guida bump" ] + , Html.text " it will update you to " + , Html.code [] [ Html.text "\"2.0.0\"" ] + , Html.text ". Etc." + ] + ] + } + , { name = "\"exposed-modules\"" + , type_ = Nothing + , children = + [ Html.p [] + [ Html.text "A list of modules that will be exposed to people using your package. The order you list them will be the order they appear on " + , Button.view (Button.Link "https://package.guida-lang.org") Button.Text Nothing [] [ Html.text "package.guida-lang.org" ] + , Html.text "." + ] + , Html.p [] + [ Html.strong [] [ Html.text "Note:" ] + , Html.text " If you have five or more modules, you can use a labelled list like " + , Button.view (Button.Link "https://github.com/elm/core/blob/1.0.5/elm.json") Button.Text Nothing [] [ Html.text "this" ] + , Html.text ". We show the labels on the package website to help people sort through larger packages with distinct categories. Labels must be under 20 characters." + ] + ] + } + , { name = "\"guida-version\"" + , type_ = Nothing + , children = + [ Html.p [] + [ Html.text "The range of Elm compilers that work with your package. Right now " + , Html.code [] [ Html.text "\"0.19.0 <= v < 0.20.0\"" ] + , Html.text " is always what you want for this." + ] + ] + } + , { name = "\"dependencies\"" + , type_ = Nothing + , children = + [ Html.p [] + [ Html.text "A list of packages that you depend upon. In each application, there can only be one version of each package, so wide ranges are great. Fewer dependencies is even better though!" + ] + , Note.view + [ Html.strong [] [ Html.text "Note:" ] + , Html.text " Dependency ranges should only express tested ranges. It is not nice to use optimistic ranges and end up causing build failures for your users down the line. Eventually we would like to have an automated system that tries to build and test packages as new packages come out. If it all works, we could send a PR to the author widening the range." + ] + ] + } + , { name = "\"test-dependencies\"" + , type_ = Nothing + , children = + [ Html.text "Dependencies that are only used in the " + , Html.code [] [ Html.text "tests/" ] + , Html.text " directory by " + , Html.code [] [ Html.text "guida test" ] + , Html.text ". Values from these packages will not appear in any final build artifacts." + ] + } + ] + , References.view + [ "https://github.com/elm/compiler/blob/0.19.1/docs/elm.json/application.md" + , "https://github.com/elm/compiler/blob/0.19.1/docs/elm.json/package.md" + , "https://gren-lang.org/book/appendix/gren_json/" + ] + ] - Route.Commands command -> - commandView command - Route.Hints hint -> - hintView hint - } +recordsView : List (Html msg) +recordsView = + [ Html.h1 [] [ Html.text "Records" ] + , References.view + [ "https://elm-lang.org/docs/records" + ] + ] commandView : Route.Command -> List (Html msg) @@ -335,8 +1030,10 @@ sidebarNavigation model = [ { title = "Guide" , links = [ { title = "Introduction", href = "/docs", active = model.section == Route.Introduction } - , { title = "Elm", href = "/docs/elm", active = model.section == Route.Elm } + , { title = "Syntax", href = "/docs/syntax", active = model.section == Route.Syntax } + , { title = "From JavaScript or Elm?", href = "/docs/from-javascript-or-elm", active = model.section == Route.FromJavaScriptOrElm } , { title = "guida.json", href = "/docs/guida-json", active = model.section == Route.GuidaJson } + , { title = "Records", href = "/docs/records", active = model.section == Route.Records } ] } , { title = "Commands" diff --git a/src/Page/Examples.elm b/src/Page/Examples.elm index baaf5c4..2796036 100644 --- a/src/Page/Examples.elm +++ b/src/Page/Examples.elm @@ -4,7 +4,6 @@ import Browser import Components.Button as Button import Html exposing (Html) import Html.Attributes as Attr -import Html.Attributes.Aria as Aria import Layout.Main as Layout import Session exposing (Session) diff --git a/src/Page/NotFound.elm b/src/Page/NotFound.elm index c1ad509..54b7872 100644 --- a/src/Page/NotFound.elm +++ b/src/Page/NotFound.elm @@ -1,28 +1,32 @@ module Page.NotFound exposing (view) import Browser +import Components.Button as Button +import Components.HeroPattern as HeroPattern import Html import Html.Attributes as Attr -import Html.Attributes.Aria as Aria +import Layout.Main as Layout +import Session exposing (Session) -- VIEW -view : Browser.Document Never -view = +view : Session -> (Session.Msg -> msg) -> Browser.Document msg +view session toSessionMsg = { title = "Guida: Page not found" , body = - [ Html.main_ [ Aria.role "main" ] - [ Html.section [] - [ Html.h2 [] [ Html.text "Page not found" ] - , Html.p [] [ Html.text "Sorry, we couldn't find the page you're looking for." ] - , Html.ul [] - [ Html.li [] [ Html.a [ Attr.href "/" ] [ Html.text "Go back home" ] ] - , Html.li [] [ Html.a [ Attr.href "https://github.com/guida-lang/guida-lang.org/issues" ] [ Html.text "File a bug" ] ] + Layout.view { sidebarNavigation = [] } session toSessionMsg <| + [ HeroPattern.view + , Html.div [ Attr.class "mx-auto flex h-full max-w-xl flex-col items-center justify-center py-16 text-center" ] + [ Html.p [ Attr.class "text-sm font-semibold text-zinc-900 dark:text-white" ] [ Html.text "404" ] + , Html.h1 [ Attr.class "mt-2 text-2xl font-bold text-zinc-900 dark:text-white" ] [ Html.text "Page not found" ] + , Html.p [ Attr.class "mt-2 text-base text-zinc-600 dark:text-zinc-400" ] [ Html.text "Sorry, we couldn't find the page you're looking for." ] + , Html.div [ Attr.class "not-prose mb-16 flex gap-3" ] + [ Button.view (Button.Link "/") Button.Primary Nothing [ Attr.class "mt-8" ] [ Html.text "Go back home" ] + , Button.view (Button.Link "https://github.com/guida-lang/guida-lang.org/issues") Button.Outline (Just Button.RightArrow) [ Attr.class "mt-8" ] [ Html.text "File a bug" ] ] ] ] - ] } diff --git a/src/Route.elm b/src/Route.elm index a663c86..982e60b 100644 --- a/src/Route.elm +++ b/src/Route.elm @@ -23,8 +23,10 @@ type Route type DocumentationSection = Introduction - | Elm + | Syntax + | FromJavaScriptOrElm | GuidaJson + | Records | Commands Command | Hints Hint @@ -179,8 +181,10 @@ parser = Parser.oneOf [ Parser.map Home Parser.top , Parser.map (Docs Introduction) (Parser.s "docs") - , Parser.map (Docs Elm) (Parser.s "docs" Parser.s "elm") + , Parser.map (Docs Syntax) (Parser.s "docs" Parser.s "syntax") + , Parser.map (Docs FromJavaScriptOrElm) (Parser.s "docs" Parser.s "from-javascript-or-elm") , Parser.map (Docs GuidaJson) (Parser.s "docs" Parser.s "guida-json") + , Parser.map (Docs Records) (Parser.s "docs" Parser.s "records") , Parser.map (Docs << Commands) (Parser.s "docs" Parser.s "1.0.0" Parser.s "commands" commandParser) , Parser.map (Docs << Hints) (Parser.s "docs" Parser.s "1.0.0" Parser.s "hints" hintParser) , Parser.map Community (Parser.s "community") diff --git a/styles/typography.js b/styles/typography.js index 7becadb..906d6fb 100644 --- a/styles/typography.js +++ b/styles/typography.js @@ -215,8 +215,6 @@ module.exports = { width: '100%', tableLayout: 'auto', textAlign: 'left', - marginTop: theme('spacing.8'), - marginBottom: theme('spacing.8'), lineHeight: theme('lineHeight.6'), }, thead: { From debbb80c8db24c217a06d4cca2a4e95fd1f291ed Mon Sep 17 00:00:00 2001 From: Decio Ferreira Date: Thu, 9 Oct 2025 22:56:28 +0100 Subject: [PATCH 05/11] WIP updated version --- src/Components/CodeBlock.elm | 2 +- src/Layout/Header.elm | 2 +- src/Page/Docs.elm | 15 +++++++++++++++ src/Route.elm | 2 ++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Components/CodeBlock.elm b/src/Components/CodeBlock.elm index d851359..c7d02e2 100644 --- a/src/Components/CodeBlock.elm +++ b/src/Components/CodeBlock.elm @@ -6,7 +6,7 @@ import Html.Attributes as Attr view : String -> Html msg view content = - Html.pre [ Attr.class "overflow-auto p-4 bg-neutral-100" ] + Html.pre [ Attr.class "not-prose overflow-auto p-4 bg-neutral-100 dark:bg-white/2.5 dark:text-white" ] [ Html.code [ Attr.class "shadow-none p-0" ] [ Html.text content ] ] diff --git a/src/Layout/Header.elm b/src/Layout/Header.elm index 63b3ae2..4be0077 100644 --- a/src/Layout/Header.elm +++ b/src/Layout/Header.elm @@ -60,7 +60,7 @@ view config = (Aria.role "banner" :: Attr.classList [ ( "fixed inset-x-0 top-0 z-50 flex h-14 items-center justify-between gap-12 px-4 transition sm:px-6", True ) - , ( "lg:left-72 lg:z-30 lg:px-8 xl:left-80", config.hasSidebar ) + , ( "lg:left-72 lg:z-30 lg:px-6 xl:left-80", config.hasSidebar ) , ( "backdrop-blur-xs dark:backdrop-blur-sm", not config.isInsideMobileNavigation ) , ( "bg-white dark:bg-zinc-900", config.isInsideMobileNavigation ) , ( "bg-white/[var(--bg-opacity-light)] dark:bg-zinc-900/[var(--bg-opacity-dark)]", not config.isInsideMobileNavigation ) diff --git a/src/Page/Docs.elm b/src/Page/Docs.elm index 7a5577b..f289c12 100644 --- a/src/Page/Docs.elm +++ b/src/Page/Docs.elm @@ -58,6 +58,9 @@ view session toSessionMsg model = Route.Records -> recordsView + Route.Interop -> + interopView + Route.Commands command -> commandView command @@ -876,6 +879,15 @@ recordsView = ] +interopView : List (Html msg) +interopView = + [ Html.h1 [] [ Html.text "JavaScript Interop" ] + , References.view + [ "https://elm-lang.org/docs/records" + ] + ] + + commandView : Route.Command -> List (Html msg) commandView command = case command of @@ -1034,6 +1046,9 @@ sidebarNavigation model = , { title = "From JavaScript or Elm?", href = "/docs/from-javascript-or-elm", active = model.section == Route.FromJavaScriptOrElm } , { title = "guida.json", href = "/docs/guida-json", active = model.section == Route.GuidaJson } , { title = "Records", href = "/docs/records", active = model.section == Route.Records } + + -- guide.elm-lang.org + , { title = "JavaScript Interop", href = "/docs/interop", active = model.section == Route.Interop } ] } , { title = "Commands" diff --git a/src/Route.elm b/src/Route.elm index 982e60b..beba117 100644 --- a/src/Route.elm +++ b/src/Route.elm @@ -27,6 +27,7 @@ type DocumentationSection | FromJavaScriptOrElm | GuidaJson | Records + | Interop | Commands Command | Hints Hint @@ -185,6 +186,7 @@ parser = , Parser.map (Docs FromJavaScriptOrElm) (Parser.s "docs" Parser.s "from-javascript-or-elm") , Parser.map (Docs GuidaJson) (Parser.s "docs" Parser.s "guida-json") , Parser.map (Docs Records) (Parser.s "docs" Parser.s "records") + , Parser.map (Docs Interop) (Parser.s "docs" Parser.s "interop") , Parser.map (Docs << Commands) (Parser.s "docs" Parser.s "1.0.0" Parser.s "commands" commandParser) , Parser.map (Docs << Hints) (Parser.s "docs" Parser.s "1.0.0" Parser.s "hints" hintParser) , Parser.map Community (Parser.s "community") From d3d023cc0ca406d51abeba5d3df9cee7963d7125 Mon Sep 17 00:00:00 2001 From: Decio Ferreira Date: Thu, 9 Oct 2025 23:16:49 +0100 Subject: [PATCH 06/11] fix some elm-review warnings --- src/Components/Button.elm | 2 + src/Components/CodeBlock.elm | 2 +- src/Components/GridPattern.elm | 17 ++++- src/Components/MobileNavigation.elm | 3 +- src/Components/Note.elm | 2 +- src/Components/Properties.elm | 5 +- src/Icon.elm | 102 +--------------------------- src/Layout/Header.elm | 2 + src/Layout/Main.elm | 5 +- src/Layout/Navigation.elm | 2 + src/Main.elm | 16 ++--- src/Page/Docs.elm | 4 +- 12 files changed, 42 insertions(+), 120 deletions(-) diff --git a/src/Components/Button.elm b/src/Components/Button.elm index 67b4719..be51d7d 100644 --- a/src/Components/Button.elm +++ b/src/Components/Button.elm @@ -82,6 +82,7 @@ view type_ variant maybeArrow attrs children = arrowIconElem : Html msg arrowIconElem = let + variantAttrs : List (Svg.Attribute msg) variantAttrs = case variant of Text -> @@ -90,6 +91,7 @@ view type_ variant maybeArrow attrs children = _ -> [] + arrowAttrs : List (Svg.Attribute msg) arrowAttrs = case maybeArrow of Just LeftArrow -> diff --git a/src/Components/CodeBlock.elm b/src/Components/CodeBlock.elm index c7d02e2..493d8d6 100644 --- a/src/Components/CodeBlock.elm +++ b/src/Components/CodeBlock.elm @@ -1,4 +1,4 @@ -module Components.CodeBlock exposing (..) +module Components.CodeBlock exposing (view) import Html exposing (Html) import Html.Attributes as Attr diff --git a/src/Components/GridPattern.elm b/src/Components/GridPattern.elm index 17c9e8a..ae6a144 100644 --- a/src/Components/GridPattern.elm +++ b/src/Components/GridPattern.elm @@ -5,6 +5,7 @@ module Components.GridPattern exposing import Html exposing (Html) import Html.Attributes.Aria as Aria +import Icon import Svg import Svg.Attributes as SvgAttr @@ -39,14 +40,24 @@ view attrs config = ] (List.map (\( x, y ) -> - Svg.rect - [ SvgAttr.strokeWidth "0" + -- Svg.rect + -- [ SvgAttr.strokeWidth "0" + -- , SvgAttr.width (String.fromInt (config.width + 1)) + -- , SvgAttr.height (String.fromInt (config.height + 1)) + -- , SvgAttr.x (String.fromInt (x * config.width)) + -- , SvgAttr.y (String.fromInt (y * config.height)) + -- ] + -- [] + Icon.logo + [ SvgAttr.class "dark:text-amber-700/20" + , SvgAttr.strokeWidth "0" + + -- , SvgAttr.fillOpacity "0.4" , SvgAttr.width (String.fromInt (config.width + 1)) , SvgAttr.height (String.fromInt (config.height + 1)) , SvgAttr.x (String.fromInt (x * config.width)) , SvgAttr.y (String.fromInt (y * config.height)) ] - [] ) config.squares ) diff --git a/src/Components/MobileNavigation.elm b/src/Components/MobileNavigation.elm index 8dc454f..da68731 100644 --- a/src/Components/MobileNavigation.elm +++ b/src/Components/MobileNavigation.elm @@ -1,4 +1,4 @@ -module Components.MobileNavigation exposing (..) +module Components.MobileNavigation exposing (view) import Html exposing (Html) import Html.Attributes as Attr @@ -37,6 +37,7 @@ xIcon className = view : { hasSidebar : Bool, isOpen : Bool, toggleMsg : msg } -> Html msg view { hasSidebar, isOpen, toggleMsg } = let + toogleIcon : String -> Html msg toogleIcon = if isOpen then xIcon diff --git a/src/Components/Note.elm b/src/Components/Note.elm index 488ca83..ce0d9b8 100644 --- a/src/Components/Note.elm +++ b/src/Components/Note.elm @@ -1,4 +1,4 @@ -module Components.Note exposing (..) +module Components.Note exposing (view) import Html exposing (Html) import Html.Attributes as Attr diff --git a/src/Components/Properties.elm b/src/Components/Properties.elm index 86a429f..29b8ab4 100644 --- a/src/Components/Properties.elm +++ b/src/Components/Properties.elm @@ -1,4 +1,7 @@ -module Components.Properties exposing (view) +module Components.Properties exposing + ( Property + , view + ) import Html exposing (Html) import Html.Attributes as Attr diff --git a/src/Icon.elm b/src/Icon.elm index 13899e4..45270e0 100644 --- a/src/Icon.elm +++ b/src/Icon.elm @@ -1,13 +1,8 @@ module Icon exposing - ( arrowPath - , burger - , check - , cross - , discord + ( discord , github , info , logo - , xMark ) import Html @@ -17,82 +12,6 @@ import Svg exposing (Svg) import Svg.Attributes as SvgAttr -arrowPath : List (Html.Attribute msg) -> Svg msg -arrowPath attrs = - Svg.svg - (SvgAttr.fill "none" - :: SvgAttr.viewBox "0 0 24 24" - :: SvgAttr.strokeWidth "1.5" - :: SvgAttr.stroke "currentColor" - :: Attr.attribute "aria-hidden" "true" - :: attrs - ) - [ Svg.path - [ SvgAttr.strokeLinecap "round" - , SvgAttr.strokeLinejoin "round" - , SvgAttr.d "M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99" - ] - [] - ] - - -burger : List (Html.Attribute msg) -> Svg msg -burger attrs = - Svg.svg - (SvgAttr.fill "none" - :: SvgAttr.viewBox "0 0 24 24" - :: SvgAttr.strokeWidth "1.5" - :: SvgAttr.stroke "currentColor" - :: Attr.attribute "aria-hidden" "true" - :: attrs - ) - [ Svg.path - [ SvgAttr.strokeLinecap "round" - , SvgAttr.strokeLinejoin "round" - , SvgAttr.d "M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" - ] - [] - ] - - -check : List (Html.Attribute msg) -> Svg msg -check attrs = - Svg.svg - (SvgAttr.fill "none" - :: SvgAttr.viewBox "0 0 24 24" - :: SvgAttr.strokeWidth "1.5" - :: SvgAttr.stroke "currentColor" - :: Attr.attribute "aria-hidden" "true" - :: attrs - ) - [ Svg.path - [ SvgAttr.strokeLinecap "round" - , SvgAttr.strokeLinejoin "round" - , SvgAttr.d "m4.5 12.75 6 6 9-13.5" - ] - [] - ] - - -cross : List (Html.Attribute msg) -> Svg msg -cross attrs = - Svg.svg - (SvgAttr.fill "none" - :: SvgAttr.viewBox "0 0 24 24" - :: SvgAttr.strokeWidth "1.5" - :: SvgAttr.stroke "currentColor" - :: Attr.attribute "aria-hidden" "true" - :: attrs - ) - [ Svg.path - [ SvgAttr.strokeLinecap "round" - , SvgAttr.strokeLinejoin "round" - , SvgAttr.d "M6 18 18 6M6 6l12 12" - ] - [] - ] - - github : List (Html.Attribute msg) -> Svg msg github attrs = Svg.svg @@ -165,22 +84,3 @@ logo attrs = ] [] ] - - -xMark : List (Html.Attribute msg) -> Svg msg -xMark attrs = - Svg.svg - (SvgAttr.fill "none" - :: SvgAttr.viewBox "0 0 24 24" - :: SvgAttr.strokeWidth "1.5" - :: SvgAttr.stroke "currentColor" - :: Attr.attribute "aria-hidden" "true" - :: attrs - ) - [ Svg.path - [ SvgAttr.strokeLinecap "round" - , SvgAttr.strokeLinejoin "round" - , SvgAttr.d "M6 18 18 6M6 6l12 12" - ] - [] - ] diff --git a/src/Layout/Header.elm b/src/Layout/Header.elm index 4be0077..7607ec4 100644 --- a/src/Layout/Header.elm +++ b/src/Layout/Header.elm @@ -48,9 +48,11 @@ socialLink href icon children = view : Config msg -> Html msg view config = let + isMobileNavigationOpen : Bool isMobileNavigationOpen = Session.isMobileNavigationOpen config.session + classNameAttrs : List (Html.Attribute msg) classNameAttrs = config.className |> Maybe.map (List.singleton << Attr.class) diff --git a/src/Layout/Main.elm b/src/Layout/Main.elm index b5416f9..8b93ff5 100644 --- a/src/Layout/Main.elm +++ b/src/Layout/Main.elm @@ -1,5 +1,6 @@ module Layout.Main exposing - ( fullscreenView + ( Config + , fullscreenView , view ) @@ -137,7 +138,7 @@ fullscreenView session toSessionMsg children = in [ Html.div [ Attr.class "contents" ] [ Html.div [ Attr.class "w-full" ] - [ Html.div [ Attr.classList [ ( "h-full", True ) ] ] + [ Html.div [ Attr.class "h-full" ] [ Html.header [ Attr.class "contents lg:pointer-events-none lg:fixed lg:inset-0 lg:z-40 lg:flex" ] [ sidebarView config session toSessionMsg ] diff --git a/src/Layout/Navigation.elm b/src/Layout/Navigation.elm index 36fa790..1b38517 100644 --- a/src/Layout/Navigation.elm +++ b/src/Layout/Navigation.elm @@ -1,5 +1,6 @@ module Layout.Navigation exposing ( Navigation + , NavigationGroup , NavigationLink , view ) @@ -90,6 +91,7 @@ navigationLinkView navigationLink = activePageMarker : NavigationGroup -> List (Html msg) activePageMarker group = let + maybeActivePageIndex : Maybe Int maybeActivePageIndex = List.indexedMap Tuple.pair group.links |> List.filter (\( _, link ) -> link.active) diff --git a/src/Main.elm b/src/Main.elm index 8246674..454d7d7 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -1,10 +1,15 @@ -module Main exposing (main) +module Main exposing + ( CurrentPage + , Flags + , Model + , Msg + , main + ) import Browser import Browser.Events import Browser.Navigation as Nav import Components.ThemeToggle as ThemeToggle -import Html import Page.Community as Community import Page.Docs as Docs import Page.Examples as Examples @@ -195,13 +200,6 @@ view model = Try.view model.session SessionMsg TryMsg subModel -viewPage : (msg -> Msg) -> Browser.Document msg -> Browser.Document Msg -viewPage toMsg { title, body } = - { title = title - , body = List.map (Html.map toMsg) body - } - - -- MAIN diff --git a/src/Page/Docs.elm b/src/Page/Docs.elm index f289c12..77417d5 100644 --- a/src/Page/Docs.elm +++ b/src/Page/Docs.elm @@ -5,7 +5,7 @@ module Page.Docs exposing ) import Browser -import Components.Button as Button exposing (Type(..)) +import Components.Button as Button import Components.CodeBlock as CodeBlock import Components.Note as Note import Components.Properties as Properties @@ -1033,9 +1033,11 @@ hintView hint = sidebarNavigation : Model -> Navigation sidebarNavigation model = let + activeCommand : Route.Command -> Bool activeCommand cmd = model.section == Route.Commands cmd + activeHint : Route.Hint -> Bool activeHint hint = model.section == Route.Hints hint in From 06fd3cafc5aa99175f648255e465f411b0784263 Mon Sep 17 00:00:00 2001 From: Decio Ferreira Date: Mon, 13 Oct 2025 18:56:30 +0100 Subject: [PATCH 07/11] WIP updated version --- elm.json | 8 +- src/Components/References.elm | 2 +- src/Layout/Main.elm | 21 +- src/Layout/Navigation.elm | 97 +- src/Main.elm | 81 +- src/Page/Community.elm | 165 +- src/Page/Docs.elm | 2964 ++++++++++++++++++++++----------- src/Page/Examples.elm | 2 +- src/Page/Home.elm | 12 +- src/Page/NotFound.elm | 2 +- src/Page/Try.elm | 95 +- src/Route.elm | 84 +- 12 files changed, 2384 insertions(+), 1149 deletions(-) diff --git a/elm.json b/elm.json index dfd8913..b76c672 100644 --- a/elm.json +++ b/elm.json @@ -7,22 +7,26 @@ "dependencies": { "direct": { "debois/elm-dom": "1.3.0", + "dillonkearns/elm-markdown": "7.0.1", "elm/browser": "1.0.2", "elm/core": "1.0.5", "elm/html": "1.0.0", "elm/http": "2.0.0", "elm/json": "1.1.3", + "elm/parser": "1.1.0", "elm/project-metadata-utils": "1.0.2", "elm/svg": "1.0.1", "elm/url": "1.0.0", + "elm-community/result-extra": "2.4.0", "fapian/elm-html-aria": "1.4.0" }, "indirect": { "elm/bytes": "1.0.8", "elm/file": "1.0.5", - "elm/parser": "1.1.0", + "elm/regex": "1.0.0", "elm/time": "1.0.0", - "elm/virtual-dom": "1.0.3" + "elm/virtual-dom": "1.0.3", + "rtfeldman/elm-hex": "1.0.0" } }, "test-dependencies": { diff --git a/src/Components/References.elm b/src/Components/References.elm index 347ab1a..920081a 100644 --- a/src/Components/References.elm +++ b/src/Components/References.elm @@ -10,7 +10,7 @@ view : List String -> Html msg view links = Html.div [ Attr.class "my-6" ] [ Html.hr [] [] - , Html.h2 [ Attr.class "scroll-mt-24" ] [ Html.text "References" ] + , Html.h2 [] [ Html.text "References" ] , Html.ul [ Aria.role "list" ] (List.map linkView links) ] diff --git a/src/Layout/Main.elm b/src/Layout/Main.elm index 8b93ff5..b209a75 100644 --- a/src/Layout/Main.elm +++ b/src/Layout/Main.elm @@ -20,8 +20,9 @@ import Session exposing (Session) -- CONFIGURATION -type alias Config = - { sidebarNavigation : Navigation +type alias Config route = + { sidebarNavigation : Navigation route + , currentRoute : route } @@ -29,7 +30,7 @@ type alias Config = -- VIEW -sidebarView : Config -> Session -> (Session.Msg -> msg) -> Html msg +sidebarView : Config route -> Session -> (Session.Msg -> msg) -> Html msg sidebarView config session toSessionMsg = let header : Html msg @@ -54,11 +55,11 @@ sidebarView config session toSessionMsg = ] ] , header - , Navigation.view [ Attr.class "hidden lg:mt-10 lg:block" ] config.sidebarNavigation + , Navigation.view [ Attr.class "hidden lg:mt-10 lg:block" ] config.currentRoute config.sidebarNavigation ] -dialogView : Config -> Session -> (Session.Msg -> msg) -> Html msg +dialogView : Config route -> Session -> (Session.Msg -> msg) -> Html msg dialogView config session toSessionMsg = let openAttrs : List (Html.Attribute msg) @@ -87,12 +88,12 @@ dialogView config session toSessionMsg = , toggleNavigationMsg = toSessionMsg Session.ToggleMobileNavigation } , Html.div [ Attr.class "fixed top-14 bottom-0 left-0 w-full overflow-y-auto bg-white px-4 pt-6 pb-4 ring-1 shadow-lg shadow-zinc-900/10 ring-zinc-900/7.5 duration-500 ease-in-out data-closed:-translate-x-full min-[416px]:max-w-sm sm:px-6 sm:pb-10 dark:bg-zinc-900 dark:ring-zinc-800" ] - [ Navigation.view [] config.sidebarNavigation + [ Navigation.view [] config.currentRoute config.sidebarNavigation ] ] -view : Config -> Session -> (Session.Msg -> msg) -> List (Html msg) -> List (Html msg) +view : Config route -> Session -> (Session.Msg -> msg) -> List (Html msg) -> List (Html msg) view config session toSessionMsg children = [ Html.div [ Attr.class "contents" ] [ Html.div [ Attr.class "w-full" ] @@ -105,7 +106,7 @@ view config session toSessionMsg children = [ Html.header [ Attr.class "contents lg:pointer-events-none lg:fixed lg:inset-0 lg:z-40 lg:flex" ] [ sidebarView config session toSessionMsg ] - , Html.div [ Attr.class "relative flex h-full flex-col px-4 pt-14 sm:px-6 lg:px-8" ] + , Html.div [ Attr.id "main", Attr.class "relative flex h-full flex-col px-4 pt-14 sm:px-6 lg:px-8" ] [ Html.main_ [ Attr.class "flex-auto" ] [ Html.article [ Attr.class "flex h-full flex-col pt-16 pb-10" ] [ Html.div @@ -132,9 +133,9 @@ view config session toSessionMsg children = fullscreenView : Session -> (Session.Msg -> msg) -> List (Html msg) -> List (Html msg) fullscreenView session toSessionMsg children = let - config : Config + config : Config () config = - { sidebarNavigation = [] } + { sidebarNavigation = [], currentRoute = () } in [ Html.div [ Attr.class "contents" ] [ Html.div [ Attr.class "w-full" ] diff --git a/src/Layout/Navigation.elm b/src/Layout/Navigation.elm index 1b38517..90c4e0f 100644 --- a/src/Layout/Navigation.elm +++ b/src/Layout/Navigation.elm @@ -2,6 +2,7 @@ module Layout.Navigation exposing ( Navigation , NavigationGroup , NavigationLink + , NavigationSection , view ) @@ -12,29 +13,37 @@ import Html.Attributes.Aria as Aria import Layout.Global as Global -type alias Navigation = - List NavigationGroup +type alias Navigation route = + List (NavigationGroup route) -type alias NavigationGroup = +type alias NavigationGroup route = { title : String - , links : List NavigationLink + , links : List (NavigationLink route) } -type alias NavigationLink = +type alias NavigationLink route = { title : String , href : String - , active : Bool + , route : route + , sections : List (NavigationSection route) } -view : List (Html.Attribute msg) -> Navigation -> Html msg -view attrs navigation = +type alias NavigationSection route = + { title : String + , href : String + , route : route + } + + +view : List (Html.Attribute msg) -> route -> Navigation route -> Html msg +view attrs currentRoute navigation = Html.nav attrs [ Html.ul [ Aria.role "list" ] (List.map topLevelNavItem Global.topLevelNavItems - ++ List.indexedMap navigationGroupView navigation + ++ List.indexedMap (navigationGroupView currentRoute) navigation ) ] @@ -50,8 +59,8 @@ topLevelNavItem { href, children } = ] -navigationGroupView : Int -> NavigationGroup -> Html msg -navigationGroupView groupIndex navigationGroup = +navigationGroupView : route -> Int -> NavigationGroup route -> Html msg +navigationGroupView currentRoute groupIndex navigationGroup = Html.li [ Attr.classList [ ( "relative mt-6", True ) @@ -59,42 +68,80 @@ navigationGroupView groupIndex navigationGroup = ] ] [ Html.h2 [ Attr.class "text-xs font-semibold text-zinc-900 dark:text-white" ] - [ Html.text navigationGroup.title + [ Html.text (String.fromInt groupIndex ++ ". " ++ navigationGroup.title) ] , Html.div [ Attr.class "relative mt-3 pl-2" ] (Html.div [ Attr.class "absolute inset-y-0 left-2 w-px bg-zinc-900/10 dark:bg-white/5" ] [] - :: activePageMarker navigationGroup + :: activePageMarker currentRoute navigationGroup ++ [ Html.ul [ Aria.role "list", Attr.class "border-l border-transparent" ] - (List.map navigationLinkView navigationGroup.links) + (List.map (navigationLinkView currentRoute) navigationGroup.links) ] ) ] -navigationLinkView : NavigationLink -> Html msg -navigationLinkView navigationLink = +navigationLinkView : route -> NavigationLink route -> Html msg +navigationLinkView currentRoute navigationLink = + let + sections : List (Html msg) + sections = + case navigationLink.sections of + [] -> + [] + + _ -> + [ Html.ul [ Aria.role "list" ] + (List.map (navigationSectionView currentRoute) navigationLink.sections) + ] + + active : Bool + active = + navigationLink.route == currentRoute + in Html.li [ Attr.class "relative" ] - [ Link.view + (Link.view [ Attr.href navigationLink.href , Attr.classList - [ ( "flex justify-between gap-2 py-1 pr-3 text-sm transition", True ) - , ( "pl-4", True ) - , ( "text-zinc-900 dark:text-white", navigationLink.active ) - , ( "text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white", not navigationLink.active ) + [ ( "flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4", True ) + , ( "text-zinc-900 dark:text-white", active ) + , ( "text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white", not active ) ] ] [ Html.text navigationLink.title ] + :: sections + ) + + +navigationSectionView : route -> NavigationSection route -> Html msg +navigationSectionView currentRoute section = + let + active : Bool + active = + section.route == currentRoute + in + Html.li [] + [ Link.view + [ Attr.href section.href + , Attr.classList + [ ( "flex justify-between gap-2 py-1 pr-3 text-sm transition pl-7", True ) + , ( "text-zinc-900 dark:text-white", active ) + , ( "text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white", not active ) + ] + ] + [ Html.text section.title + ] ] -activePageMarker : NavigationGroup -> List (Html msg) -activePageMarker group = +activePageMarker : route -> NavigationGroup route -> List (Html msg) +activePageMarker currentRoute group = let maybeActivePageIndex : Maybe Int maybeActivePageIndex = - List.indexedMap Tuple.pair group.links - |> List.filter (\( _, link ) -> link.active) + List.concatMap (\link -> link.route :: List.map .route link.sections) group.links + |> List.indexedMap Tuple.pair + |> List.filter (\( _, route ) -> route == currentRoute) |> List.head |> Maybe.map Tuple.first in diff --git a/src/Main.elm b/src/Main.elm index 454d7d7..5e09be4 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -7,6 +7,7 @@ module Main exposing ) import Browser +import Browser.Dom import Browser.Events import Browser.Navigation as Nav import Components.ThemeToggle as ThemeToggle @@ -16,8 +17,9 @@ import Page.Examples as Examples import Page.Home as Home import Page.NotFound as NotFound import Page.Try as Try -import Route exposing (Route) +import Route import Session exposing (Session) +import Task import Url exposing (Url) @@ -52,7 +54,7 @@ init flags url navKey = _ -> ThemeToggle.Light in - changeRouteTo (Route.fromUrl url) + changeRouteTo url { session = Session.init flags.year theme navKey , currentPage = NotFound } @@ -62,36 +64,52 @@ init flags url navKey = -- UPDATE -changeRouteTo : Maybe Route -> Model -> ( Model, Cmd Msg ) -changeRouteTo maybeRoute model = - case maybeRoute of - Nothing -> - ( { model | currentPage = NotFound }, Cmd.none ) - - Just Route.Home -> - ( { model | currentPage = Home }, Cmd.none ) - - Just (Route.Docs subRoute) -> - Docs.init subRoute - |> updateWith Docs identity model - - Just Route.Community -> - ( { model | currentPage = Community }, Cmd.none ) - - Just Route.Examples -> - ( { model | currentPage = Examples }, Cmd.none ) - - Just (Route.Example example) -> - Try.init (Just example) - |> updateWith Try TryMsg model - - Just Route.Try -> - Try.init Nothing - |> updateWith Try TryMsg model +changeRouteTo : Url -> Model -> ( Model, Cmd Msg ) +changeRouteTo url model = + let + ( updatedModel, cmd ) = + case Route.fromUrl url of + Nothing -> + ( { model | currentPage = NotFound }, Cmd.none ) + + Just Route.Home -> + ( { model | currentPage = Home }, Cmd.none ) + + Just (Route.Docs subRoute) -> + Docs.init subRoute + |> updateWith Docs identity model + + Just Route.Community -> + ( { model | currentPage = Community }, Cmd.none ) + + Just Route.Examples -> + ( { model | currentPage = Examples }, Cmd.none ) + + Just (Route.Example example) -> + Try.init (Just example) + |> updateWith Try TryMsg model + + Just Route.Try -> + Try.init Nothing + |> updateWith Try TryMsg model + + scrollToFragment : Cmd Msg + scrollToFragment = + case url.fragment of + Just fragment -> + Browser.Dom.getElement fragment + |> Task.andThen (\{ element } -> Browser.Dom.setViewport 0 (element.y - 96)) + |> Task.attempt (\_ -> NoOp) + + Nothing -> + Task.attempt (\_ -> NoOp) (Browser.Dom.setViewport 0 0) + in + ( updatedModel, Cmd.batch [ cmd, scrollToFragment ] ) type Msg - = ChangedUrl Url + = NoOp + | ChangedUrl Url | ClickedLink Browser.UrlRequest -- SESSION | SessionMsg Session.Msg @@ -102,8 +120,11 @@ type Msg update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case ( msg, model.currentPage ) of + ( NoOp, _ ) -> + ( model, Cmd.none ) + ( ChangedUrl url, _ ) -> - changeRouteTo (Route.fromUrl url) model + changeRouteTo url model ( ClickedLink urlRequest, _ ) -> case urlRequest of diff --git a/src/Page/Community.elm b/src/Page/Community.elm index a91cc68..a58e2ee 100644 --- a/src/Page/Community.elm +++ b/src/Page/Community.elm @@ -3,7 +3,6 @@ module Page.Community exposing (view) import Browser import Html import Html.Attributes as Attr -import Html.Attributes.Aria as Aria import Layout.Main as Layout import Session exposing (Session) @@ -16,90 +15,86 @@ view : Session -> (Session.Msg -> msg) -> Browser.Document msg view session toSessionMsg = { title = "Guida: Community" , body = - Layout.view { sidebarNavigation = [] } session toSessionMsg <| - [ Html.main_ [ Aria.role "main" ] - [ Html.section [] - [ Html.h2 [] [ Html.text "Community" ] - , Html.p [] - [ Html.text "Guida is built in the open, and its future depends on the people who use it, contribute to it, and share ideas. Whether you're here to report a bug, propose a feature, improve the compiler, or just explore the language, you're very welcome to join the community." - ] - , Html.h3 [] [ Html.text "Where to Talk" ] - , Html.ul [] - [ Html.li [] - [ Html.strong [] [ Html.text "Discord:" ] - , Html.text " Join us in the " - , Html.a [ Attr.href "https://discord.gg/B6WgPzf5Aa" ] [ Html.text "Guida Discord" ] - , Html.text " to connect with other contributors and users, ask questions, and share ideas." - ] - , Html.li [] - [ Html.strong [] [ Html.text "GitHub Discussions:" ] - , Html.text " On the " - , Html.a [ Attr.href "https://github.com/orgs/guida-lang/discussions" ] [ Html.text "Guida Discussions" ] - , Html.text " you can ask and answer questions, share updates, have open-ended conversations, and follow along on decisions affecting the community's way of working." - ] - , Html.li [] - [ Html.strong [] [ Html.text "GitHub Issues:" ] - , Html.text " Found a bug or missing feature? Report it in the " - , Html.a [ Attr.href "https://github.com/guida-lang/compiler/issues" ] [ Html.text "Guida Compiler issue tracker" ] - , Html.text "." - ] - ] - , Html.h3 [] [ Html.text "How to Contribute" ] - , Html.ul [] - [ Html.li [] - [ Html.text "Read the " - , Html.a [ Attr.href "https://github.com/guida-lang/compiler/blob/master/CONTRIBUTING.md" ] [ Html.text "Contributing Guide" ] - , Html.text " to learn how to get started." - ] - , Html.li [] - [ Html.text "Check out open issues labeled " - , Html.a [ Attr.href "https://github.com/guida-lang/compiler/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22" ] [ Html.text "“good first issue”" ] - , Html.text "." - ] - , Html.li [] - [ Html.text "Improvements to the " - , Html.strong [] [ Html.text "compiler" ] - , Html.text ", " - , Html.strong [] [ Html.text "package registry" ] - , Html.text ", and " - , Html.strong [] [ Html.text "tooling" ] - , Html.text " are always welcome." - ] - ] - , Html.h3 [] [ Html.text "Stay Up to Date" ] - , Html.ul [] - [ Html.li [] - [ Html.text "Follow ongoing work on " - , Html.a [ Attr.href "https://github.com/guida-lang" ] [ Html.text "GitHub" ] - , Html.text "." - ] - , Html.li [] - [ Html.text "See the " - , Html.a [ Attr.href "https://github.com/orgs/guida-lang/discussions" ] [ Html.text "GitHub discussions" ] - , Html.text " for what's next." - ] - , Html.li [] - [ Html.text "Keep an eye on the " - , Html.a [ Attr.href "https://github.com/orgs/guida-lang/discussions/112" ] [ Html.text "Interesting Projects & Tools" ] - , Html.text " list to discover related work in the ecosystem." - ] - ] - , Html.h3 [] [ Html.text "Guiding Principles" ] - , Html.p [] [ Html.strong [] [ Html.text "Guida thrives on:" ] ] - , Html.ul [] - [ Html.li [] - [ Html.strong [] [ Html.text "Backward Compatibility:" ] - , Html.text " Respecting existing code whenever possible." - ] - , Html.li [] - [ Html.strong [] [ Html.text "Self-hosted development:" ] - , Html.text " Building the language in the language itself." - ] - , Html.li [] - [ Html.strong [] [ Html.text "Community-driven innovation:" ] - , Html.text " Growing through shared ideas and contributions." - ] - ] + Layout.view { sidebarNavigation = [], currentRoute = () } session toSessionMsg <| + [ Html.h1 [] [ Html.text "Community" ] + , Html.p [] + [ Html.text "Guida is built in the open, and its future depends on the people who use it, contribute to it, and share ideas. Whether you're here to report a bug, propose a feature, improve the compiler, or just explore the language, you're very welcome to join the community." + ] + , Html.h2 [] [ Html.text "Where to Talk" ] + , Html.ul [] + [ Html.li [] + [ Html.strong [] [ Html.text "Discord:" ] + , Html.text " Join us in the " + , Html.a [ Attr.href "https://discord.gg/B6WgPzf5Aa" ] [ Html.text "Guida Discord" ] + , Html.text " to connect with other contributors and users, ask questions, and share ideas." + ] + , Html.li [] + [ Html.strong [] [ Html.text "GitHub Discussions:" ] + , Html.text " On the " + , Html.a [ Attr.href "https://github.com/orgs/guida-lang/discussions" ] [ Html.text "Guida Discussions" ] + , Html.text " you can ask and answer questions, share updates, have open-ended conversations, and follow along on decisions affecting the community's way of working." + ] + , Html.li [] + [ Html.strong [] [ Html.text "GitHub Issues:" ] + , Html.text " Found a bug or missing feature? Report it in the " + , Html.a [ Attr.href "https://github.com/guida-lang/compiler/issues" ] [ Html.text "Guida Compiler issue tracker" ] + , Html.text "." + ] + ] + , Html.h3 [] [ Html.text "How to Contribute" ] + , Html.ul [] + [ Html.li [] + [ Html.text "Read the " + , Html.a [ Attr.href "https://github.com/guida-lang/compiler/blob/master/CONTRIBUTING.md" ] [ Html.text "Contributing Guide" ] + , Html.text " to learn how to get started." + ] + , Html.li [] + [ Html.text "Check out open issues labeled " + , Html.a [ Attr.href "https://github.com/guida-lang/compiler/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22" ] [ Html.text "“good first issue”" ] + , Html.text "." + ] + , Html.li [] + [ Html.text "Improvements to the " + , Html.strong [] [ Html.text "compiler" ] + , Html.text ", " + , Html.strong [] [ Html.text "package registry" ] + , Html.text ", and " + , Html.strong [] [ Html.text "tooling" ] + , Html.text " are always welcome." + ] + ] + , Html.h3 [] [ Html.text "Stay Up to Date" ] + , Html.ul [] + [ Html.li [] + [ Html.text "Follow ongoing work on " + , Html.a [ Attr.href "https://github.com/guida-lang" ] [ Html.text "GitHub" ] + , Html.text "." + ] + , Html.li [] + [ Html.text "See the " + , Html.a [ Attr.href "https://github.com/orgs/guida-lang/discussions" ] [ Html.text "GitHub discussions" ] + , Html.text " for what's next." + ] + , Html.li [] + [ Html.text "Keep an eye on the " + , Html.a [ Attr.href "https://github.com/orgs/guida-lang/discussions/112" ] [ Html.text "Interesting Projects & Tools" ] + , Html.text " list to discover related work in the ecosystem." + ] + ] + , Html.h3 [] [ Html.text "Guiding Principles" ] + , Html.p [] [ Html.strong [] [ Html.text "Guida thrives on:" ] ] + , Html.ul [] + [ Html.li [] + [ Html.strong [] [ Html.text "Backward Compatibility:" ] + , Html.text " Respecting existing code whenever possible." + ] + , Html.li [] + [ Html.strong [] [ Html.text "Self-hosted development:" ] + , Html.text " Building the language in the language itself." + ] + , Html.li [] + [ Html.strong [] [ Html.text "Community-driven innovation:" ] + , Html.text " Growing through shared ideas and contributions." ] ] ] diff --git a/src/Page/Docs.elm b/src/Page/Docs.elm index 77417d5..1d57ea2 100644 --- a/src/Page/Docs.elm +++ b/src/Page/Docs.elm @@ -5,21 +5,112 @@ module Page.Docs exposing ) import Browser -import Components.Button as Button import Components.CodeBlock as CodeBlock -import Components.Note as Note -import Components.Properties as Properties -import Components.References as References -import Components.Table as Table import Html exposing (Html) import Html.Attributes as Attr +import Html.Attributes.Aria as Aria import Layout.Main as Layout import Layout.Navigation exposing (Navigation) +import Markdown.Block +import Markdown.Html +import Markdown.Parser +import Markdown.Renderer exposing (Renderer, defaultHtmlRenderer) +import Parser +import Parser.Advanced +import Result.Extra as Result import Route import Session exposing (Session) +-- MARKDOWN + + +markdownRender : String -> List (Html msg) +markdownRender = + Markdown.Parser.parse + >> Result.mapError deadEndsToString + >> Result.andThen (\ast -> Markdown.Renderer.render htmlRenderer ast) + >> Result.mapError (List.singleton << Html.text) + >> Result.merge + + +deadEndsToString : List (Parser.Advanced.DeadEnd String Parser.Problem) -> String +deadEndsToString = + List.map Markdown.Parser.deadEndToString >> String.join "\n" + + +htmlRenderer : Renderer (Html msg) +htmlRenderer = + { defaultHtmlRenderer + | heading = + \{ level, rawText, children } -> + let + tag : List (Html.Attribute msg) -> List (Html msg) -> Html msg + tag = + case level of + Markdown.Block.H1 -> + Html.h1 + + Markdown.Block.H2 -> + Html.h2 + + Markdown.Block.H3 -> + Html.h3 + + Markdown.Block.H4 -> + Html.h4 + + Markdown.Block.H5 -> + Html.h5 + + Markdown.Block.H6 -> + Html.h6 + in + tag [ Attr.id (String.replace " " "-" (String.toLower rawText)) ] + children + , codeBlock = \{ body } -> CodeBlock.view body + , html = + Markdown.Html.oneOf + [ Markdown.Html.tag "properties" + (Html.ul + [ Aria.role "list" + , Attr.class "m-0 list-none divide-y divide-zinc-900/5 p-0 dark:divide-white/5" + ] + ) + , Markdown.Html.tag "property" + (\name maybeType children -> + let + typeDescriptionTerm : List (Html msg) + typeDescriptionTerm = + maybeType + |> Maybe.map + (\type_ -> + [ Html.dt [ Attr.class "sr-only" ] [ Html.text "Type" ] + , Html.dd [ Attr.class "font-mono text-xs text-zinc-400 dark:text-zinc-500" ] [ Html.text type_ ] + ] + ) + |> Maybe.withDefault [] + in + Html.li [ Attr.class "m-0 px-0 py-4 first:pt-0 last:pb-0" ] + [ Html.dl [ Attr.class "m-0 flex flex-wrap items-center gap-x-3 gap-y-2" ] + (Html.dt [ Attr.class "sr-only" ] [ Html.text "Name" ] + :: Html.dd [] [ Html.code [] [ Html.text name ] ] + :: typeDescriptionTerm + ++ [ Html.dt [ Attr.class "sr-only" ] [ Html.text "Description" ] + , Html.dd [ Attr.class "w-full flex-none [&>:first-child]:mt-0 [&>:last-child]:mb-0" ] + children + ] + ) + ] + ) + |> Markdown.Html.withAttribute "name" + |> Markdown.Html.withOptionalAttribute "type" + ] + } + + + -- MODEL @@ -41,25 +132,85 @@ view : Session -> (Session.Msg -> msg) -> Model -> Browser.Document msg view session toSessionMsg model = { title = "Guida: Documentation" , body = - Layout.view { sidebarNavigation = sidebarNavigation model } session toSessionMsg <| + Layout.view { sidebarNavigation = sidebarNavigation, currentRoute = model.section } session toSessionMsg <| case model.section of Route.Introduction -> introductionView - Route.Syntax -> - syntaxView + Route.WhatIsGuida -> + whatIsGuidaView + + Route.Installation -> + markdownRender "# Installation" + + Route.YourFirstProgram -> + markdownRender "# Your First Program" + + Route.ProjectSetup -> + markdownRender "# Project Setup" + + Route.MigrationFromElm -> + markdownRender "# Migration from Elm" + + Route.SyntaxOverview -> + markdownRender "# Syntax Overview" + + Route.ValuesAndTypes -> + markdownRender "# Values and Types" + + Route.FunctionsAndExpressions -> + markdownRender "# Functions and Expressions" + + Route.ModulesAndImports -> + markdownRender "# Modules and Imports" + + Route.CustomTypes -> + markdownRender "# Custom Types" + + Route.PatternMatching -> + markdownRender "# Pattern Matching" + + Route.ErrorHandling -> + markdownRender "# Error Handling" - Route.FromJavaScriptOrElm -> - fromJavaScriptOrElmView + Route.ImmutabilityAndPurity -> + markdownRender "# Immutability and Purity" - Route.GuidaJson -> - guidaJsonView + Route.TheTypeSystem -> + markdownRender "# The Type System" - Route.Records -> - recordsView + Route.ConcurrencyAndEffects -> + markdownRender "# Concurrency and Effects" - Route.Interop -> - interopView + Route.StateAndArchitecture -> + markdownRender "# State and Architecture" + + Route.ApplicationStructure -> + markdownRender "# Application Structure" + + Route.TheGuidaArchitecture -> + markdownRender "# The Guida Architecture" + + Route.RoutingAndNavigation -> + markdownRender "# Routing and Navigation" + + Route.Interoperability -> + markdownRender "# Interoperability" + + Route.ContributingGettingStarted -> + markdownRender "# Getting Started" + + Route.ContributingWaysToContribute -> + markdownRender "# Ways To Contribute" + + Route.ContributingDevelopmentWorkflow -> + markdownRender "# Development Workflow" + + Route.ContributingReportingIssues -> + markdownRender "# Reporting Issues" + + Route.ContributingJoinTheCommunity -> + markdownRender "# Join the Community" Route.Commands command -> commandView command @@ -69,839 +220,163 @@ view session toSessionMsg model = } + +-- 0. OVERVIEW + + introductionView : List (Html msg) introductionView = - [ Html.h1 [] [ Html.text "An Introduction to Guida" ] - , Html.p [] - [ Html.strong [] [ Html.text "Guida is a functional language that compiles to JavaScript." ] - , Html.text " It helps you make websites and web apps. It has a strong emphasis on simplicity and quality tooling." - ] - , Html.p [] [ Html.text "This guide will:" ] - , Html.ul [] - [ Html.li [] [ Html.text "Teach you the fundamentals of programming in Guida." ] - , Html.li [] - [ Html.text "Show you how to make interactive apps with " - , Html.strong [] [ Button.view (Button.Link "https://guide.elm-lang.org/architecture") Button.Text Nothing [] [ Html.text "The Elm Architecture" ] ] - , Html.text "." - ] - , Html.li [] [ Html.text "Emphasize principles and patterns that generalize to programming in any language." ] - ] - , Html.p [] - [ Html.text "By the end we hope you will not only be able to create great web apps in Guida, but also understand the core ideas and patterns that make Guida nice to use." - ] - , Html.p [] - [ Html.text "If you are on the fence, we can safely guarantee that if you give Guida a shot and actually make a project in it, you will end up writing better JavaScript code. The ideas transfer pretty easily!" - ] - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "A Quick Sample" ] - , Html.p [] - [ Html.text "Here is a little program that lets you increment and decrement a number:" - ] - , CodeBlock.view """module Main exposing (main) + markdownRender """ +# Introduction -import Browser -import Html exposing (Html, button, div, text) -import Html.Events exposing (onClick) +Welcome to the **Guida Documentation** — your complete guide to understanding, using, +and contributing to the Guida programming language. +Guida builds upon the foundation of [Elm](https://elm-lang.org/), with a focus on +long-term stability, modern tooling, and an open ecosystem. -main = - Browser.sandbox { init = init, update = update, view = view } +This documentation is designed to help everyone — from developers trying Guida for +the first time, to teams adopting it, to contributors improving the language itself. +## What You'll Find Here -type alias Model = - Int - - -init : Model -init = - 0 - - -type Msg - = Increment - | Decrement - - -update : Msg -> Model -> Model -update msg model = - case msg of - Increment -> - model + 1 - - Decrement -> - model - 1 - - -view : Model -> Html Msg -view model = - div [] - [ button [ onClick Decrement ] [ text "-" ] - , div [] [ text (String.fromInt model) ] - , button [ onClick Increment ] [ text "+" ] - ]""" - , Html.p [] - [ Html.text "Try it out in the online editor " - , Button.view (Button.Link "/examples/buttons") Button.Text Nothing [] [ Html.text "here" ] - , Html.text "." - ] - , Html.p [] - [ Html.text "The code can definitely look unfamiliar at first, so we will get into how this example works soon!" - ] - , Html.h2 [ Attr.class "scroll-mt-12" ] - [ Html.text "Why a functional " - , Html.em [] [ Html.text "language" ] - , Html.text "?" - ] - , Html.p [] - [ Html.text "You can get some benefits from programming in a functional style, but there are some things you can only get from a functional language like Guida:" - ] - , Html.ul [] - [ Html.li [] [ Html.text "No runtime errors in practice." ] - , Html.li [] [ Html.text "Friendly error messages." ] - , Html.li [] [ Html.text "Reliable refactoring." ] - , Html.li [] [ Html.text "Automatically enforced semantic versioning for all Guida packages." ] - ] - , Html.p [] - [ Html.text "No combination of JS libraries can give you all of these guarantees. They come from the design of the language itself! And thanks to these guarantees, it is quite common for Guida programmers to say they never felt so " - , Html.strong [] [ Html.text "confident" ] - , Html.text " while programming. Confident to add features quickly. Confident to refactor thousands of lines. But without the background anxiety that you missed something important!" - ] - , Html.p [] - [ Html.text "Guida has a huge emphasis on making it easy to learn and use, so give Guida a shot and see what you think. We hope you will be pleasantly surprised!" - ] - , References.view - [ "https://guide.elm-lang.org" - ] - ] +This documentation is organized as a **book**, moving from high-level concepts to +practical guides and advanced topics. +Each section builds on the previous one, so you can read it in order or jump directly +to what you need. +### 1. Getting Started -syntaxView : List (Html msg) -syntaxView = - [ Html.h1 [] [ Html.text "Syntax" ] - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Comments" ] - , CodeBlock.view """-- a single line comment - -{- a multiline comment - {- can be nested -} --}""" - , Html.p [] [ Html.text "Here's a handy trick that every Guida programmer should know:" ] - , CodeBlock.view """{--} -add x y = x + y ---}""" - , Html.p [] - [ Html.text "Just add or remove the " - , Html.code [] [ Html.text "}" ] - , Html.text " on the first line and you'll toggle between commented and uncommented!" - ] - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Literals" ] - , CodeBlock.view """-- Boolean -True : Bool -False : Bool - -42 : number -- Int or Float depending on usage -3.14 : Float - -'a' : Char -"abc" : String - --- multi-line String -\"\"\" -This is useful for holding JSON or other -content that has "quotation marks". -\"\"\"""" - , Html.p [] [ Html.text "Typical manipulation of literals:" ] - , CodeBlock.view """True && not (True || False) -(2 + 4) * (4^2 - 9) -"abc" ++ "def\"""" - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Lists" ] - , Html.p [] [ Html.text "Here are three things that are equivalent:" ] - , CodeBlock.view """[ 1, 2, 3, 4 ] -1 :: [ 2, 3, 4 ] -1 :: 2 :: 3 :: 4 :: []""" - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Conditionals" ] - , CodeBlock.view "if powerLevel > 9000 then \"OVER 9000!!!\" else \"meh\"" - , Html.p [] [ Html.text "If you need to branch on many different conditions, you just chain this construct together." ] - , CodeBlock.view """if key == 40 then - n + 1 - - else if key == 38 then - n - 1 - - else - n""" - , Html.p [] [ Html.text "You can also have conditional behavior based on the structure of custom types and literals:" ] - , CodeBlock.view """ case maybeList of - Just xs -> xs - Nothing -> [] - - case xs of - [] -> - Nothing - first :: rest -> - Just (first, rest) - - case n of - 0 -> 1 - 1 -> 1 - _ -> fib (n-1) + fib (n-2)""" - , Html.p [] [ Html.text "Each pattern is indentation sensitive, meaning that you have to align all of your patterns." ] - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Records" ] - , Html.p [] - [ Html.text "For more explanation of Guida's record system, see " - , Button.view (Button.Link "/docs/records") Button.Text Nothing [] [ Html.text "this overview" ] - , Html.text ", or " - , Button.view (Button.Link "https://elm-lang.org/news/0.7") Button.Text Nothing [] [ Html.text "Elm's initial announcement." ] - ] - , CodeBlock.view """-- create records -origin = { x = 0, y = 0 } -point = { x = 3, y = 4 } - --- access fields -origin.x == 0 -point.x == 3 - --- field access function -List.map .x [ origin, point ] == [ 0, 3 ] - --- update a field -{ point | x = 6 } == { x = 6, y = 4 } - --- update many fields -{ point | x = point.x + 1, y = point.y + 1 }""" - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Functions" ] - , CodeBlock.view """square n = - n^2 - -hypotenuse a b = - sqrt (square a + square b) - -distance (a,b) (x,y) = - hypotenuse (a - x) (b - y)""" - , Html.p [] [ Html.text "Anonymous functions:" ] - , CodeBlock.view """square = - \\n -> n^2 - -squares = - List.map (\\n -> n^2) (List.range 1 100)""" - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Operators" ] - , Html.p [] - [ Html.text "In addition to the normal math operations for addition and subtraction, we have the " - , Button.view (Button.Link "https://package.elm-lang.org/packages/elm/core/latest/Basics#(%3C|)") Button.Text Nothing [] [ Html.text "(<|)" ] - , Html.text " and " - , Button.view (Button.Link "https://package.elm-lang.org/packages/elm/core/latest/Basics#(|%3E)") Button.Text Nothing [] [ Html.text "(|>)" ] - , Html.text " operators. They are aliases for function application, allowing you to write fewer parentheses." - ] - , CodeBlock.view """viewNames1 names = - String.join ", " (List.sort names) - -viewNames2 names = - names - |> List.sort - |> String.join ", " - --- (arg |> func) is the same as (func arg) --- Just keep repeating that transformation!""" - , Html.p [] [ Html.text "Historical note: this is borrowed from F#, inspired by Unix pipes." ] - , Html.p [] - [ Html.text "Relatedly, " - , Button.view (Button.Link "https://package.elm-lang.org/packages/elm/core/latest/Basics#(%3C%3C)") Button.Text Nothing [] [ Html.text "(<<)" ] - , Html.text " and " - , Button.view (Button.Link "https://package.elm-lang.org/packages/elm/core/latest/Basics#(%3E%3E)") Button.Text Nothing [] [ Html.text "(>>)" ] - , Html.text " are function composition operators." - ] - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Let Expressions" ] - , Html.p [] - [ Html.code [] [ Html.text "let" ] - , Html.text " these values be defined " - , Html.code [] [ Html.text "in" ] - , Html.text " this specific expression." - ] - , CodeBlock.view """ let - twentyFour = - 3 * 8 - - sixteen = - 4 ^ 2 - in - twentyFour + sixteen""" - , Html.p [] - [ Html.text "This is useful when an expression is getting large. You can make a " - , Html.code [] [ Html.text "let" ] - , Html.text " to break it into smaller definitions and put them all together " - , Html.code [] [ Html.text "in" ] - , Html.text " a smaller expression." - ] - , Html.p [] [ Html.text "You can define functions and use \"destructuring assignment\" in let expressions too." ] - , CodeBlock.view """ let - ( three, four ) = - ( 3, 4 ) - - hypotenuse a b = - sqrt (a^2 + b^2) - in - hypotenuse three four""" - , Html.p [] [ Html.text "Let-expressions are indentation sensitive, so each definition must align with the one above it." ] - , Html.p [] [ Html.text "Finally, you can add type annotations in let-expressions." ] - , CodeBlock.view """ let - name : String - name = - "Hermann" - - increment : Int -> Int - increment n = - n + 1 - in - increment 10""" - , Html.p [] [ Html.text "It is best to only do this on concrete types. Break generic functions into their own top-level definitions." ] - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Applying Functions" ] - , CodeBlock.view """-- alias for appending lists and two lists -append xs ys = xs ++ ys -xs = [1,2,3] -ys = [4,5,6] - --- All of the following expressions are equivalent: -a1 = append xs ys -a2 = xs ++ ys - -b2 = (++) xs ys - -c1 = (append xs) ys -c2 = ((++) xs) ys""" - , Html.p [] [ Html.text "The basic arithmetic infix operators all figure out what type they should have automatically." ] - , CodeBlock.view """23 + 19 : number -2.0 + 1 : Float - -6 * 7 : number -10 * 4.2 : Float - -100 // 2 : Int -1 / 2 : Float""" - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Modules" ] - , CodeBlock.view """module MyModule exposing (..) - --- qualified imports -import List -- List.map, List.foldl -import List as L -- L.map, L.foldl - --- open imports -import List exposing (..) -- map, foldl, concat, ... -import List exposing ( map, foldl ) -- map, foldl - -import Maybe exposing ( Maybe ) -- Maybe -import Maybe exposing ( Maybe(..) ) -- Maybe, Just, Nothing""" - , Html.p [] - [ Html.text "Qualified imports are preferred. Module names must match their file name, so module " - , Html.code [] [ Html.text "Parser.Utils" ] - , Html.text " needs to be in file " - , Html.code [] [ Html.text "Parser/Utils.guida" ] - , Html.text "." - ] - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Type Annotations" ] - , CodeBlock.view """answer : Int -answer = - 42 +Covers the basics of installing and running Guida, setting up your environment, and +compiling your first project. -factorial : Int -> Int -factorial n = - List.product (List.range 1 n) - -distance : { x : Float, y : Float } -> Float -distance {x,y} = - sqrt (x^2 + y^2)""" - , Html.p [] - [ Html.text "Learn how to read types and use type annotations " - , Button.view (Button.Link "/docs/types/reading-types") Button.Text Nothing [] [ Html.text "here" ] - , Html.text "." - ] - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Type Aliases" ] - , CodeBlock.view """type alias Name = String -type alias Age = Int - -info : (Name,Age) -info = - ("Steve", 28) - -type alias Point = { x:Float, y:Float } - -origin : Point -origin = - { x = 0, y = 0 }""" - , Html.p [] - [ Html.text "Learn more about type aliases " - , Button.view (Button.Link "/docs/types/type-aliases") Button.Text Nothing [] [ Html.text "here" ] - , Html.text "." - ] - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Custom Types" ] - , CodeBlock.view """type User - = Regular String Int - | Visitor String""" - , Html.p [] - [ Html.text "Not sure what this means? Read " - , Button.view (Button.Link "/docs/types/custom-types") Button.Text Nothing [] [ Html.text "this" ] - , Html.text "!" - ] - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "JavaScript Interop" ] - , CodeBlock.view """-- incoming values -port prices : (Float -> msg) -> Sub msg - --- outgoing values -port time : Float -> Cmd msg""" - , Html.p [] [ Html.text "From JS, you talk to these ports like this:" ] - , CodeBlock.view """var app = Elm.Example.init(); - -app.ports.prices.send(42); -app.ports.prices.send(13); - -app.ports.time.subscribe(callback); -app.ports.time.unsubscribe(callback);""" - , Html.p [] - [ Html.text "Read more about JavaScript interop " - , Button.view (Button.Link "/docs/interop") Button.Text Nothing [] [ Html.text "here" ] - , Html.text "." - ] - , References.view - [ "https://elm-lang.org/docs/syntax" - , "https://guide.elm-lang.org/core_language" - ] - ] +If you're new to the language, this is the best place to start. +### 2. The Language -fromJavaScriptOrElmView : List (Html msg) -fromJavaScriptOrElmView = - [ Html.h1 [] [ Html.text "From JavaScript or Elm?" ] - , Html.p [] [ Html.text "The following tables show side-by-side mappings between JavaScript, Elm and Guida. A lot of things are very similar, especially once you get used to the relatively minor syntactic difference." ] - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Literals" ] - , Table.view - [ [ Html.text "JavaScript" ] - , [ Html.text "Elm" ] - , [ Html.text "Guida" ] - ] - [ [ [ Html.code [] [ Html.text "3" ] ] - , [ Html.code [] [ Html.text "3" ] ] - , [ Html.code [] [ Html.text "3" ] ] - ] - , [ [ Html.code [] [ Html.text "3.1415" ] ] - , [ Html.code [] [ Html.text "3.1415" ] ] - , [ Html.code [] [ Html.text "3.1415" ] ] - ] - , [ [ Html.code [] [ Html.text "\"Hello world!\"" ] ] - , [ Html.code [] [ Html.text "\"Hello world!\"" ] ] - , [ Html.code [] [ Html.text "\"Hello world!\"" ] ] - ] - , [ [ Html.code [] [ Html.text "`multiline string`" ] ] - , [ Html.code [] [ Html.text "\"\"\"multiline string\"\"\"" ] ] - , [ Html.code [] [ Html.text "\"\"\"multiline string\"\"\"" ] ] - ] - , [ [ Html.code [] [ Html.text "'Hello world!'" ] ] - , [ Html.text "Cannot use single quotes for strings" ] - , [ Html.text "Cannot use single quotes for strings" ] - ] - , [ [ Html.text "No distinction between characters and strings" ] - , [ Html.code [] [ Html.text "'a'" ] ] - , [ Html.code [] [ Html.text "'a'" ] ] - ] - , [ [ Html.code [] [ Html.text "true" ] ] - , [ Html.code [] [ Html.text "True" ] ] - , [ Html.code [] [ Html.text "True" ] ] - ] - ] - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Objects / Records" ] - , Table.view - [ [ Html.text "JavaScript" ] - , [ Html.text "Elm" ] - , [ Html.text "Guida" ] - ] - [ [ [ Html.code [] [ Html.text "{ x: 3, y: 4 }" ] ] - , [ Html.code [] [ Html.text "{ x = 3, y = 4 }" ] ] - , [ Html.code [] [ Html.text "{ x = 3, y = 4 }" ] ] - ] - , [ [ Html.code [] [ Html.text "point.x" ] ] - , [ Html.code [] [ Html.text "point.x" ] ] - , [ Html.code [] [ Html.text "point.x" ] ] - ] - , [ [ Html.code [] [ Html.text "point.x = 42" ] ] - , [ Html.code [] [ Html.text "{ point | x = 42 }" ] ] - , [ Html.code [] [ Html.text "{ point | x = 42 }" ] ] - ] - ] - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Functions" ] - , Table.view - [ [ Html.text "JavaScript" ] - , [ Html.text "Elm" ] - , [ Html.text "Guida" ] - ] - [ [ [ Html.code [] [ Html.text "function(x, y) { return x + y; }" ] ] - , [ Html.code [] [ Html.text "\\x y -> x + y" ] ] - , [ Html.code [] [ Html.text "\\x y -> x + y" ] ] - ] - , [ [ Html.code [] [ Html.text "Math.max(3, 4)" ] ] - , [ Html.code [] [ Html.text "max 3 4" ] ] - , [ Html.code [] [ Html.text "max 3 4" ] ] - ] - , [ [ Html.code [] [ Html.text "Math.min(1, Math.pow(2, 4))" ] ] - , [ Html.code [] [ Html.text "min 1 (2^4)" ] ] - , [ Html.code [] [ Html.text "min 1 (2^4)" ] ] - ] - , [ [ Html.code [] [ Html.text "numbers.map(Math.sqrt)" ] ] - , [ Html.code [] [ Html.text "List.map sqrt numbers" ] ] - , [ Html.code [] [ Html.text "List.map sqrt numbers" ] ] - ] - , [ [ Html.code [] [ Html.text "points.map(function(p) { return p.x })" ] ] - , [ Html.code [] [ Html.text "List.map .x points" ] ] - , [ Html.code [] [ Html.text "List.map .x points" ] ] - ] - ] - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Control Flow" ] - , Table.view - [ [ Html.text "JavaScript" ] - , [ Html.text "Elm" ] - , [ Html.text "Guida" ] - ] - [ [ [ Html.code [] [ Html.text "3 > 2 ? 'cat' : 'dog'" ] ] - , [ Html.code [] [ Html.text "if 3 > 2 then \"cat\" else \"dog\"" ] ] - , [ Html.code [] [ Html.text "if 3 > 2 then \"cat\" else \"dog\"" ] ] - ] - , [ [ Html.code [] [ Html.text "var x = 42; ..." ] ] - , [ Html.code [] [ Html.text "let x = 42 in ..." ] ] - , [ Html.code [] [ Html.text "let x = 42 in ..." ] ] - ] - , [ [ Html.code [] [ Html.text "return 42" ] ] - , [ Html.text "Everything is an expression, no need for return" ] - , [ Html.text "Everything is an expression, no need for return" ] - ] - ] - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Strings" ] - , Table.view - [ [ Html.text "JavaScript" ] - , [ Html.text "Elm" ] - , [ Html.text "Guida" ] - ] - [ [ [ Html.code [] [ Html.text "'abc' + '123'" ] ] - , [ Html.code [] [ Html.text "\"abc\" ++ \"123\"" ] ] - , [ Html.code [] [ Html.text "\"abc\" ++ \"123\"" ] ] - ] - , [ [ Html.code [] [ Html.text "'abc'.length" ] ] - , [ Html.code [] [ Html.text "String.length \"abc\"" ] ] - , [ Html.code [] [ Html.text "String.length \"abc\"" ] ] - ] - , [ [ Html.code [] [ Html.text "'abc'.toUpperCase()" ] ] - , [ Html.code [] [ Html.text "String.toUpper \"abc\"" ] ] - , [ Html.code [] [ Html.text "String.toUpper \"abc\"" ] ] - ] - , [ [ Html.code [] [ Html.text "'abc' + 123" ] ] - , [ Html.code [] [ Html.text "\"abc\" ++ String.fromInt 123" ] ] - , [ Html.code [] [ Html.text "\"abc\" ++ String.fromInt 123" ] ] - ] - ] - , References.view - [ "https://elm-lang.org/docs/from-javascript" - ] - ] +An introduction to the syntax, types, and semantics of Guida — for those familiar +with [Elm](https://elm-lang.org/), this section highlights what's identical and what's evolving. +It also includes deeper dives into functions, types, modules, and language features. -guidaJsonView : List (Html msg) -guidaJsonView = - [ Html.h1 [] [ Html.text "guida.json" ] - , Html.p [] - [ Html.text "The " - , Html.code [] [ Html.text "guida.json" ] - , Html.text " describes your project. There are two different types of project: applications and packages." - ] - , Html.p [] - [ Html.text "This file is generated by the " - , Button.view (Button.Link "/docs/1.0.0/commands/init") Button.Text Nothing [] [ Html.text "guida init" ] - , Html.text " command, to simplify setup. To generate an application, run " - , Html.code [] [ Html.text "guida init" ] - , Html.text ". To generate a package, run " - , Html.code [] [ Html.text "guida init --package" ] - , Html.text "." - ] - , Html.p [] - [ Html.text "Depending on the type of project, the " - , Html.code [] [ Html.text "guida.json" ] - , Html.text " looks slightly different." - ] - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Application" ] - , Html.p [] - [ Html.text "When running " - , Html.code [] [ Html.text "guida init" ] - , Html.text " a " - , Html.code [] [ Html.text "guida.json" ] - , Html.text " file is generated with the following content:" - ] - , CodeBlock.view """{ - "type": "application", - "source-directories": [ - "src" - ], - "guida-version": "1.0.0", - "dependencies": { - "direct": { - "elm/browser": "1.0.2", - "elm/core": "1.0.5", - "elm/html": "1.0.0" - }, - "indirect": { - "elm/json": "1.1.4", - "elm/time": "1.0.0", - "elm/url": "1.0.0", - "elm/virtual-dom": "1.0.4" - } - }, - "test-dependencies": { - "direct": { - "elm-explorations/test": "2.2.0" - }, - "indirect": { - "elm/bytes": "1.0.8", - "elm/random": "1.0.0" - } - } -}""" - , Properties.view - [ { name = "\"type\"" - , type_ = Nothing - , children = - [ Html.text "Either " - , Html.code [] [ Html.text "\"application\"" ] - , Html.text " or " - , Html.code [] [ Html.text "\"package\"" ] - , Html.text ". All the other fields are based on this choice." - ] - } - , { name = "\"source-directories\"" - , type_ = Nothing - , children = - [ Html.text "A list of directories where Guida code lives. Most projects just use " - , Html.code [] [ Html.text "\"src\"" ] - , Html.text " for everything." - ] - } - , { name = "\"guida-version\"" - , type_ = Nothing - , children = - [ Html.text "The exact version of Guida this builds with. Should be " - , Html.code [] [ Html.text "\"1.0.0\"" ] - , Html.text " for most people!" - ] - } - , { name = "\"dependencies\"" - , type_ = Nothing - , children = - [ Html.p [] - [ Html.text "All the packages you depend upon. We use exact versions, so your " - , Html.code [] [ Html.text "guida.json" ] - , Html.text " file doubles as a \"lock file\" that ensures reliable builds." - ] - , Html.p [] - [ Html.text "You can use modules from any " - , Html.code [] [ Html.text "\"direct\"" ] - , Html.text " dependency in your code. Some " - , Html.code [] [ Html.text "\"direct\"" ] - , Html.text " dependencies have their own dependencies that folks typically do not care about. These are the " - , Html.code [] [ Html.text "\"indirect\"" ] - , Html.text " dependencies. They are listed explicitly so that (1) builds are reproducible and (2) you can easily review the quantity and quality of dependencies." - ] - ] - } - , { name = "\"test-dependencies\"" - , type_ = Nothing - , children = - [ Html.p [] - [ Html.text "All the packages that you use in " - , Html.code [] [ Html.text "tests/" ] - , Html.text " with " - , Html.code [] [ Html.text "guida test" ] - , Html.text " but not in the application you actually want to ship. This also uses exact versions to make tests more reliable." - ] - ] - } - ] - , Html.h2 [ Attr.class "scroll-mt-12" ] [ Html.text "Package" ] - , Html.p [] - [ Html.text "When running " - , Html.code [] [ Html.text "guida init --package" ] - , Html.text " a " - , Html.code [] [ Html.text "guida.json" ] - , Html.text " file is generated with the following content:" - ] - , CodeBlock.view """{ - "type": "package", - "name": "author/project", - "summary": "helpful summary of your project, less than 80 characters", - "license": "BSD-3-Clause", - "version": "1.0.0", - "exposed-modules": [], - "guida-version": "1.0.0 <= v < 2.0.0", - "dependencies": { - "elm/core": "1.0.5 <= v < 2.0.0" - }, - "test-dependencies": { - "elm-explorations/test": "2.2.0 <= v < 3.0.0" - } -}""" - , Properties.view - [ { name = "\"type\"" - , type_ = Nothing - , children = - [ Html.p [] - [ Html.text "Either " - , Html.code [] [ Html.text "\"application\"" ] - , Html.text " or " - , Html.code [] [ Html.text "\"package\"" ] - , Html.text ". All the other fields are based on this choice." - ] - ] - } - , { name = "\"name\"" - , type_ = Nothing - , children = - [ Html.p [] - [ Html.text "The name of a GitHub repo like " - , Html.code [] [ Html.text "\"elm/core\"" ] - , Html.text " or " - , Html.code [] [ Html.text "\"rtfeldman/elm-css\"" ] - , Html.text "." - ] - , Note.view - [ Html.strong [] [ Html.text "Note:" ] - , Html.text " We currently only support GitHub repos to ensure that there are no author name collisions. This seems like a pretty tricky problem to solve in a pleasant way. For example, do we have to keep an author name registry and give them out as we see them? But if someone is the same person on two platforms? And how to make this all happen in a way this is really nice for typical Elm users? Etc. So adding other hosting endpoints is harder than it sounds." - ] - ] - } - , { name = "\"summary\"" - , type_ = Nothing - , children = - [ Html.p [] - [ Html.text "A short summary that will appear on " - , Button.view (Button.Link "https://package.guida-lang.org") Button.Text Nothing [] [ Html.text "package.guida-lang.org" ] - , Html.text " that describes what the package is for. Must be under 80 characters." - ] - ] - } - , { name = "\"license\"" - , type_ = Nothing - , children = - [ Html.p [] - [ Html.text "An OSI approved SPDX code like " - , Html.code [] [ Html.text "\"BSD-3-Clause\"" ] - , Html.text " or " - , Html.code [] [ Html.text "\"MIT\"" ] - , Html.text ". These are the two most common licenses in the Elm ecosystem, but you can see the full list of options " - , Button.view (Button.Link "https://spdx.org/licenses") Button.Text Nothing [] [ Html.text "here" ] - , Html.text "." - ] - ] - } - , { name = "\"version\"" - , type_ = Nothing - , children = - [ Html.p [] - [ Html.text "All packages start at " - , Html.code [] [ Html.text "\"1.0.0\"" ] - , Html.text " and from there, Guida automatically enforces semantic versioning by comparing API changes." - ] - , Html.p [] - [ Html.text "So if you make a PATCH change and call " - , Html.code [] [ Html.text "guida bump" ] - , Html.text " it will update you to " - , Html.code [] [ Html.text "\"1.0.1\"" ] - , Html.text ". And if you then decide to remove a function (a MAJOR change) and call " - , Html.code [] [ Html.text "guida bump" ] - , Html.text " it will update you to " - , Html.code [] [ Html.text "\"2.0.0\"" ] - , Html.text ". Etc." - ] - ] - } - , { name = "\"exposed-modules\"" - , type_ = Nothing - , children = - [ Html.p [] - [ Html.text "A list of modules that will be exposed to people using your package. The order you list them will be the order they appear on " - , Button.view (Button.Link "https://package.guida-lang.org") Button.Text Nothing [] [ Html.text "package.guida-lang.org" ] - , Html.text "." - ] - , Html.p [] - [ Html.strong [] [ Html.text "Note:" ] - , Html.text " If you have five or more modules, you can use a labelled list like " - , Button.view (Button.Link "https://github.com/elm/core/blob/1.0.5/elm.json") Button.Text Nothing [] [ Html.text "this" ] - , Html.text ". We show the labels on the package website to help people sort through larger packages with distinct categories. Labels must be under 20 characters." - ] - ] - } - , { name = "\"guida-version\"" - , type_ = Nothing - , children = - [ Html.p [] - [ Html.text "The range of Elm compilers that work with your package. Right now " - , Html.code [] [ Html.text "\"0.19.0 <= v < 0.20.0\"" ] - , Html.text " is always what you want for this." - ] - ] - } - , { name = "\"dependencies\"" - , type_ = Nothing - , children = - [ Html.p [] - [ Html.text "A list of packages that you depend upon. In each application, there can only be one version of each package, so wide ranges are great. Fewer dependencies is even better though!" - ] - , Note.view - [ Html.strong [] [ Html.text "Note:" ] - , Html.text " Dependency ranges should only express tested ranges. It is not nice to use optimistic ranges and end up causing build failures for your users down the line. Eventually we would like to have an automated system that tries to build and test packages as new packages come out. If it all works, we could send a PR to the author widening the range." - ] - ] - } - , { name = "\"test-dependencies\"" - , type_ = Nothing - , children = - [ Html.text "Dependencies that are only used in the " - , Html.code [] [ Html.text "tests/" ] - , Html.text " directory by " - , Html.code [] [ Html.text "guida test" ] - , Html.text ". Values from these packages will not appear in any final build artifacts." - ] - } - ] - , References.view - [ "https://github.com/elm/compiler/blob/0.19.1/docs/elm.json/application.md" - , "https://github.com/elm/compiler/blob/0.19.1/docs/elm.json/package.md" - , "https://gren-lang.org/book/appendix/gren_json/" - ] - ] +### 3. Core Concepts +Explore the key ideas that shape Guida's design — from its functional programming +foundations to its type system and immutability model. +This section explains how Guida relates to [Elm](https://elm-lang.org/), and what makes +its approach to clarity and composition distinct. -recordsView : List (Html msg) -recordsView = - [ Html.h1 [] [ Html.text "Records" ] - , References.view - [ "https://elm-lang.org/docs/records" - ] - ] +### 4. Building Applications +Understand how to organize Guida projects, structure modules, manage dependencies, and +build real-world applications. +Includes patterns for architecture, testing, and interop with JavaScript code. -interopView : List (Html msg) -interopView = - [ Html.h1 [] [ Html.text "JavaScript Interop" ] - , References.view - [ "https://elm-lang.org/docs/records" - ] - ] +### 5. Commands + +A complete reference for the Guida CLI — including all supported commands, options, +and common workflows. + +### 6. Contributing + +Explains how to contribute to Guida itself: from working on the compiler, to testing, +documentation, and ecosystem packages. + +It also describes the principles behind Guida's self-hosted development model. + +### 7. Advanced Topics + +For readers who want to dive deeper into how Guida works under the hood. + +## How to Use This Documentation + +You can approach this guide in two ways: + +- **As a learner:** Read from start to finish to understand Guida's philosophy, syntax, +and tooling. +- **As a reference:** Jump to sections using the sidebar or search to look up specific +commands, examples, or design details. + +Every section includes links to related topics, examples, and source code, so you can +explore at your own pace. + +## Staying Up to Date + +Guida is an evolving project. + +As the language, compiler, and ecosystem grow, this documentation will evolve too — with +notes marking version changes and upcoming features. + +You can follow ongoing discussions and announcements on +the [Guida Discord server](https://discord.gg/Ur33engz). + """ + + +whatIsGuidaView : List (Html msg) +whatIsGuidaView = + markdownRender + """ +# What is Guida? + +**Guida** is a functional programming language that builds upon the foundation of [Elm](https://elm-lang.org/), offering **backward compatibility with all existing Elm 0.19.1 projects**. + +Guida's main goal is to provide a language and toolchain that feel familiar to Elm developers while enabling broader adoption in professional and team environments. +It aims to give companies confidence to use and invest in a language that is reliable, maintainable, and evolving in an open way. + +Join the [Guida Discord server](https://discord.gg/Ur33engz) to connect with the community, ask questions, and share ideas. + +## Vision + +Guida builds on the foundations of [Elm](https://elm-lang.org/), aiming to advance the future of functional programming. +By translating Elm's compiler from Haskell to a self-hosted environment, Guida helps developers to +build reliable, maintainable, and performant applications without leaving the language they love. + +**Continuity and Confidence (Version 0.x):** +Guida starts by ensuring full backward compatibility with Elm v0.19.1, allowing developers to migrate +effortlessly and explore Guida with complete confidence. + +This commitment to continuity means that this version will faithfully replicate not only the +features and behaviors of Elm v0.19.1, but also any existing bugs and quirks. +By doing so, we provide a stable and predictable environment for developers, ensuring that their +existing Elm projects work exactly as expected when migrated to Guida. + +**Evolution and Innovation (Version 1.x and Beyond):** +As Guida evolves, we will introduce new features and improvements. +This phase will foster a unified ecosystem that adapts to the needs of its users. + +**Core Principles:** + +- **Backward Compatibility:** Respect for existing Elm projects, ensuring a frictionless migration. +- **Accessibility:** Lowering barriers for developers by implementing Guida's core in its own syntax. + +Our ultimate goal is to create a language that inherits the best aspects of Elm while adapting and +growing to meet the needs of its users. +""" commandView : Route.Command -> List (Html msg) commandView command = case command of Route.Repl -> - [ Html.h1 [] [ Html.text "guida repl" ] - , Html.p [] [ Html.text "The REPL lets you interact with Guida values and functions in your terminal." ] - , Html.p [] [ Html.text "You can type in expressions, definitions, custom types, and module imports using normal Guida syntax." ] - , CodeBlock.view """> 1 + 1 + markdownRender """ +# guida repl + +The REPL lets you interact with Guida values and functions in your terminal. + +You can type in expressions, definitions, custom types, and module imports using normal Guida syntax. + +```guida +> 1 + 1 2 : number > "hello" ++ "world" -"helloworld" : String""" - , Html.p [] [ Html.text "The same can be done with definitions and custom types:" ] - , CodeBlock.view """> fortyTwo = 42 +"helloworld" : String +``` + +The same can be done with definitions and custom types: + +```guida +> fortyTwo = 42 42 : number > increment n = n + 1 @@ -930,65 +405,61 @@ commandView command = | Regular name -> "Hey again!" | Visitor name -> "Nice to meet you!" | -"Hey again!" : String""" - , Html.p [] - [ Html.text "When you run " - , Html.code [] [ Html.text "guida repl" ] - , Html.text " in a project with an " - , Button.view (Button.Link "/docs/guida-json") Button.Text Nothing [] [ Html.text "guida.json" ] - , Html.text " file, you can import any module available in the project. So if your project has an " - , Html.code [] [ Html.text "elm/html" ] - , Html.text " dependency, you could say:" - ] - , CodeBlock.view """> import Html exposing (Html) +"Hey again!" : String +``` +When you run `guida repl` in a project with a [/docs/guida-json](guida.json) file, you can +import any module available in the project. So if your project has an `elm/html` dependency, +you could say: + +```guida +> import Html exposing (Html) > Html.text "hello" : Html msg - > Html.text - : String -> Html msg""" - , Html.p [] - [ Html.text "If you create a module in your project named " - , Html.code [] [ Html.text "MyThing" ] - , Html.text " in your project, you can say " - , Html.code [] [ Html.text "import MyThing" ] - , Html.text " in the REPL as well. Any module that is accessible in your project should be accessible in the REPL." - ] - , Html.hr [] [] - , Html.h2 [ Attr.class "scroll-mt-24" ] [ Html.text "Exit" ] - , Html.p [] - [ Html.text "To exit the REPL, you can type " - , Html.code [] [ Html.text ":exit" ] - , Html.text "." - ] - , Html.p [] - [ Html.text "You can also press " - , Html.code [] [ Html.text "ctrl-d" ] - , Html.text " or " - , Html.code [] [ Html.text "ctrl-c" ] - , Html.text " on some platforms." - ] - , Html.hr [] [] - , Html.h2 [ Attr.class "scroll-mt-24" ] [ Html.text "Flags" ] - , Html.p [] [ Html.text "You can customize this command with the following flags:" ] - , Properties.view - [ { name = "--interpreter=" - , type_ = Nothing - , children = [ Html.text "Path to a alternate JS interpreter, like node or nodejs." ] - } - , { name = "--no-colors" - , type_ = Nothing - , children = [ Html.text "Turn off the colors in the REPL. This can help if you are having trouble reading the values. Some terminals use a custom color scheme that diverges significantly from the standard ANSI colors, so another path may be to pick a more standard color scheme." ] - } - ] - , References.view - [ "https://github.com/elm/compiler/blob/0.19.1/hints/repl.md" - ] - ] + : String -> Html msg +``` + +If you create a module in your project named `MyThing` in your project, you can say +`import MyThing` in the REPL as well. Any module that is accessible in your project +should be accessible in the REPL. + +--- + +## Exit + +To exit the REPL, you can type `:exit`. + +You can also press `ctrl-d` or `ctrl-c` on some platforms. + +--- + +## Flags + +You can customize this command with the following flags: + + + Path to a alternate JS interpreter, like node or nodejs. + Turn off the colors in the REPL. This can help if you are having trouble reading the values. Some terminals use a custom color scheme that diverges significantly from the standard ANSI colors, so another path may be to pick a more standard color scheme. + + +--- + +## References + +- +""" Route.Init -> - [ Html.h1 [] [ Html.text "guida init" ] - ] + markdownRender """ +# guida init + +--- + +## References + +- +""" Route.Make -> [ Html.h1 [] [ Html.text "guida make" ] @@ -1026,63 +497,1700 @@ commandView command = hintView : Route.Hint -> List (Html msg) hintView hint = case hint of - _ -> - [] - - -sidebarNavigation : Model -> Navigation -sidebarNavigation model = - let - activeCommand : Route.Command -> Bool - activeCommand cmd = - model.section == Route.Commands cmd - - activeHint : Route.Hint -> Bool - activeHint hint = - model.section == Route.Hints hint - in - [ { title = "Guide" - , links = - [ { title = "Introduction", href = "/docs", active = model.section == Route.Introduction } - , { title = "Syntax", href = "/docs/syntax", active = model.section == Route.Syntax } - , { title = "From JavaScript or Elm?", href = "/docs/from-javascript-or-elm", active = model.section == Route.FromJavaScriptOrElm } - , { title = "guida.json", href = "/docs/guida-json", active = model.section == Route.GuidaJson } - , { title = "Records", href = "/docs/records", active = model.section == Route.Records } - - -- guide.elm-lang.org - , { title = "JavaScript Interop", href = "/docs/interop", active = model.section == Route.Interop } - ] - } - , { title = "Commands" + Route.BadRecursion -> + markdownRender """ +# Bad Recursion + +There are two problems that will lead you here, both of them pretty tricky: + + 1. [**No Mutation**](#no-mutation) - Defining values in Guida is slightly different than + defining values in languages like JavaScript. + 2. [**Tricky Recursion**](#tricky-recursion) - Sometimes you need to define recursive values + when creating generators, decoders, and parsers. A common case is a JSON decoder a discussion + forums where a comment may have replies, which may have replies, which may have replies, etc. + +## No Mutation + +Languages like JavaScript let you "reassign" variables. When you say `x = x + 1` it means: +whatever `x` was pointing to, have it point to `x + 1` instead. This is called *mutating* a +variable. All values are immutable in Guida, so reassigning variables does not make any sense! +Okay, so what *should* `x = x + 1` mean in Guida? + +Well, what does it mean with functions? In Guida, we write recursive functions like this: + +```guida +factorial : Int -> Int +factorial n = + if n <= 0 then 1 else n * factorial (n - 1) +``` + +One cool thing about Guida is that whenever you see `factorial 3`, you can always replace that +expression with `if 3 <= 0 then 1 else 3 * factorial (3 - 1)` and it will work exactly the same. +So when Guida code gets evaluated, we will keep expanding `factorial` until the `if` produces a 1. +At that point, we are done expanding and move on. + +The thing that surprises newcomers is that recursion works the same way with values too. So +take the following definition: + +```guida +x = x + 1 +``` + +We are actually defining `x` in terms of itself. So it would expand out to `x = ... + 1 + 1 + 1 + 1`, +trying to add one to `x` an infinite number of times! This means your program would just +run forever, endlessly expanding `x`. In practice, this means the page freezes and the computer +starts to get kind of warm. No good! We can detect cases like this with the compiler, so we +give an error at compile time so this does not happen in the wild. + +The fix is usually to just give the new value a new name. So you could rewrite it to: + +```guida +x1 = x + 1 +``` + +Now `x` is the old value and `x1` is the new value. Again, one cool thing about Guida is +that whenever you see a `factorial 3` you can safely replace it with its definition. +Well, the same is true of values. Wherever I see `x1`, I can replace it with `x + 1`. +Thanks to the way definitions work in Guida, this is always safe! + +## Tricky Recursion + +Now, there are some cases where you *do* want a recursive value. Say you are building a +website with comments and replies. You may define a comment like this: + +```guida +type alias Comment = + { message : String + , upvotes : Int + , downvotes : Int + , responses : Responses + } + +type Responses = + Responses (List Comment) +``` + +You may have run into this definition in the [hints for recursive aliases](/docs/1.0.0/hints/recursive-alias)! +Anyway, once you have comments, you may want to turn them into JSON to send back to your +server or to store in your database or whatever. So you will probably write some code like this: + +```guida +import Json.Decode as Decode exposing (Decoder) + +decodeComment : Decoder Comment +decodeComment = + Decode.map4 Comment + (Decode.field "message" Decode.string) + (Decode.field "upvotes" Decode.int) + (Decode.field "downvotes" Decode.int) + (Decode.field "responses" decodeResponses) + +-- PROBLEM +decodeResponses : Decoder Responses +decodeResponses = + Decode.map Responses (Decode.list decodeComment) +``` + +The problem is that now `decodeComment` is defined in terms of itself! To know what +`decodeComment` is, I need to expand `decodeResponses`. To know what `decodeResponses` is, +I need to expand `decodeComment`. This loop will repeat endlessly! + +In this case, the trick is to use `Json.Decode.lazy` which delays the evaluation of +a decoder until it is needed. So the valid definition would look like this: + +```guida +import Json.Decode as Decode exposing (Decoder) + +decodeComment : Decoder Comment +decodeComment = + Decode.map4 Comment + (Decode.field "message" Decode.string) + (Decode.field "upvotes" Decode.int) + (Decode.field "downvotes" Decode.int) + (Decode.field "responses" decodeResponses) + +-- SOLUTION + +decodeResponses : Decoder Responses +decodeResponses = + Decode.map Responses (Decode.list (Decode.lazy (\\_ -> decodeComment))) +``` + +Notice that in `decodeResponses`, we hide `decodeComment` behind an anonymous function. +Guida cannot evaluate an anonymous function until it is given arguments, so it allows us +to delay evaluation until it is needed. If there are no comments, we will not need to expand it! + +This saves us from expanding the value infinitely. Instead we only expand the value if +we need to. + +> **Note:** The same kind of logic can be applied to tasks, random value generators, and +parsers. Use `lazy` or `andThen` to make sure a recursive value is only expanded if needed. + +## Understanding "Bad Recursion" + +The compiler tries to detect bad recursion, but how does it know the difference between +good and bad situations? Writing `factorial` is fine, but writing `x = x + 1` is not. One version +of `decodeComment` was bad, but the other was fine. What is the rule? + +**Guida will allow recursive definitions as long as there is at least one lambda before +you get back to yourself. **So if we write `factorial` without any pretty syntax, it looks +like this: + +```guida +factorial = + \\n -> if n <= 0 then 1 else n * factorial (n - 1) +``` + +There is technically a lambda between the definition and the use, so it is okay! The +same is true with the good version of `decodeComment`. There is a lambda between the +definition and the use. As long as there is a lambda before you get back to yourself, +the compiler will let it through. + +**This rule is nice, but it does not catch everything.** It is pretty easy to write a +definition where the recursion is hidden behind a lambda, but it still immediately expands forever: + +```guida +x = + (\\_ -> x) () + 1 +``` + +This follows the rules, but it immediately expands until our program runs out of stack +space. It leads to a runtime error as soon as you start your program. It is nice to fail fast, +but why not have the compiler detect this as well? It turns out this is much harder than it sounds! + +This is called [the halting problem](https://en.wikipedia.org/wiki/Halting_problem) in +computer science. Computational theorists were asking: + +> Can we determine if a program will finish running (i.e. halt) or if it will continue to +run forever? + +It turns out that Alan Turing wrote a proof in 1936 showing that (1) in some cases you just +have to check by running the program and (2) this check will take forever for programs that +do not halt! + +**So we cannot solve the halting problem *in general*, but our simple rule about lambdas +can detect the majority of bad cases *in practice*.** + +--- + +## References + +- +""" + + Route.ComparingCustomTypes -> + markdownRender """ +# Comparing Custom Types + +The built-in comparison operators work on a fixed set of types, like `Int` and `String`. +That covers a lot of cases, but what happens when you want to compare custom types? + +This page aims to catalog these scenarios and offer alternative paths that can get you unstuck. + +## Wrapped Types + +It is common to try to get some extra type safety by creating really simple custom types: + +```guida +type Id = Id Int +type Age = Age Int + +type Comment = Comment String +type Description = Description String +``` + +By wrapping the primitive values like this, the type system can now help you make +sure that you never mix up a `Id` and an `Age`. Those are different types! This trick +is extra cool because it has no runtime cost in `--optimize` mode. The compiler can just +use an `Int` or `String` directly when you use that flag! + +The problem arises when you want to use a `Id` as a key in a dictionary. This is a totally +reasonable thing to do, but the current version of Guida cannot handle this scenario. + +Instead of creating a `Dict Id Info` type, one thing you can do is create a custom data +structure like this: + +```guida +module User exposing (Id, Table, empty, get, add) + +import Dict exposing (Dict) + + +-- USER + +type Id = Id Int + + +-- TABLE + +type Table info = + Table Int (Dict Int info) + +empty : Table info +empty = + Table 0 Dict.empty + +get : Id -> Table info -> Maybe info +get (Id id) (Table _ dict) = + Dict.get id dict + +add : info -> Table info -> (Table info, Id) +add info (Table nextId dict) = + ( Table (nextId + 1) (Dict.insert nextId info dict) + , Id nextId + ) +``` + +There are a couple nice things about this approach: + +1. The only way to get a new `User.Id` is to `add` information to a `User.Table`. +2. All the operations on a `User.Table` are explicit. Does it make sense to remove users? + To merge two tables together? Are there any special details to consider in those cases? + This will always be captured explicitly in the interface of the `User` module. +3. If you ever want to switch the internal representation from `Dict` to `Array` or + something else, it is no problem. All the changes will be within the `User` module. + +So while this approach is not as convenient as using a `Dict` directly, it has some benefits +of its own that can be helpful in some cases. + +## Enumerations to Ints + +Say you need to define a `trafficLightToInt` function: + +```guida +type TrafficLight = Green | Yellow | Red + +trafficLightToInt : TrafficLight -> Int +trafficLightToInt trafficLight = + ??? +``` + +We have heard that some people would prefer to use a dictionary for this sort of thing. +That way you do not need to write the numbers yourself, they can be generated such that +you never have a typo. + +I would recommend using a `case` expression though: + +```guida +type TrafficLight = Green | Yellow | Red + +trafficLightToInt : TrafficLight -> Int +trafficLightToInt trafficLight = + case trafficLight of + Green -> 1 + Yellow -> 2 + Red -> 3 +``` + +This is really straight-forward while avoiding questions like “is `Green` less than or +greater than `Red`?” + +## Something else? + +If you have some other situation, please tell us about it [here](https://github.com/guida-lang/guida-lang.org/issues). +We can use the particulars of your scenario to add more advice on this page! + +--- + +## References + +- +""" + + Route.ComparingRecords -> + markdownRender """ +# Comparing Records + +The built-in comparison operators work on a fixed set of types, like `Int` and `String`. +That covers a lot of cases, but what happens when you want to compare records? + +This page aims to catalog these scenarios and offer alternative paths that can get you unstuck. + +## Sorting Records + +Say we want a `view` function that can show a list of students sorted by different characteristics. + +We could create something like this: + +```guida +import Html exposing (..) + +type alias Student = + { name : String + , age : Int + , gpa : Float + } + +type Order = Name | Age | GPA + +viewStudents : Order -> List Student -> Html msg +viewStudents order students = + let + orderlyStudents = + case order of + Name -> List.sortBy .name students + Age -> List.sortBy .age students + GPA -> List.sortBy .gpa students + in + ul [] (List.map viewStudent orderlyStudents) + +viewStudent : Student -> Html msg +viewStudent student = + li [] [ text student.name ] +``` + +If you are worried about the performance of changing the order or updating information +about particular students, you can start using the [`Html.Lazy`](https://package.elm-lang.org/packages/elm/html/latest/Html-Lazy) +and [`Html.Keyed`](https://package.elm-lang.org/packages/elm/html/latest/Html-Keyed) modules. +The updated code would look something like this: + +```guida +import Html exposing (..) +import Html.Lazy exposing (lazy) +import Html.Keyed as Keyed + +type Order = Name | Age | GPA + +type alias Student = + { name : String + , age : Int + , gpa : Float + } + +viewStudents : Order -> List Student -> Html msg +viewStudents order students = + let + orderlyStudents = + case order of + Name -> List.sortBy .name students + Age -> List.sortBy .age students + GPA -> List.sortBy .gpa students + in + Keyed.ul [] (List.map viewKeyedStudent orderlyStudents) + +viewKeyedStudent : Student -> (String, Html msg) +viewKeyedStudent student = + ( student.name, lazy viewStudent student ) + +viewStudent : Student -> Html msg +viewStudent student = + li [] [ text student.name ] +``` + +By using `Keyed.ul` we help the renderer move the DOM nodes around based on their key. +This makes it much cheaper to reorder a bunch of students. And by using `lazy` we help +the renderer skip a bunch of work. If the `Student` is the same as last time, the render +can skip over it. + +> **Note:** Some people are skeptical of having logic like this in `view` functions, but +> I think the alternative (maintaining sort order in your `Model`) has some serious downsides. +> Say a colleague is adding a message to `Add` students, but they do not know about the sort +> order rules needed for presentation. Bug! So in this alternate design, you must diligently +> test your `update` function to make sure that no message disturbs the sort order. This is bound +> to lead to bugs over time! +> +> With all the optimizations possible with `Html.Lazy` and `Html.Keyed`, I would always +> be inclined to work on optimizing my `view` functions rather than making my `update` +> functions more complicated and error prone. + +## Something else? + +If you have some other situation, please tell us about it [here](https://github.com/guida-lang/guida-lang.org/issues). +We can use the particulars of your scenario to add more advice on this page! + +--- + +## References + +- +""" + + Route.ImplicitCasts -> + markdownRender """ +# Implicit Casts + +Many languages automatically convert from `Int` to `Float` when they think it is necessary. +This conversion is often called an [implicit cast](https://en.wikipedia.org/wiki/Type_conversion). + +Languages that will add in implicit casts for addition include: + +- JavaScript +- Python +- Ruby +- C +- C++ +- C# +- Java +- Scala + +These languages generally agree that an `Int` may be implicitly cast to a `Float` when necessary. +So everyone is doing it, why not Guida?! + +## Type Inference + Implicit Casts + +Guida comes from the ML-family of languages. Languages in the ML-family that **never** do +implicit casts include: + +- Standard ML +- OCaml +- Elm +- F# +- Haskell + +Why would so many languages from this lineage require explicit conversions though? + +Well, we have to go back to the 1970s for some background. J. Roger Hindley and Robin Milner +independently discovered an algorithm that could _efficiently_ figure out the type of +everything in your program without any type annotations. Type Inference! Every ML-family +language has some variation of this algorithm at the center of its design. + +For decades, the problem was that nobody could figure out how to combine type inference +with implicit casts AND make the resulting algorithm efficient enough for daily use. +As far as I know, Scala was the first widely known language to figure out how to combine +these two things! Its creator, Martin Odersky did a lot of work on combining type inference +and subtyping to make this possible. + +So for any ML-family language designed before Scala, it is safe to assume that implicit +conversions just was not an option. Okay, but what about Guida?! It comes after Scala, so why +not do it like them?! + +1. You pay performance cost to mix type inference and implicit conversions. At least as far + as anyone knows, it defeats an optimization that is crucial to getting _reliably_ good + performance. It is fine in most cases, but it can be a real issue in very large code bases. +2. Based on experience reports from Scala users, it seemed like the convenience was not worth + the hidden cost. Yes, you can convert `n` in `(n + 1.5)` and everything is nice, but when you + are in larger programs that are sparsely annotated, it can be quite difficult to figure out + what is going on. + +This user data may be confounded by the fact that Scala allows quite extensive conversions, +not just from `Int` to `Float`, but I think it is worth taking seriously nonetheless. +So it is _possible_, but it has tradeoffs. + +## Conclusion + +First, based on the landscape of design possibilities, it seems like requiring _explicit_ +conversions is a pretty nice balance. We can have type inference, it can produce friendly +error messages, the algorithm is snappy, and an unintended implicit cast will not flow +hundreds of lines before manifesting to the user. + +Second, Guida very much favors explicit code, so this also fits in with the overall spirit +of the language and libraries. + +I hope that clarifies why you have to add those `toFloat` and `round` functions! It definitely +can take some getting used to, but there are tons of folks who get past that acclimation +period and really love the tradeoffs! + +--- + +## References + +- +""" + + Route.ImportCycles -> + markdownRender """ +# Import Cycles + +What is an import cycle? In practice you may see it if you create two modules with +interrelated `User` and `Comment` types like this: + +```guida +module Comment exposing (..) + +import User + +type alias Comment = + { comment : String + , author : User.User + } +``` + +```guida +module User exposing (..) + +import Comment + +type alias User = + { name : String + , comments : List Comment.Comment + } +``` + +Notice that to compile `Comment` we need to `import User`. And notice that to compile `User` +we need to `import Comment`. We need both to compile either! + +Now this is *possible* if the compiler figures out any module cycles and puts them all +in one big file to compile them together. That seems fine in our small example, but imagine we +have a cycle of 20 modules. If you change *one* of them, you must now recompile *all* of +them. In a large code base, this causes extremely long compile times. It is also very hard +to disentangle them in practice, so you just end up with slow builds. That is your life now. + +The thing is that you can always write the code *without* cycles by shuffling declarations +around, and the resulting code is often much clearer. + +## How to Break Cycles + +There are quite a few ways to break our `Comment` and `User` cycle from above, so let's go +through four useful strategies. The first one is by far the most common solution! + +### 1. Combine the Modules + +One approach is to just combine the two modules. If we check out the resulting code, +we have actually revealed a problem in how we are representing our data: + +```guida +module BadCombination1 exposing (..) + +type alias Comment = + { comment : String + , author : User + } + +type alias User = + { name : String + , comments : List Comment + } +``` + +Notice that the `Comment` type alias is defined in terms of the `User` type alias and vice versa. +Having recursive type aliases like this does not work! That problem is described in depth +[here](/docs/1.0.0/hints/recursive-alias), but the quick takeaway is that one `type alias` +needs to become a `type` to break the recursion. So let's try again: + +```guida +module BadCombination2 exposing (..) + +type alias Comment = + { comment : String + , author : User + } + +type alias User = + { name : String + , comments : AllUserComments + } + +type AllUserComments = AllUserComments (List Comment) +``` + +Okay, now we have broken the recursion, but we need to ask ourselves, how are we going to actually +instantiate these `Comment` and `User` types that we have described. A `Comment` will always +have an author, and that `User` will always refer back to the `Comment`. So we seem to want +cyclic data here. If we were in JavaScript we might instantiate all the comments in one pass, +and then go back through and mutate the users to point to all the relevant comments. +In other words, we need *mutation* to create this cyclic data! + +All values are immutable in Guida, so we need to use a more functional strategy. +One common approach is to use unique identifiers. Instead of referring directly to "the user object" +we can refer to a user ID: + +```guida +module GoodCombination exposing (..) + +import Dict + +type alias Comment = + { comment : String + , author : UserId + } + +type alias UserId = String + +type alias AllComments = + Dict.Dict UserId (List Comment) +``` + +Now in this world, we do not even have cycles in our types anymore! That means we can actually +break these out into separate modules again: + +```guida +module Comment exposing (..) + +import Dict +import User + +type alias Comment = + { comment : String + , author : User.Id + } + +type alias AllComments = + Dict.Dict User.Id (List Comment) +``` + +```guida +module User exposing (..) + +type alias Id = String +``` + +So now we are back to the two modules we wanted, but we have data structures that are +going to work much better in a functional language like Guida! **This is the common approach, +and it is what you hope will happen!** + +### 2. Make a New Module + +Now say there are actually a ton of functions and values in the `Comment` and `User` modules. +Combining them into one does not seem like a good strategy. Instead you can create a *third* +module that just has the shared types and functions. Let's pretend we call that third +module `GoodCombination`. So rather than having `Comment` and `User` depend on each other, +they now both depend on `GoodCombination`. We broke our cycle! + +**This strategy is less common.** You generally want to keep the core `type` of a module with +all the functions that act upon it directly, so separating a `type` from everything else is a +bad sign. So maybe there is a `User` module that contains a bunch of helper functions, but you +*use* all those helper functions in a bunch of other modules that interact with users in +various ways. In that scenario, it is still more sophisticated than "just throw the types in a +module together" and hope it turns out alright. + +### 3. Use Type Variables + +Another way to avoid module cycles is to be more generic in how you represent your data: + +```guida +module Comment exposing (..) + +type alias Comment author = + { comment : String + , author : author + } +``` + +```guida +module User exposing (..) + +type alias User comment = + { name : String + , comments : List comment + } +``` + +Notice that `Comment` and `User` no longer need to import each other! Instead, whenever we use +these modules, we need to fill in the type variable. So we may import both `Comment` and `User` +and try to combine them into a `Comment (User (Comment (User ...)))`. Gah, we ran into the +recursive type alias thing again! + +So this strategy fails pretty badly with our particular example. The code is more complicated +and it still does not work! So **this strategy is rarely useful**, but when it works, it can +simplify things quite a lot. + +### 4. Hiding Implementation Details in Packages + +This gets a little bit trickier when you are creating a package like `elm-lang/parser` which is +built around the `Parser` type. + +That package has a couple exposed modules: `Parser`, `Parser.LanguageKit`, and `Parser.LowLevel`. +All of these modules want access to the internal details of the `Parser` type, but we do +not want to ever expose those internal details to the *users* of this package. So where should +the `Parser` type live?! + +Usually you know which module should expose the type for the best public API. In this case, +it makes sense for it to live in the `Parser` module. The way to manage this is to create +a `Parser.Internal` module with a definition like: + +```guida +module Parser.Internal exposing (..) + +type Parser a = + Parser ... +``` + +Now we can `import Parser.Internal` and use it in any of the modules in our package. +The trick is that we never expose the `Parser.Internal` module to the *users* of our package. +We can see what is inside, but they cannot! Then in the `Parser` module we can say: + +```guida +module Parser exposing (..) + +import Parser.Internal as Internal + +type alias Parser a = + Internal.Parser a +``` + +So now folks see a `Parser` type exposed by the `Parser` module, and it is the one that is +used throughout all the modules in the package. Do not screw up your data representation +to avoid this trick! I think we can improve how this appears in documentation, but overall +this is the best way to go. + +Now again, this strategy is particularly useful in packages. It is not as worthwhile +in application code. + +--- + +## References + +- +""" + + Route.Imports -> + markdownRender """ +# Imports + +When getting started with Guida, it is pretty common to have questions about how the `import` +declarations work exactly. These questions usually arise when you start playing with the `Html` +library so we will focus on that. + +## `import` + +A Guida file is called a **module**. To access code in other files, you need to `import` it! + +So say you want to use the [`div`](htthttps://package.elm-lang.org/packages/elm/html/latest/Html#div) +function from the [`elm/html`](http://package.elm-lang.org/packages/elm/html/latest) package. +The simplest way is to import it like this: + +```guida +import Html + +main = + Html.div [] [] +``` + +After saying `import Html` we can refer to anything inside that module as long as it is *qualified*. +This works for: + +- **Values** - we can refer to `Html.text`, `Html.h1`, etc. +- **Types** - We can refer to [`Attribute`](http://package.elm-lang.org/packages/elm/html/latest/Html#Attribute) + as `Html.Attribute`. + +So if we add a type annotation to `main` it would look like this: + +```guida +import Html + +main : Html.Html msg +main = + Html.div [] [] +``` + +We are referring to the [`Html`](http://package.elm-lang.org/packages/elm/html/latest/Html#Html) +type, using its *qualified* name `Html.Html`. This can feel weird at first, but it starts +feeling natural quite quickly! + +> **Note:** Modules do not contain other modules. So the `Html` module *does not* contain +> the `Html.Attributes` module. Those are separate names that happen to have some overlap. +> So if you say `import Html` you *do not* get access to `Html.Attributes.style`. +> You must `import Html.Attributes` module separately. + +## `as` + +It is best practice to always use *qualified* names, but sometimes module names are so long +that it becomes unwieldy. This is common for the `Html.Attributes` module. We can use the `as` +keyword to help with this: + +```guida +import Html +import Html.Attributes as A + +main = + Html.div [ A.style "color" "red" ] [ Html.text "Hello!" ] +``` + +Saying `import Html.Attributes as A` lets us refer to any value or type in `Html.Attributes` +as long as it is qualified with an `A`. So now we can refer to +[`style`](http://package.elm-lang.org/packages/elm/html/latest/Html-Attributes#style) as `A.style`. + +## `exposing` + +In quick drafts, maybe you want to use *unqualified* names. You can do that with the `exposing` +keyword like this: + +```guida +import Html exposing (..) +import Html.Attributes exposing (style) + +main : Html msg +main = + div [ style "color" "red" ] [ text "Hello!" ] +``` + +Saying `import Html exposing (..)` means I can refer to any value or type from the `Html` module +without qualification. Notice that I use the `Html` type, the `div` function, and the `text` +function without qualification in the example above. + +> **Note:** It seems neat to expose types and values directly, but it can get out of hand. +Say you `import` ten modules `exposing` all of their content. It quickly becomes difficult +to figure out what is going on in your code. "Wait, where is this function from?" +And then trying to sort through all the imports to find it. Point is, use `exposing (..)` +sparingly! + +Saying `import Html.Attributes exposing (style)` is a bit more reasonable. It means I can refer +to the `style` function without qualification, but that is it. You are still importing the +`Html.Attributes` module like normal though, so you would say `Html.Attributes.class` +or `Html.Attributes.id` to refer to other values and types from that module. + +## `as` and `exposing` + +There is one last way to import a module. You can combine `as` and `exposing` to try to +get a nice balance of qualified names: + +```guida +import Html exposing (Html, div, text) +import Html.Attributes as A exposing (style) + +main : Html msg +main = + div [ A.class "greeting", style "color" "red" ] [ text "Hello!" ] +``` + +Notice that I refer to `A.class` which is qualified and `style` which is unqualified. + +## Default Imports + +We just learned all the variations of the `import` syntax in Guida. You will use some +version of that syntax to `import` any module you ever write. + +It would be the best policy to make it so every module in the whole ecosystem works this way. +We thought so in the past at least, but there are some modules that are so commonly used +that the Guida compiler automatically adds the imports to every file. +These default imports include: + +```guida +import Basics exposing (..) +import List exposing (List, (::)) +import Maybe exposing (Maybe(..)) +import Result exposing (Result(..)) +import String +import Tuple + +import Debug + +import Platform exposing (Program) +import Platform.Cmd as Cmd exposing (Cmd) +import Platform.Sub as Sub exposing (Sub) +``` + +You can think of these imports being at the top of any module you write. + +One could argue that `Maybe` is so fundamental to how we handle errors in Guida code that +it is *basically* part of the language. One could also argue that it is extraordinarily +annoying to have to import `Maybe` once you get past your first couple weeks with Guida. +Either way, we know that default imports are not ideal in some sense, so we have tried to keep +the default imports as minimal as possible. + +> **Note:** Guida performs dead code elimination, so if you do not use something from a module, +it is not included in the generated code. So if you `import` a module with hundreds of +functions, you do not need to worry about the size of your assets. You will only get what you use! + +--- + +## References + +- +""" + + Route.InfiniteTypes -> + markdownRender """ +# Infinite Types + +Infinite types are probably the trickiest kind of bugs to track down. **Writing down type +annotations is usually the fastest way to figure them out.** Let's work through an example +to get a feel for how these errors usually work though! + +## Example + +A common way to get an infinite type error is very small typos. For example, do you see the +problem in the following code? + +```guida +incrementNumbers list = + List.map incrementNumbers list + +incrementNumber n = + n + 1 +``` + +The issue is that `incrementNumbers` calls itself, not the `incrementNumber` function +defined below. So there is an extra `s` in this program! Let's focus on that: + +```guida +incrementNumbers list = + List.map incrementNumbers list -- BUG extra `s` makes this self-recursive +``` + +Now the compiler does not know that anything is wrong yet. It just tries to figure out the +types like normal. It knows that `incrementNumbers` is a function. The definition uses +`List.map` so we can deduce that `list : List t1` and the result of this function call should +be some other `List t2`. This also means that `incrementNumbers : List t1 -> List t2`. + +The issue is that `List.map` uses `incrementNumbers` on `list`! That means that each element of +`list` (which has type `t1`) must be fed into `incrementNumbers` (which takes `List t1`) + +That means that `t1 = List t1`, which is an infinite type! If we start expanding this, +we get `List (List (List (List (List ...))))` out to infinity! + +The point is mainly that we are in a confusing situation. The types are confusing. +This explanation is confusing. The compiler is confused. It is a bad time. But luckily, +the more type annotations you add, the better chance there is that you and the compiler +can figure things out! So say we change our definition to: + +```guida +incrementNumbers : List Int -> List Int +incrementNumbers list = + List.map incrementNumbers list -- STILL HAS BUG +``` + +Now we are going to get a pretty normal type error. Hey, you said that each element in the +`list` is an `Int` but I cannot feed that into a `List Int -> List Int` function! +Something like that. + +In summary, the root issue is often some small typo, and the best way out is to start adding +type annotations on everything! + +--- + +## References + +- +""" + + Route.MissingPatterns -> + markdownRender """ +# Missing Patterns + +Guida checks to make sure that all possible inputs to a function or `case` are handled. +This gives us the guarantee that no Guida code is ever going to crash because data had +an unexpected shape. + +There are a couple of techniques for making this work for you in every scenario. + +## The danger of wildcard patterns + +A common scenario is that you want to add a tag to a custom type that is used in a bunch +of places. For example, maybe you are working different variations of users in a chat room: + +```guida +type User + = Regular String Int + | Anonymous + +toName : User -> String +toName user = + case user of + Regular name _ -> + name + + _ -> + "anonymous" +``` + +Notice the wildcard pattern in `toName`. This will hurt us! Say we add a `Visitor String` +variant to `User` at some point. Now we have a bug that visitor names are reported as `"anonymous"`, +and the compiler cannot help us! + +So instead, it is better to explicitly list all possible variants, like this: + +```guida +type User + = Regular String Int + | Visitor String + | Anonymous + +toName : User -> String +toName user = + case user of + Regular name _ -> + name + + Anonymous -> + "anonymous" +``` + +Now the compiler will say "hey, what should `toName` do when it sees a `Visitor`?" +This is a tiny bit of extra work, but it is very worth it! + +## I want to go fast! + +Imagine that the `User` type appears in 20 or 30 functions across your project. When we add +a `Visitor` variant, the compiler points out all the places that need to be updated. +That is very convenient, but in a big project, maybe you want to get through it extra quickly. + +In that case, it can be helpful to use +[`Debug.todo`](https://package.elm-lang.org/packages/elm/core/latest/Debug#todo) to leave some +code incomplete: + +```guida +type User + = Regular String Int + | Visitor String + | Anonymous + +toName : User -> String +toName user = + case user of + Regular name _ -> + name + + Visitor _ -> + Debug.todo "give the visitor name" + + Anonymous -> + "anonymous" + +-- and maybe a bunch of other things +``` + +In this case it is easier to just write the implementation, but the point is that on more +complex functions, you can put things off a bit. + +The Guida compiler is actually aware of `Debug.todo` so when it sees it in a `case` like this, +it will crash with a bunch of helpful information. It will tell you: + +1. The name of the module that contains the code. +2. The line numbers of the `case` containing the TODO. +3. The particular value that led to this TODO. + +From that information you have a pretty good idea of what went wrong and can go fix it. + +I tend to use `Debug.todo` as the message when my goal is to go quick because it makes +it easy to go and find all remaining todos in my code before a release. + +## A list that definitely is not empty + +This can come up from time to time, but Guida **will not** let you write code like this: + +```guida +last : List a -> a +last list = + case list of + [x] -> + x + + _ :: rest -> + last rest +``` + +This is no good. It does not handle the empty list. There are two ways to handle this. +One is to make the function return a `Maybe` like this: + +```guida +last : List a -> Maybe a +last list = + case list of + [] -> + Nothing + + [x] -> + Just x + + _ :: rest -> + last rest +``` + +This is nice because it lets users know that there might be a failure, so they can recover +from it however they want. + +The other option is to "unroll the list" one level to ensure that no one can ever provide +an empty list in the first place: + +```guida +last : a -> List a -> a +last first rest = + case rest of + [] -> + first + + newFirst :: newRest -> + last newFirst newRest +``` + +By demanding the first element of the list as an argument, it becomes impossible to call +this function if you have an empty list! + +This "unroll the list" trick is quite useful. I recommend using it directly, not through some +external library. It is nothing special. Just a useful idea! + +--- + +## References + +- +""" + + Route.Optimize -> + markdownRender """ +# Optimize + +When you are serving a website, there are two kinds of optimizations you want to do: + +1. **Asset Size** - How can we send as few bits as possible? +2. **Performance** - How can those bits run as quickly as possible? + +It turns out that Guida does really well on both! We have +[very small assets](https://elm-lang.org/news/small-assets-without-the-headache) and +[very fast code](https://elm-lang.org/news/blazing-fast-html-round-two) when compared to +the popular alternatives. + +Okay, but how do we get those numbers? + +## Instructions + +Step one is to compile with the `--optimize` flag. This does things like shortening record +field names and unboxing values. + +Step two is to call `uglifyjs` with a bunch of special flags. The flags unlock optimizations +that are unreliable in normal JS code, but because Guida does not have side-effects, +they work fine for us! + +Putting those together, here is how I would optimize `src/Main.guida` with two terminal commands: + +```bash +guida make src/Main.guida --optimize --output=guida.js +uglifyjs guida.js --compress "pure_funcs=[F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9],pure_getters,keep_fargs=false,unsafe_comps,unsafe" | uglifyjs --mangle --output guida.min.js +``` + +After this you will have an `guida.js` and a significantly smaller `guida.min.js` file! + +**Note 1:** `uglifyjs` is called twice there. First to `--compress` and second to `--mangle`. +This is necessary! Otherwise `uglifyjs` will ignore our `pure_funcs` flag. + +**Note 2:** If the `uglifyjs` command is not available in your terminal, you can run the +command `npm install uglify-js --global` to download it. You probably already have `npm` +from getting `guida repl` working, but if not, it is bundled with [nodejs](https://nodejs.org/). + +## Scripts + +It is hard to remember all that, so it is probably a good idea to write a script that does it. + +I would maybe want to run `./optimize.sh src/Main.guida` and get out `guida.js` and `guida.min.js`, +so on Mac or Linux, I would make a script called `optimize.sh` like this: + +```bash +#!/bin/sh + +set -e + +js="guida.js" +min="guida.min.js" + +guida make --optimize --output=$js $@ + +uglifyjs $js --compress "pure_funcs=[F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9],pure_getters,keep_fargs=false,unsafe_comps,unsafe" | uglifyjs --mangle --output $min + +echo "Initial size: $(cat $js | wc -c) bytes ($js)" +echo "Minified size:$(cat $min | wc -c) bytes ($min)" +echo "Gzipped size: $(cat $min | gzip -c | wc -c) bytes" +``` + +It also prints out all the asset sizes for you! Your server should be configured to gzip +the assets it sends, so the last line is telling you how many bytes would _actually_ get sent +to the user. + +Again, the important commands are `guida` and `uglifyjs` which work on any platform, so it +should not be too tough to do something similar on Windows. + +--- + +## References + +- +""" + + Route.PortModules -> + markdownRender """ +# Port Modules + +The package ecosystem is one of the most important parts of Guida. Right now, our ecosystem +has some compelling benefits: + +- There are many obvious default packages that work well. +- Adding dependencies cannot introduce runtime exceptions. +- Patch changes cannot lead to surprise build failures. + +These are really important factors if you want to *quickly* create *reliable* applications. +The Guida community thinks this is valuable. + +Other communities think that the *number* of packages is a better measure of ecosystem health. +That is a fine metric to use, but it is not the one we use for Guida. We would rather have 50 +great packages than 100k packages of wildly varying quality. + +## So what about ports? + +Imagine you install a new package that claims to support `localStorage`. You get it set up, +working through any compile errors. You run it, but it does not seem to work! After trying to +figure it out for hours, you realize there is some poorly documented `port` to hook up... + +Okay, now you need to hook up some JavaScript code. Is that JS file in the Guida package? +Or is it on `npm`? Wait, what version on `npm` though? And is this patch version going +to work as well? Also, how does this file fit into my build process? And assuming we get +through all that, maybe the `port` has the same name as one of the ports in your project. +Or it clashes with a `port` name in another package. + +**Suddenly adding dependencies is much more complicated and risky!** An experienced developer +would always check for ports up front, spending a bunch of time manually classifying unacceptable +packages. Most people would not know to do that and learn all the pitfalls through personal +experience, ultimately spending even *more* time than the person who defensively checks +to avoid these issues. + +So "ports in packages" would impose an enormous cost on application developers, and in the end, +we would have a less reliable package ecosystem overall. + +## Conclusion + +Our wager with the Guida package ecosystem is that it is better to get a package *right* +than to get it *right now*. So while we could use "ports in packages" as a way to get twenty +`localStorage` packages of varying quality *right now*, we are choosing not to go that route. +Instead we ask that developers use ports directly in their application code, getting the same +result a different way. + +Now this may not be the right choice for your particular project, and that is okay! +We will be expanding our core libraries over time, as explained [here](https://github.com/elm/projects/blob/master/roadmap.md#where-is-the-localstorage-package), +and we hope you will circle back later to see if Guida has grown into a better fit! + +If you have more questions about this choice or what it means for your application, +please come ask in the [Guida Discord](https://discord.gg/B6WgPzf5Aa). Folks are friendly +and happy to help out! Chances are that a `port` in your application will work great for +your case once you learn more about how they are meant to be used. + +--- + +## References + +- +""" + + Route.RecursiveAlias -> + markdownRender """ +# Recursive Type Aliases + +At the root of this issue is the distinction between a `type` and a `type alias`. + +## What is a type alias? + +When you create a type alias, you are just creating a shorthand to refer to an existing type. +So when you say the following: + +```guida +type alias Time = Float + +type alias Degree = Float + +type alias Weight = Float +``` + +You have not created any *new* types, you just made some alternate names for `Float`. +You can write down things like this and it'll work fine: + +```guida +add : Time -> Degree -> Weight +add time degree = + time + degree +``` + +This is kind of a weird way to use type aliases though. The typical usage would be for records, +where you do not want to write out the whole thing every time. Stuff like this: + +```guida +type alias Person = + { name : String + , age : Int + , height : Float + } +``` + +It is much easier to write down `Person` in a type, and then it will just expand out to +the underlying type when the compiler checks the program. + +## Recursive type aliases? + +Okay, so let's say you have some type that may contain itself. In Guida, a common example +of this is a comment that might have subcomments: + +```guida +type alias Comment = + { message : String + , upvotes : Int + , downvotes : Int + , responses : List Comment + } +``` + +Now remember that type *aliases* are just alternate names for the real type. So to make `Comment` +into a concrete type, the compiler would start expanding it out. + +```elm + { message : String + , upvotes : Int + , downvotes : Int + , responses : + List + { message : String + , upvotes : Int + , downvotes : Int + , responses : + List + { message : String + , upvotes : Int + , downvotes : Int + , responses : List ... + } + } + } +``` + +The compiler cannot deal with values like this. It would just keep expanding forever. + +## Recursive types! + +In cases where you want a recursive type, you need to actually create a brand new type. +This is what the `type` keyword is for. A simple example of this can be seen when defining a +linked list: + +```guida +type List + = Empty + | Node Int List +``` + +No matter what, the type of `Node n xs` is going to be `List`. There is no expansion to be done. +This means you can represent recursive structures with types that do not explode into infinity. + +So let's return to wanting to represent a `Comment` that may have responses. There are a couple +ways to do this: + +### Obvious, but kind of annoying + +```guida +type Comment = + Comment + { message : String + , upvotes : Int + , downvotes : Int + , responses : List Comment + } +``` + +Now let's say you want to register an upvote on a comment: + +```guida +upvote : Comment -> Comment +upvote (Comment comment) = + Comment { comment | upvotes = 1 + comment.upvotes } +``` + +It is kind of annoying that we now have to unwrap and wrap the record to do anything with it. + +### Less obvious, but nicer + +```guida +type alias Comment = + { message : String + , upvotes : Int + , downvotes : Int + , responses : Responses + } + +type Responses = Responses (List Comment) +``` + +In this world, we introduce the `Responses` type to capture the recursion, but `Comment` +is still an alias for a record. This means the `upvote` function looks nice again: + +```guida +upvote : Comment -> Comment +upvote comment = + { comment | upvotes = 1 + comment.upvotes } +``` + +So rather than having to unwrap a `Comment` to do *anything* to it, you only have to do +some unwrapping in the cases where you are doing something recursive. In practice, this means +you will do less unwrapping which is nice. + +## Mutually recursive type aliases + +It is also possible to build type aliases that are *mutually* recursive. That might be +something like this: + +```guida +type alias Comment = + { message : String + , upvotes : Int + , downvotes : Int + , responses : Responses + } + +type alias Responses = + { sortBy : SortBy + , responses : List Comment + } + +type SortBy = Time | Score | MostResponses +``` + +When you try to expand `Comment` you have to expand `Responses` which needs to expand `Comment` +which needs to expand `Responses`, etc. + +So this is just a fancy case of a self-recursive type alias. The solution is the same. +Somewhere in that cycle, you need to define an actual `type` to end the infinite expansion. + +--- + +## References + +- +""" + + Route.Shadowing -> + markdownRender """ +# Shadowing + +Variable shadowing is when you define the same variable name twice in an ambiguous way. +Here is a pretty reasonable use of shadowing: + +```guida +viewName : Maybe String -> Html msg +viewName name = + case name of + Nothing -> + ... + + Just name -> + ... +``` + +I define a `name` with type `Maybe String` and then in that second branch, I define a `name` +that is a `String`. Now that there are two `name` values, it is not 100% obvious which +one you want in that second branch. + +Most linters produce warnings on variable shadowing, so Guida makes "best practices" the default. +Just rename the first one to `maybeName` and move on. + +This choice is relatively uncommon in programming languages though, so I want to provide the +reasoning behind it. + +## The Cost of Shadowing + +The code snippet from above is the best case scenario for variable shadowing. +It is pretty clear really. But that is because it is a fake example. It does not even compile. + +In a large module that is evolving over time, this is going to cause bugs in a very predictable way. +You will have two definitions, separated by hundreds of lines. For example: + +```guida +name : String +name = + "Tom" + +-- hundreds of lines + +viewName : String -> Html msg +viewName name = + ... name ... name ... name ... +``` + +Okay, so the `viewName` function has an argument `name` and it uses it three times. +Maybe the `viewName` function is 50 lines long in total, so those uses are not totally easy to see. +This is fine so far, but say your colleague comes along five months later and wants to support +first and last names. They refactor the code like this: + +```guida +viewName : String -> String -> Html msg +viewName firstName lastName = + ... name ... name ... name ... +``` + +The code compiles, but it does not work as intended. They forgot to change all the uses of `name`, +and because it shadows the top-level `name` value, it always shows up as `"Tom"`. +It is a simple mistake, but it is always the last thing I think of. + +> Is the data being fetched properly? Let me log all of the JSON requests. Maybe the JSON +> decoders are messed up? Hmm. Maybe someone is transforming the name in a bad way at some point? +> Let me check my `update` code. + +Basically, a bunch of time gets wasted on something that could easily be detected by the compiler. +But this bug is rare, right? + +## Aggregate Cost + +Thinking of a unique and helpful name takes some extra time. Maybe 30 seconds. But it means that: + +1. Your code is easier to read and understand later on. So you spend 30 seconds once `O(1)` + rather than spending 10 seconds each time someone reads that code in the future `O(n)`. +2. The tricky shadowing bug described above is impossible. Say there is a 5% chance that + any given edit produces a shadowing bug, and that resolving that shadowing bug takes one hour. + That means the expected time for each edit increases by three minutes. + +If you are still skeptical, I encourage you can play around with the number of edits, +time costs, and probabilities here. When shadowing is not allowed, the resulting overhead +for the entire lifetime of the code is the 30 seconds it takes to pick a better name, +so that is what you need to beat! + +## Summary + +Without shadowing, the code is easier to read and folks spend less time on pointless debugging. +The net outcome is that folks have more time to make something wonderful with Guida! + +--- + +## References + +- +""" + + Route.TypeAnnotations -> + markdownRender """ +# Type Annotation Problems + +At the root of this kind of issue is always the fact that a type annotation in your code does +not match the corresponding definition. Now that may mean that the type annotation is "wrong" +or it may mean that the definition is "wrong". The compiler cannot figure out your intent, +only that there is some mismatch. + +This document is going to outline the various things that can go wrong and show some examples. + +## Annotation vs. Definition + +The most common issue is with user-defined type variables that are too general. +So lets say you have defined a function like this: + +```guida +addPair : (a, a) -> a +addPair (x, y) = + x + y +``` + +The issue is that the type annotation is saying "I will accept a tuple containing literally +*anything*" but the definition is using `(+)` which requires things to be numbers. +So the compiler is going to infer that the true type of the definition is this: + +```guida +addPair : (number, number) -> number +``` + +So you will probably see an error saying "I cannot match `a` with `number`" which is +essentially saying, you are trying to provide a type annotation that is **too general**. +You are saying `addPair` accepts anything, but in fact, it can only handle numbers. + +In cases like this, you want to go with whatever the compiler inferred. It is good at +figuring this kind of stuff out ;) + +## Annotation vs. Itself + +It is also possible to have a type annotation that clashes with itself. This is probably more rare, +but someone will run into it eventually. Let's use another version of `addPair` with problems: + +```guida +addPair : (Int, Int) -> number +addPair (x, y) = + x + y +``` + +In this case the annotation says we should get a `number` out, but because we were specific +about the inputs being `Int`, the output should also be an `Int`. + +## Annotation vs. Internal Annotation + +A quite tricky case is when an outer type annotation clashes with an inner type annotation. +Here is an example of this: + +```guida +filter : (a -> Bool) -> List a -> List a +filter isOkay list = + let + keepIfOkay : a -> Maybe a + keepIfOkay x = + if isOkay x then Just x else Nothing + in + List.filterMap keepIfOkay list +``` + +This case is very unfortunate because all the type annotations are correct, but there is a detail +of how type inference works right now that **user-defined type variables are not shared +between annotations**. This can lead to probably the worst type error messages we have because +the problem here is that `a` in the outer annotation does not equal `a` in the inner annotation. + +For now the best route is to leave off the inner annotation. It is unfortunate, +and hopefully we will be able to do a nicer thing in future releases. + +--- + +## References + +- +""" + + +sidebarNavigation : Navigation Route.DocumentationSection +sidebarNavigation = + [ { title = "Overview" + , links = + [ { title = "Introduction", href = "/docs", route = Route.Introduction, sections = [] } + , { title = "What is Guida?", href = "/docs/what-is-guida", route = Route.WhatIsGuida, sections = [] } + ] + } + , { title = "Getting Started" + , links = + [ { title = "Installation", href = "/docs/installation", route = Route.Installation, sections = [] } + , { title = "Your First Program", href = "/docs/your-first-program", route = Route.YourFirstProgram, sections = [] } + , { title = "Project Setup", href = "/docs/project-setup", route = Route.ProjectSetup, sections = [] } + , { title = "Migration from Elm", href = "/docs/migration-from-elm", route = Route.MigrationFromElm, sections = [] } + ] + } + , { title = "The Language" + , links = + [ { title = "Syntax Overview", href = "/docs/syntax-overview", route = Route.SyntaxOverview, sections = [] } + , { title = "Values and Types", href = "/docs/values-and-types", route = Route.ValuesAndTypes, sections = [] } + , { title = "Functions and Expressions", href = "/docs/functions-and-expressions", route = Route.FunctionsAndExpressions, sections = [] } + , { title = "Modules and Imports", href = "/docs/modules-and-imports", route = Route.ModulesAndImports, sections = [] } + , { title = "Custom Types", href = "/docs/custom-types", route = Route.CustomTypes, sections = [] } + , { title = "Pattern Matching", href = "/docs/pattern-matching", route = Route.PatternMatching, sections = [] } + , { title = "Error Handling", href = "/docs/error-handling", route = Route.ErrorHandling, sections = [] } + ] + } + , { title = "Core Concepts" + , links = + [ { title = "Immutability and Purity", href = "/docs/immutability-and-purity", route = Route.ImmutabilityAndPurity, sections = [] } + , { title = "The Type System", href = "/docs/the-type-system", route = Route.TheTypeSystem, sections = [] } + , { title = "Concurrency and Effects", href = "/docs/concurrency-and-effects", route = Route.ConcurrencyAndEffects, sections = [] } + , { title = "State and Architecture", href = "/docs/state-and-architecture", route = Route.StateAndArchitecture, sections = [] } + ] + } + , { title = "Building Applications" + , links = + [ { title = "Application Structure", href = "/docs/application-structure", route = Route.ApplicationStructure, sections = [] } + , { title = "The Guida Architecture", href = "/docs/the-guida-architecture", route = Route.TheGuidaArchitecture, sections = [] } + , { title = "Routing and Navigation", href = "/docs/routing-and-navigation", route = Route.RoutingAndNavigation, sections = [] } + , { title = "Interoperability", href = "/docs/interoperability", route = Route.Interoperability, sections = [] } + ] + } + , { title = "Commands" + , links = + [ { title = "repl", href = "/docs/1.0.0/commands/repl", route = Route.Commands Route.Repl, sections = [] } + , { title = "init", href = "/docs/1.0.0/commands/init", route = Route.Commands Route.Init, sections = [] } + , { title = "make", href = "/docs/1.0.0/commands/make", route = Route.Commands Route.Make, sections = [] } + , { title = "install", href = "/docs/1.0.0/commands/install", route = Route.Commands Route.Install, sections = [] } + , { title = "uninstall", href = "/docs/1.0.0/commands/uninstall", route = Route.Commands Route.Uninstall, sections = [] } + , { title = "bump", href = "/docs/1.0.0/commands/bump", route = Route.Commands Route.Bump, sections = [] } + , { title = "diff", href = "/docs/1.0.0/commands/diff", route = Route.Commands Route.Diff, sections = [] } + , { title = "publish", href = "/docs/1.0.0/commands/publish", route = Route.Commands Route.Publish, sections = [] } + , { title = "format", href = "/docs/1.0.0/commands/format", route = Route.Commands Route.Format, sections = [] } + , { title = "test", href = "/docs/1.0.0/commands/test", route = Route.Commands Route.Test, sections = [] } + ] + } + , { title = "Contributing" , links = - [ { title = "repl", href = "/docs/1.0.0/commands/repl", active = activeCommand Route.Repl } - , { title = "init", href = "/docs/1.0.0/commands/init", active = activeCommand Route.Init } - , { title = "make", href = "/docs/1.0.0/commands/make", active = activeCommand Route.Make } - , { title = "install", href = "/docs/1.0.0/commands/install", active = activeCommand Route.Install } - , { title = "uninstall", href = "/docs/1.0.0/commands/uninstall", active = activeCommand Route.Uninstall } - , { title = "bump", href = "/docs/1.0.0/commands/bump", active = activeCommand Route.Bump } - , { title = "diff", href = "/docs/1.0.0/commands/diff", active = activeCommand Route.Diff } - , { title = "publish", href = "/docs/1.0.0/commands/publish", active = activeCommand Route.Publish } - , { title = "format", href = "/docs/1.0.0/commands/format", active = activeCommand Route.Format } - , { title = "test", href = "/docs/1.0.0/commands/test", active = activeCommand Route.Test } + [ { title = "Getting Started", href = "/docs/contributing/getting-started", route = Route.ContributingGettingStarted, sections = [] } + , { title = "Ways to Contribute", href = "/docs/contributing/ways-to-contribute", route = Route.ContributingWaysToContribute, sections = [] } + , { title = "Development Workflow", href = "/docs/contributing/development-workflow", route = Route.ContributingDevelopmentWorkflow, sections = [] } + , { title = "Reporting Issues", href = "/docs/contributing/reporting-issues", route = Route.ContributingReportingIssues, sections = [] } + , { title = "Join the Community", href = "/docs/contributing/join-the-community", route = Route.ContributingJoinTheCommunity, sections = [] } ] } - , { title = "Hints" + , { title = "Advanced Topics" , links = - [ { title = "Bad recursion", href = "/docs/1.0.0/hints/bad-recursion", active = activeHint Route.BadRecursion } - , { title = "Comparing custom types", href = "/docs/1.0.0/hints/comparing-custom-types", active = activeHint Route.ComparingCustomTypes } - , { title = "Comparing records", href = "/docs/1.0.0/hints/comparing-records", active = activeHint Route.ComparingRecords } - , { title = "Implicit casts", href = "/docs/1.0.0/hints/implicit-casts", active = activeHint Route.ImplicitCasts } - , { title = "Import cycles", href = "/docs/1.0.0/hints/import-cycles", active = activeHint Route.ImportCycles } - , { title = "Imports", href = "/docs/1.0.0/hints/imports", active = activeHint Route.Imports } - , { title = "Infinite type", href = "/docs/1.0.0/hints/infinite-type", active = activeHint Route.InfiniteType } - , { title = "Missing patterns", href = "/docs/1.0.0/hints/missing-patterns", active = activeHint Route.MissingPatterns } - , { title = "Optimize", href = "/docs/1.0.0/hints/optimize", active = activeHint Route.Optimize } - , { title = "Port modules", href = "/docs/1.0.0/hints/port-modules", active = activeHint Route.PortModules } - , { title = "Recursive alias", href = "/docs/1.0.0/hints/recursive-alias", active = activeHint Route.RecursiveAlias } - , { title = "Shadowing", href = "/docs/1.0.0/hints/shadowing", active = activeHint Route.Shadowing } - , { title = "Tuples", href = "/docs/1.0.0/hints/tuples", active = activeHint Route.Tuples } - , { title = "Type annotations", href = "/docs/1.0.0/hints/type-annotations", active = activeHint Route.TypeAnnotations } + [ { title = "Bad Recursion", href = "/docs/1.0.0/hints/bad-recursion", route = Route.Hints Route.BadRecursion, sections = [] } + , { title = "Comparing Custom Types", href = "/docs/1.0.0/hints/comparing-custom-types", route = Route.Hints Route.ComparingCustomTypes, sections = [] } + , { title = "Comparing Records", href = "/docs/1.0.0/hints/comparing-records", route = Route.Hints Route.ComparingRecords, sections = [] } + , { title = "Implicit Casts", href = "/docs/1.0.0/hints/implicit-casts", route = Route.Hints Route.ImplicitCasts, sections = [] } + , { title = "Import Cycles", href = "/docs/1.0.0/hints/import-cycles", route = Route.Hints Route.ImportCycles, sections = [] } + , { title = "Imports", href = "/docs/1.0.0/hints/imports", route = Route.Hints Route.Imports, sections = [] } + , { title = "Infinite Types", href = "/docs/1.0.0/hints/infinite-types", route = Route.Hints Route.InfiniteTypes, sections = [] } + , { title = "Missing Patterns", href = "/docs/1.0.0/hints/missing-patterns", route = Route.Hints Route.MissingPatterns, sections = [] } + , { title = "Optimize", href = "/docs/1.0.0/hints/optimize", route = Route.Hints Route.Optimize, sections = [] } + , { title = "Port Modules", href = "/docs/1.0.0/hints/port-modules", route = Route.Hints Route.PortModules, sections = [] } + , { title = "Recursive Type Aliases", href = "/docs/1.0.0/hints/recursive-alias", route = Route.Hints Route.RecursiveAlias, sections = [] } + , { title = "Shadowing", href = "/docs/1.0.0/hints/shadowing", route = Route.Hints Route.Shadowing, sections = [] } + , { title = "Type Annotation Problems", href = "/docs/1.0.0/hints/type-annotations", route = Route.Hints Route.TypeAnnotations, sections = [] } ] } ] diff --git a/src/Page/Examples.elm b/src/Page/Examples.elm index 2796036..2f9c1f1 100644 --- a/src/Page/Examples.elm +++ b/src/Page/Examples.elm @@ -92,7 +92,7 @@ view : Session -> (Session.Msg -> msg) -> Browser.Document msg view session toSessionMsg = { title = "Guida: Examples" , body = - Layout.view { sidebarNavigation = [] } session toSessionMsg <| + Layout.view { sidebarNavigation = [], currentRoute = [] } session toSessionMsg <| [ Html.h1 [] [ Html.text "Examples" ] , Html.div [ Attr.class "not-prose mt-4 grid grid-cols-1 gap-8 border-t border-zinc-900/5 pt-10 sm:grid-cols-2 lg:grid-cols-4 dark:border-white/5" ] (List.map exampleView examples) diff --git a/src/Page/Home.elm b/src/Page/Home.elm index 74c6e71..da9d1d9 100644 --- a/src/Page/Home.elm +++ b/src/Page/Home.elm @@ -17,7 +17,7 @@ view : Session -> (Session.Msg -> msg) -> Browser.Document msg view session toSessionMsg = { title = "Guida: Home" , body = - Layout.view { sidebarNavigation = [] } session toSessionMsg <| + Layout.view { sidebarNavigation = [], currentRoute = () } session toSessionMsg <| [ HeroPattern.view , Html.h1 [] [ Html.text "What is Guida?" ] , Html.p [ Attr.class "lead" ] @@ -33,7 +33,7 @@ view session toSessionMsg = , Button.view (Button.Link "/docs") Button.Outline (Just Button.RightArrow) [] <| [ Html.text "Documentation" ] ] - , Html.h2 [ Attr.class "scroll-mt-24" ] [ Html.text "Vision" ] + , Html.h2 [] [ Html.text "Vision" ] , Html.p [] [ Html.text "Guida builds on the foundations of Elm, aiming to advance the future of functional programming. By translating Elm's compiler from Haskell to a self-hosted environment, Guida helps developers to build reliable, maintainable, and performant applications without leaving the language they love." ] @@ -63,7 +63,7 @@ view session toSessionMsg = [ Html.text "Our ultimate goal is to create a language that inherits the best aspects of Elm while adapting and growing to meet the needs of its users." ] , Html.section [] - [ Html.h2 [ Attr.class "scroll-mt-24" ] [ Html.text "Try It" ] + [ Html.h2 [] [ Html.text "Try It" ] , Html.p [] [ Html.text "Experiment with Guida in your browser. Write, run, and explore code instantly with the " , Html.a [ Attr.href "/try" ] [ Html.text "online Guida playground" ] @@ -76,7 +76,7 @@ view session toSessionMsg = ] ] , Html.section [] - [ Html.h2 [ Attr.class "scroll-mt-24" ] [ Html.text "Documentation" ] + [ Html.h2 [] [ Html.text "Documentation" ] , Html.p [] [ Html.text "The " , Html.a [ Attr.href "/docs" ] [ Html.text "documentation" ] @@ -86,7 +86,7 @@ view session toSessionMsg = ] ] , Html.section [] - [ Html.h2 [ Attr.class "scroll-mt-24" ] [ Html.text "Community" ] + [ Html.h2 [] [ Html.text "Community" ] , Html.p [] [ Html.text "Join us to shape the language together. See our " , Html.a [ Attr.href "/community" ] [ Html.text "Community" ] @@ -154,7 +154,7 @@ view session toSessionMsg = ] ] , Html.section [] - [ Html.h2 [ Attr.class "scroll-mt-24" ] [ Html.text "Packages" ] + [ Html.h2 [] [ Html.text "Packages" ] , Html.p [] [ Html.text "Explore and publish libraries with the " , Html.a [ Attr.href "https://package.guida-lang.org" ] [ Html.text "Guida package registry" ] diff --git a/src/Page/NotFound.elm b/src/Page/NotFound.elm index 54b7872..29c447f 100644 --- a/src/Page/NotFound.elm +++ b/src/Page/NotFound.elm @@ -17,7 +17,7 @@ view : Session -> (Session.Msg -> msg) -> Browser.Document msg view session toSessionMsg = { title = "Guida: Page not found" , body = - Layout.view { sidebarNavigation = [] } session toSessionMsg <| + Layout.view { sidebarNavigation = [], currentRoute = () } session toSessionMsg <| [ HeroPattern.view , Html.div [ Attr.class "mx-auto flex h-full max-w-xl flex-col items-center justify-center py-16 text-center" ] [ Html.p [ Attr.class "text-sm font-semibold text-zinc-900 dark:text-white" ] [ Html.text "404" ] diff --git a/src/Page/Try.elm b/src/Page/Try.elm index 1786852..9978884 100644 --- a/src/Page/Try.elm +++ b/src/Page/Try.elm @@ -263,74 +263,77 @@ view session toSessionMsg toMsg model = [ Button.view Button.Button Button.Primary Nothing [ Events.onClick (toMsg Rebuild) ] [ Html.text "rebuild" ] ] ] - , Html.main_ [ Attr.class "overflow-y-auto bg-white" ] (outputView toMsg model) + , outputView toMsg model ] ] } -outputView : (Msg -> msg) -> Model -> List (Html msg) +outputView : (Msg -> msg) -> Model -> Html msg outputView toMsg model = case ( model.example, model.maybeResult ) of ( Nothing, Nothing ) -> - [ Html.div [ Attr.class "flex h-full" ] - [ Html.div [ Attr.class "m-auto w-sm items-center" ] - [ Html.div - [ Attr.class "my-4 group relative flex rounded-2xl bg-zinc-50 transition-shadow hover:shadow-md hover:shadow-zinc-900/5 dark:bg-white/2.5 dark:hover:shadow-black/5" - , Events.on "mouseover" (Decode.map3 (\rectangle clientX clientY -> toMsg (OnMouseOver rectangle clientX clientY)) (DOM.currentTarget DOM.boundingClientRect) (Decode.field "clientX" Decode.int) (Decode.field "clientY" Decode.int)) - ] - [ ResourcePattern.view { mouseX = model.mouseX, mouseY = model.mouseY } - , Html.div [ Attr.class "absolute inset-0 rounded-2xl ring-1 ring-zinc-900/7.5 ring-inset group-hover:ring-zinc-900/10 dark:ring-white/10 dark:group-hover:ring-white/20" ] [] - , Html.div [ Attr.class "relative rounded-2xl px-4 pt-8 pb-4" ] - [ Html.h2 [ Attr.class "mt-4 text-sm/7 font-semibold text-zinc-900 dark:text-white" ] - [ Html.text "Online Editor" ] - , Html.p [ Attr.class "mt-1 text-sm text-zinc-600 dark:text-zinc-400" ] - [ Html.text "Write and compile code online!" ] - , Html.ul [] - [ Html.li [] - [ Button.view (Button.Link "/examples/hello") Button.Text Nothing [] [ Html.text "Hello World!" ] - ] - , Html.li [] - [ Button.view (Button.Link "/examples/buttons") Button.Text Nothing [] [ Html.text "Buttons" ] - ] - , Html.li [] - [ Button.view (Button.Link "/examples/clock") Button.Text Nothing [] [ Html.text "Clock" ] - ] - , Html.li [] - [ Button.view (Button.Link "/examples/quotes") Button.Text Nothing [] [ Html.text "HTTP" ] - ] - , Html.li [] - [ Button.view (Button.Link "/examples/cards") Button.Text Nothing [] [ Html.text "Cards" ] + Html.main_ [ Attr.class "overflow-y-auto" ] + [ Html.div [ Attr.class "flex h-full" ] + [ Html.div [ Attr.class "m-auto w-sm items-center" ] + [ Html.div + [ Attr.class "my-4 group relative flex rounded-2xl bg-zinc-50 transition-shadow hover:shadow-md hover:shadow-zinc-900/5 dark:bg-white/2.5 dark:hover:shadow-black/5" + , Events.on "mouseover" (Decode.map3 (\rectangle clientX clientY -> toMsg (OnMouseOver rectangle clientX clientY)) (DOM.currentTarget DOM.boundingClientRect) (Decode.field "clientX" Decode.int) (Decode.field "clientY" Decode.int)) + ] + [ ResourcePattern.view { mouseX = model.mouseX, mouseY = model.mouseY } + , Html.div [ Attr.class "absolute inset-0 rounded-2xl ring-1 ring-zinc-900/7.5 ring-inset group-hover:ring-zinc-900/10 dark:ring-white/10 dark:group-hover:ring-white/20" ] [] + , Html.div [ Attr.class "relative rounded-2xl px-4 pt-8 pb-4" ] + [ Html.h2 [ Attr.class "mt-4 text-sm/7 font-semibold text-zinc-900 dark:text-white" ] + [ Html.text "Online Editor" ] + , Html.p [ Attr.class "mt-1 text-sm text-zinc-600 dark:text-zinc-400" ] + [ Html.text "Write and compile code online!" ] + , Html.ul [] + [ Html.li [] + [ Button.view (Button.Link "/examples/hello") Button.Text Nothing [] [ Html.text "Hello World!" ] + ] + , Html.li [] + [ Button.view (Button.Link "/examples/buttons") Button.Text Nothing [] [ Html.text "Buttons" ] + ] + , Html.li [] + [ Button.view (Button.Link "/examples/clock") Button.Text Nothing [] [ Html.text "Clock" ] + ] + , Html.li [] + [ Button.view (Button.Link "/examples/quotes") Button.Text Nothing [] [ Html.text "HTTP" ] + ] + , Html.li [] + [ Button.view (Button.Link "/examples/cards") Button.Text Nothing [] [ Html.text "Cards" ] + ] + , Html.li [] + [ Button.view (Button.Link "/examples") Button.Text Nothing [] [ Html.text "More!" ] + ] ] - , Html.li [] - [ Button.view (Button.Link "/examples") Button.Text Nothing [] [ Html.text "More!" ] + , Html.p [ Attr.class "mt-1 text-sm text-zinc-600 dark:text-zinc-400" ] + [ Html.text "Explore the " + , Button.view (Button.Link "/docs") Button.Text Nothing [] [ Html.text "official documentation" ] + , Html.text " to learn how to get started with Guida." ] ] - , Html.p [ Attr.class "mt-1 text-sm text-zinc-600 dark:text-zinc-400" ] - [ Html.text "Explore the " - , Button.view (Button.Link "/docs") Button.Text Nothing [] [ Html.text "official documentation" ] - , Html.text " to learn how to get started with Guida." - ] ] ] ] ] - ] ( Just _, Nothing ) -> - [] + Html.main_ [ Attr.class "overflow-y-auto" ] [] ( _, Just (Ok output) ) -> - [ Html.iframe - [ Attr.class "h-full w-full" - , Attr.srcdoc output + Html.main_ [ Attr.class "overflow-y-auto bg-white" ] + [ Html.iframe + [ Attr.class "h-full w-full" + , Attr.srcdoc output + ] + [] ] - [] - ] ( _, Just (Err error) ) -> - [ Errors.viewError error - ] + Html.main_ [ Attr.class "overflow-y-auto bg-white" ] + [ Errors.viewError error + ] diff --git a/src/Route.elm b/src/Route.elm index beba117..f540490 100644 --- a/src/Route.elm +++ b/src/Route.elm @@ -23,11 +23,31 @@ type Route type DocumentationSection = Introduction - | Syntax - | FromJavaScriptOrElm - | GuidaJson - | Records - | Interop + | WhatIsGuida + | Installation + | YourFirstProgram + | ProjectSetup + | MigrationFromElm + | SyntaxOverview + | ValuesAndTypes + | FunctionsAndExpressions + | ModulesAndImports + | CustomTypes + | PatternMatching + | ErrorHandling + | ImmutabilityAndPurity + | TheTypeSystem + | ConcurrencyAndEffects + | StateAndArchitecture + | ApplicationStructure + | TheGuidaArchitecture + | RoutingAndNavigation + | Interoperability + | ContributingGettingStarted + | ContributingWaysToContribute + | ContributingDevelopmentWorkflow + | ContributingReportingIssues + | ContributingJoinTheCommunity | Commands Command | Hints Hint @@ -52,13 +72,12 @@ type Hint | ImplicitCasts | ImportCycles | Imports - | InfiniteType + | InfiniteTypes | MissingPatterns | Optimize | PortModules | RecursiveAlias | Shadowing - | Tuples | TypeAnnotations @@ -181,15 +200,53 @@ parser : Parser (Route -> a) a parser = Parser.oneOf [ Parser.map Home Parser.top + + -- DOCS: Overview , Parser.map (Docs Introduction) (Parser.s "docs") - , Parser.map (Docs Syntax) (Parser.s "docs" Parser.s "syntax") - , Parser.map (Docs FromJavaScriptOrElm) (Parser.s "docs" Parser.s "from-javascript-or-elm") - , Parser.map (Docs GuidaJson) (Parser.s "docs" Parser.s "guida-json") - , Parser.map (Docs Records) (Parser.s "docs" Parser.s "records") - , Parser.map (Docs Interop) (Parser.s "docs" Parser.s "interop") + , Parser.map (Docs WhatIsGuida) (Parser.s "docs" Parser.s "what-is-guida") + + -- DOCS: Getting Started + , Parser.map (Docs Installation) (Parser.s "docs" Parser.s "installation") + , Parser.map (Docs YourFirstProgram) (Parser.s "docs" Parser.s "your-first-program") + , Parser.map (Docs ProjectSetup) (Parser.s "docs" Parser.s "project-setup") + , Parser.map (Docs MigrationFromElm) (Parser.s "docs" Parser.s "migration-from-elm") + + -- DOCS: The Language + , Parser.map (Docs SyntaxOverview) (Parser.s "docs" Parser.s "syntax-overview") + , Parser.map (Docs ValuesAndTypes) (Parser.s "docs" Parser.s "values-and-types") + , Parser.map (Docs FunctionsAndExpressions) (Parser.s "docs" Parser.s "functions-and-expressions") + , Parser.map (Docs ModulesAndImports) (Parser.s "docs" Parser.s "modules-and-imports") + , Parser.map (Docs CustomTypes) (Parser.s "docs" Parser.s "custom-types") + , Parser.map (Docs PatternMatching) (Parser.s "docs" Parser.s "pattern-matching") + , Parser.map (Docs ErrorHandling) (Parser.s "docs" Parser.s "error-handling") + + -- DOCS: Core Concepts + , Parser.map (Docs ImmutabilityAndPurity) (Parser.s "docs" Parser.s "immutability-and-purity") + , Parser.map (Docs TheTypeSystem) (Parser.s "docs" Parser.s "the-type-system") + , Parser.map (Docs ConcurrencyAndEffects) (Parser.s "docs" Parser.s "concurrency-and-effects") + , Parser.map (Docs StateAndArchitecture) (Parser.s "docs" Parser.s "state-and-architecture") + + -- DOCS: Building Applications + , Parser.map (Docs ApplicationStructure) (Parser.s "docs" Parser.s "application-structure") + , Parser.map (Docs TheGuidaArchitecture) (Parser.s "docs" Parser.s "the-guida-architecture") + , Parser.map (Docs RoutingAndNavigation) (Parser.s "docs" Parser.s "routing-and-navigation") + , Parser.map (Docs Interoperability) (Parser.s "docs" Parser.s "interoperability") + + -- DOCS: Contributing + , Parser.map (Docs ContributingGettingStarted) (Parser.s "docs" Parser.s "contributing" Parser.s "getting-started") + , Parser.map (Docs ContributingWaysToContribute) (Parser.s "docs" Parser.s "contributing" Parser.s "ways-to-contribute") + , Parser.map (Docs ContributingDevelopmentWorkflow) (Parser.s "docs" Parser.s "contributing" Parser.s "development-workflow") + , Parser.map (Docs ContributingReportingIssues) (Parser.s "docs" Parser.s "contributing" Parser.s "reporting-issues") + , Parser.map (Docs ContributingJoinTheCommunity) (Parser.s "docs" Parser.s "contributing" Parser.s "join-the-community") + + -- , Parser.map (Docs << Commands) (Parser.s "docs" Parser.s "1.0.0" Parser.s "commands" commandParser) , Parser.map (Docs << Hints) (Parser.s "docs" Parser.s "1.0.0" Parser.s "hints" hintParser) + + -- COMMUNITY , Parser.map Community (Parser.s "community") + + -- EXAMPLES , Parser.map Examples (Parser.s "examples") , Parser.map (Example Animation) (Parser.s "examples" Parser.s "animation") , Parser.map (Example Book) (Parser.s "examples" Parser.s "book") @@ -247,13 +304,12 @@ hintParser = , Parser.map ImplicitCasts (Parser.s "implicit-casts") , Parser.map ImportCycles (Parser.s "import-cycles") , Parser.map Imports (Parser.s "imports") - , Parser.map InfiniteType (Parser.s "infinite-type") + , Parser.map InfiniteTypes (Parser.s "infinite-types") , Parser.map MissingPatterns (Parser.s "missing-patterns") , Parser.map Optimize (Parser.s "optimize") , Parser.map PortModules (Parser.s "port-modules") , Parser.map RecursiveAlias (Parser.s "recursive-alias") , Parser.map Shadowing (Parser.s "shadowing") - , Parser.map Tuples (Parser.s "tuples") , Parser.map TypeAnnotations (Parser.s "type-annotations") ] From 92d693a094f25b9888dab7140f041e60e7496875 Mon Sep 17 00:00:00 2001 From: Decio Ferreira Date: Mon, 13 Oct 2025 18:57:01 +0100 Subject: [PATCH 08/11] fix some elm-review warnings --- src/Components/Note.elm | 15 ---------- src/Components/Properties.elm | 53 ----------------------------------- src/Components/References.elm | 23 --------------- src/Components/Table.elm | 49 -------------------------------- 4 files changed, 140 deletions(-) delete mode 100644 src/Components/Note.elm delete mode 100644 src/Components/Properties.elm delete mode 100644 src/Components/References.elm delete mode 100644 src/Components/Table.elm diff --git a/src/Components/Note.elm b/src/Components/Note.elm deleted file mode 100644 index ce0d9b8..0000000 --- a/src/Components/Note.elm +++ /dev/null @@ -1,15 +0,0 @@ -module Components.Note exposing (view) - -import Html exposing (Html) -import Html.Attributes as Attr -import Icon -import Svg.Attributes as SvgAttr - - -view : List (Html msg) -> Html msg -view children = - Html.div [ Attr.class "my-6 flex gap-2.5 rounded-2xl border border-amber-500/20 bg-amber-50/50 p-4 text-sm/6 text-amber-900 dark:border-amber-500/30 dark:bg-amber-500/5 dark:text-amber-200 dark:[--tw-prose-links-hover:var(--color-amber-300)] dark:[--tw-prose-links:var(--color-white)]" ] - [ Icon.info [ SvgAttr.class "mt-1 h-4 w-4 flex-none fill-amber-500 stroke-white dark:fill-amber-200/20 dark:stroke-amber-200" ] - , Html.div [ Attr.class "[&>:first-child]:mt-0 [&>:last-child]:mb-0" ] - children - ] diff --git a/src/Components/Properties.elm b/src/Components/Properties.elm deleted file mode 100644 index 29b8ab4..0000000 --- a/src/Components/Properties.elm +++ /dev/null @@ -1,53 +0,0 @@ -module Components.Properties exposing - ( Property - , view - ) - -import Html exposing (Html) -import Html.Attributes as Attr -import Html.Attributes.Aria as Aria - - -type alias Property msg = - { name : String - , type_ : Maybe String - , children : List (Html msg) - } - - -view : List (Property msg) -> Html msg -view properties = - Html.div [ Attr.class "my-6" ] - [ Html.ul - [ Aria.role "list" - , Attr.class "m-0 list-none divide-y divide-zinc-900/5 p-0 dark:divide-white/5" - ] - (List.map propertyView properties) - ] - - -propertyView : Property msg -> Html msg -propertyView property = - let - typeDescriptionTerm : List (Html msg) - typeDescriptionTerm = - property.type_ - |> Maybe.map - (\type_ -> - [ Html.dt [ Attr.class "sr-only" ] [ Html.text "Type" ] - , Html.dd [ Attr.class "font-mono text-xs text-zinc-400 dark:text-zinc-500" ] [ Html.text type_ ] - ] - ) - |> Maybe.withDefault [] - in - Html.li [ Attr.class "m-0 px-0 py-4 first:pt-0 last:pb-0" ] - [ Html.dl [ Attr.class "m-0 flex flex-wrap items-center gap-x-3 gap-y-2" ] - (Html.dt [ Attr.class "sr-only" ] [ Html.text "Name" ] - :: Html.dd [] [ Html.code [] [ Html.text property.name ] ] - :: typeDescriptionTerm - ++ [ Html.dt [ Attr.class "sr-only" ] [ Html.text "Description" ] - , Html.dd [ Attr.class "w-full flex-none [&>:first-child]:mt-0 [&>:last-child]:mb-0" ] - property.children - ] - ) - ] diff --git a/src/Components/References.elm b/src/Components/References.elm deleted file mode 100644 index 920081a..0000000 --- a/src/Components/References.elm +++ /dev/null @@ -1,23 +0,0 @@ -module Components.References exposing (view) - -import Components.Button as Button -import Html exposing (Html) -import Html.Attributes as Attr -import Html.Attributes.Aria as Aria - - -view : List String -> Html msg -view links = - Html.div [ Attr.class "my-6" ] - [ Html.hr [] [] - , Html.h2 [] [ Html.text "References" ] - , Html.ul [ Aria.role "list" ] - (List.map linkView links) - ] - - -linkView : String -> Html msg -linkView href = - Html.li [] - [ Button.view (Button.Link href) Button.Text Nothing [] [ Html.text href ] - ] diff --git a/src/Components/Table.elm b/src/Components/Table.elm deleted file mode 100644 index 66e4d35..0000000 --- a/src/Components/Table.elm +++ /dev/null @@ -1,49 +0,0 @@ -module Components.Table exposing (view) - -import Html exposing (Html) -import Html.Attributes as Attr - - -view : List (List (Html msg)) -> List (List (List (Html msg))) -> Html msg -view headers rows = - Html.div [ Attr.class "overflow-hidden shadow-sm outline-1 outline-black/5 sm:rounded-lg dark:shadow-none dark:-outline-offset-1 dark:outline-white/10" ] - [ Html.table [ Attr.class "relative min-w-full divide-y divide-gray-300 dark:divide-white/15" ] - [ Html.thead [ Attr.class "bg-gray-50 dark:bg-gray-800/75" ] [ Html.tr [] (List.indexedMap headerView headers) ] - , Html.tbody [ Attr.class "divide-y divide-gray-200 bg-white dark:divide-white/10 dark:bg-gray-800/50" ] - (List.map rowView rows) - ] - ] - - -headerView : Int -> List (Html msg) -> Html msg -headerView index children = - Html.th - [ Attr.scope "col" - , Attr.class - (if index == 0 then - "px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-white" - - else - "px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-200" - ) - ] - children - - -rowView : List (List (Html msg)) -> Html msg -rowView row = - Html.tr [] (List.indexedMap cellView row) - - -cellView : Int -> List (Html msg) -> Html msg -cellView index children = - Html.td - [ Attr.class - (if index == 0 then - "py-4 pr-3 pl-4 text-sm whitespace-nowrap text-gray-500 sm:pl-6 dark:text-white" - - else - "px-3 py-4 text-sm whitespace-nowrap text-gray-500 dark:text-gray-400" - ) - ] - children From 79f12b419d614ded207a14f74c115aa7529034cb Mon Sep 17 00:00:00 2001 From: Decio Ferreira Date: Mon, 13 Oct 2025 23:11:14 +0100 Subject: [PATCH 09/11] WIP updated version --- src/Page/Docs.elm | 212 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 172 insertions(+), 40 deletions(-) diff --git a/src/Page/Docs.elm b/src/Page/Docs.elm index 1d57ea2..dffc72b 100644 --- a/src/Page/Docs.elm +++ b/src/Page/Docs.elm @@ -9,6 +9,7 @@ import Components.CodeBlock as CodeBlock import Html exposing (Html) import Html.Attributes as Attr import Html.Attributes.Aria as Aria +import Icon import Layout.Main as Layout import Layout.Navigation exposing (Navigation) import Markdown.Block @@ -20,6 +21,7 @@ import Parser.Advanced import Result.Extra as Result import Route import Session exposing (Session) +import Svg.Attributes as SvgAttr @@ -106,6 +108,14 @@ htmlRenderer = ) |> Markdown.Html.withAttribute "name" |> Markdown.Html.withOptionalAttribute "type" + , Markdown.Html.tag "todo" + (\_ -> + Html.div [ Attr.class "my-6 flex gap-2.5 rounded-2xl border border-amber-500/20 bg-amber-50/50 p-4 text-sm/6 text-amber-900 dark:border-amber-500/30 dark:bg-amber-500/5 dark:text-amber-200 dark:[--tw-prose-links-hover:var(--color-amber-300)] dark:[--tw-prose-links:var(--color-white)]" ] + [ Icon.info [ SvgAttr.class "mt-1 h-4 w-4 flex-none fill-amber-500 stroke-white dark:fill-amber-200/20 dark:stroke-amber-200" ] + , Html.div [ Attr.class "[&>:first-child]:mt-0 [&>:last-child]:mb-0" ] + [ Html.text "🚧 Work in Progress" ] + ] + ) ] } @@ -141,76 +151,172 @@ view session toSessionMsg model = whatIsGuidaView Route.Installation -> - markdownRender "# Installation" + markdownRender """ +# Installation + + +""" Route.YourFirstProgram -> - markdownRender "# Your First Program" + markdownRender """ +# Your First Program + + +""" Route.ProjectSetup -> - markdownRender "# Project Setup" + markdownRender """ +# Project Setup + + +""" Route.MigrationFromElm -> - markdownRender "# Migration from Elm" + markdownRender """ +# Migration from Elm + + +""" Route.SyntaxOverview -> - markdownRender "# Syntax Overview" + markdownRender """ +# Syntax Overview + + +""" Route.ValuesAndTypes -> - markdownRender "# Values and Types" + markdownRender """ +# Values and Types + + +""" Route.FunctionsAndExpressions -> - markdownRender "# Functions and Expressions" + markdownRender """ +# Functions and Expressions + + +""" Route.ModulesAndImports -> - markdownRender "# Modules and Imports" + markdownRender """ +# Modules and Imports + + +""" Route.CustomTypes -> - markdownRender "# Custom Types" + markdownRender """ +# Custom Types + + +""" Route.PatternMatching -> - markdownRender "# Pattern Matching" + markdownRender """ +# Pattern Matching + + +""" Route.ErrorHandling -> - markdownRender "# Error Handling" + markdownRender """ +# Error Handling + + +""" Route.ImmutabilityAndPurity -> - markdownRender "# Immutability and Purity" + markdownRender """ +# Immutability and Purity + + +""" Route.TheTypeSystem -> - markdownRender "# The Type System" + markdownRender """ +# The Type System + + +""" Route.ConcurrencyAndEffects -> - markdownRender "# Concurrency and Effects" + markdownRender """ +# Concurrency and Effects + + +""" Route.StateAndArchitecture -> - markdownRender "# State and Architecture" + markdownRender """ +# State and Architecture + + +""" Route.ApplicationStructure -> - markdownRender "# Application Structure" + markdownRender """ +# Application Structure + + +""" Route.TheGuidaArchitecture -> - markdownRender "# The Guida Architecture" + markdownRender """ +# The Guida Architecture + + +""" Route.RoutingAndNavigation -> - markdownRender "# Routing and Navigation" + markdownRender """ +# Routing and Navigation + + +""" Route.Interoperability -> - markdownRender "# Interoperability" + markdownRender """ +# Interoperability + + +""" Route.ContributingGettingStarted -> - markdownRender "# Getting Started" + markdownRender """ +# Getting Started + + +""" Route.ContributingWaysToContribute -> - markdownRender "# Ways To Contribute" + markdownRender """ +# Ways To Contribute + + +""" Route.ContributingDevelopmentWorkflow -> - markdownRender "# Development Workflow" + markdownRender """ +# Development Workflow + + +""" Route.ContributingReportingIssues -> - markdownRender "# Reporting Issues" + markdownRender """ +# Reporting Issues + + +""" Route.ContributingJoinTheCommunity -> - markdownRender "# Join the Community" + markdownRender """ +# Join the Community + + +""" Route.Commands command -> commandView command @@ -454,6 +560,8 @@ You can customize this command with the following flags: markdownRender """ # guida init + + --- ## References @@ -462,36 +570,60 @@ You can customize this command with the following flags: """ Route.Make -> - [ Html.h1 [] [ Html.text "guida make" ] - ] + markdownRender """ +# guida make + + +""" Route.Install -> - [ Html.h1 [] [ Html.text "guida install" ] - ] + markdownRender """ +# guida install + + +""" Route.Uninstall -> - [ Html.h1 [] [ Html.text "guida uninstall" ] - ] + markdownRender """ +# guida uninstall + + +""" Route.Bump -> - [ Html.h1 [] [ Html.text "guida bump" ] - ] + markdownRender """ +# guida bump + + +""" Route.Diff -> - [ Html.h1 [] [ Html.text "guida diff" ] - ] + markdownRender """ +# guida diff + + +""" Route.Publish -> - [ Html.h1 [] [ Html.text "guida publish" ] - ] + markdownRender """ +# guida publish + + +""" Route.Format -> - [ Html.h1 [] [ Html.text "guida format" ] - ] + markdownRender """ +# guida format + + +""" Route.Test -> - [ Html.h1 [] [ Html.text "guida test" ] - ] + markdownRender """ +# guida test + + +""" hintView : Route.Hint -> List (Html msg) From a493dcd4674c7a6c2d47e2da3713931f3ee164ec Mon Sep 17 00:00:00 2001 From: Decio Ferreira Date: Thu, 16 Oct 2025 22:33:54 +0100 Subject: [PATCH 10/11] WIP try registry --- elm.json | 5 +- src/Components/Button.elm | 2 +- src/Icon.elm | 124 +++++++++++++++++- src/Layout/Main.elm | 13 +- src/Page/Try.elm | 263 +++++++++++++++++++++++++++++--------- 5 files changed, 337 insertions(+), 70 deletions(-) diff --git a/elm.json b/elm.json index b76c672..5a0d9c8 100644 --- a/elm.json +++ b/elm.json @@ -9,6 +9,7 @@ "debois/elm-dom": "1.3.0", "dillonkearns/elm-markdown": "7.0.1", "elm/browser": "1.0.2", + "elm/bytes": "1.0.8", "elm/core": "1.0.5", "elm/html": "1.0.0", "elm/http": "2.0.0", @@ -18,10 +19,10 @@ "elm/svg": "1.0.1", "elm/url": "1.0.0", "elm-community/result-extra": "2.4.0", - "fapian/elm-html-aria": "1.4.0" + "fapian/elm-html-aria": "1.4.0", + "krisajenkins/remotedata": "6.1.0" }, "indirect": { - "elm/bytes": "1.0.8", "elm/file": "1.0.5", "elm/regex": "1.0.0", "elm/time": "1.0.0", diff --git a/src/Components/Button.elm b/src/Components/Button.elm index be51d7d..14384d0 100644 --- a/src/Components/Button.elm +++ b/src/Components/Button.elm @@ -74,7 +74,7 @@ view type_ variant maybeArrow attrs children = classAttrs : List (Html.Attribute msg) classAttrs = [ Attr.classList - [ ( "inline-flex gap-0.5 justify-center overflow-hidden text-sm font-medium transition", True ) + [ ( "inline-flex gap-0.5 items-center justify-center overflow-hidden text-sm font-medium transition", True ) , ( variantStyles variant, True ) ] ] diff --git a/src/Icon.elm b/src/Icon.elm index 45270e0..6ec44d6 100644 --- a/src/Icon.elm +++ b/src/Icon.elm @@ -1,17 +1,60 @@ module Icon exposing - ( discord + ( arrowPath + , check + , discord , github , info + , lockClosed , logo + , plus + , trash + , xMark ) import Html -import Html.Attributes as Attr import Html.Attributes.Aria as Aria import Svg exposing (Svg) import Svg.Attributes as SvgAttr +arrowPath : List (Html.Attribute msg) -> Svg msg +arrowPath attrs = + Svg.svg + (SvgAttr.fill "none" + :: SvgAttr.viewBox "0 0 24 24" + :: SvgAttr.strokeWidth "1.5" + :: SvgAttr.stroke "currentColor" + :: Aria.ariaHidden True + :: attrs + ) + [ Svg.path + [ SvgAttr.strokeLinecap "round" + , SvgAttr.strokeLinejoin "round" + , SvgAttr.d "M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99" + ] + [] + ] + + +check : List (Html.Attribute msg) -> Svg msg +check attrs = + Svg.svg + (SvgAttr.fill "none" + :: SvgAttr.viewBox "0 0 24 24" + :: SvgAttr.strokeWidth "1.5" + :: SvgAttr.stroke "currentColor" + :: Aria.ariaHidden True + :: attrs + ) + [ Svg.path + [ SvgAttr.strokeLinecap "round" + , SvgAttr.strokeLinejoin "round" + , SvgAttr.d "m4.5 12.75 6 6 9-13.5" + ] + [] + ] + + github : List (Html.Attribute msg) -> Svg msg github attrs = Svg.svg @@ -71,12 +114,30 @@ discord attrs = ] +lockClosed : List (Html.Attribute msg) -> Svg msg +lockClosed attrs = + Svg.svg + (SvgAttr.viewBox "0 0 24 24" + :: SvgAttr.fill "none" + :: SvgAttr.strokeWidth "1.5" + :: SvgAttr.stroke "currentColor" + :: attrs + ) + [ Svg.path + [ SvgAttr.strokeLinecap "round" + , SvgAttr.strokeLinejoin "round" + , SvgAttr.d "M16.5 10.5V6.75a4.5 4.5 0 1 0-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 0 0 2.25-2.25v-6.75a2.25 2.25 0 0 0-2.25-2.25H6.75a2.25 2.25 0 0 0-2.25 2.25v6.75a2.25 2.25 0 0 0 2.25 2.25Z" + ] + [] + ] + + logo : List (Html.Attribute msg) -> Svg msg logo attrs = Svg.svg (SvgAttr.viewBox "0 0 52.917 52.917" :: SvgAttr.fill "currentColor" - :: Attr.attribute "aria-hidden" "true" + :: Aria.ariaHidden True :: attrs ) [ Svg.path @@ -84,3 +145,60 @@ logo attrs = ] [] ] + + +plus : List (Html.Attribute msg) -> Svg msg +plus attrs = + Svg.svg + (SvgAttr.viewBox "0 0 24 24" + :: SvgAttr.fill "none" + :: SvgAttr.strokeWidth "1.5" + :: SvgAttr.stroke "currentColor" + :: Aria.ariaHidden True + :: attrs + ) + [ Svg.path + [ SvgAttr.strokeLinecap "round" + , SvgAttr.strokeLinejoin "round" + , SvgAttr.d "M12 4.5v15m7.5-7.5h-15" + ] + [] + ] + + +trash : List (Html.Attribute msg) -> Svg msg +trash attrs = + Svg.svg + (SvgAttr.viewBox "0 0 24 24" + :: SvgAttr.fill "none" + :: SvgAttr.strokeWidth "1.5" + :: SvgAttr.stroke "currentColor" + :: Aria.ariaHidden True + :: attrs + ) + [ Svg.path + [ SvgAttr.strokeLinecap "round" + , SvgAttr.strokeLinejoin "round" + , SvgAttr.d "m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" + ] + [] + ] + + +xMark : List (Html.Attribute msg) -> Svg msg +xMark attrs = + Svg.svg + (SvgAttr.fill "none" + :: SvgAttr.viewBox "0 0 24 24" + :: SvgAttr.strokeWidth "1.5" + :: SvgAttr.stroke "currentColor" + :: Aria.ariaHidden True + :: attrs + ) + [ Svg.path + [ SvgAttr.strokeLinecap "round" + , SvgAttr.strokeLinejoin "round" + , SvgAttr.d "M6 18 18 6M6 6l12 12" + ] + [] + ] diff --git a/src/Layout/Main.elm b/src/Layout/Main.elm index b209a75..8ee2ddb 100644 --- a/src/Layout/Main.elm +++ b/src/Layout/Main.elm @@ -130,25 +130,24 @@ view config session toSessionMsg children = ] -fullscreenView : Session -> (Session.Msg -> msg) -> List (Html msg) -> List (Html msg) -fullscreenView session toSessionMsg children = +fullscreenView : Session -> (Session.Msg -> msg) -> Html msg -> List (Html msg) -> List (Html msg) +fullscreenView session toSessionMsg packagesDialogView children = let config : Config () config = { sidebarNavigation = [], currentRoute = () } in [ Html.div [ Attr.class "contents" ] - [ Html.div [ Attr.class "w-full" ] - [ Html.div [ Attr.class "h-full" ] + [ Html.div [ Attr.class "w-screen" ] + [ Html.div [ Attr.class "h-screen" ] [ Html.header [ Attr.class "contents lg:pointer-events-none lg:fixed lg:inset-0 lg:z-40 lg:flex" ] [ sidebarView config session toSessionMsg ] , Html.div [ Attr.class "relative flex h-full flex-col pt-14" ] - [ Html.main_ [ Attr.class "flex-auto prose dark:prose-invert" ] - children - ] + children ] ] ] + , packagesDialogView , dialogView config session toSessionMsg ] diff --git a/src/Page/Try.elm b/src/Page/Try.elm index 9978884..191a2ff 100644 --- a/src/Page/Try.elm +++ b/src/Page/Try.elm @@ -17,13 +17,16 @@ import Elm.Error import Errors import Html exposing (Html) import Html.Attributes as Attr +import Html.Attributes.Aria as Aria import Html.Events as Events import Http +import Icon import Json.Decode as Decode import Json.Encode as Encode import Layout.Main as Layout import Route import Session exposing (Session) +import Svg.Attributes as SvgAttr port setEditorContentAndRebuild : String -> Cmd msg @@ -71,15 +74,18 @@ init maybeExample = , mouseX = 0 , mouseY = 0 } - , case maybeExample of - Just example -> - Http.get - { url = "/example-files/" ++ Route.exampleSrc example ++ ".guida" - , expect = Http.expectString GotExampleContent - } - - Nothing -> - Cmd.none + , Cmd.batch + [ case maybeExample of + Just example -> + Http.get + { url = "/example-files/" ++ Route.exampleSrc example ++ ".guida" + , expect = Http.expectString GotExampleContent + } + + Nothing -> + Cmd.none + , registryFetch + ] ) @@ -93,6 +99,7 @@ type Msg | Rebuild | RebuildResult Encode.Value | OnMouseOver DOM.Rectangle Int Int + | GotRegistry (Result Http.Error (List Package)) update : Msg -> Model -> ( Model, Cmd Msg ) @@ -128,6 +135,19 @@ update msg model = OnMouseOver { left, top } clientX clientY -> ( { model | mouseX = clientX - round left, mouseY = clientY - round top }, Cmd.none ) + GotRegistry (Ok news) -> + ( { model + | registry = + registryInitialWithDefaults model.defaultDirect model.defaultIndirect + |> registryFromNews news + |> Fetched.Success + } + , Cmd.none + ) + + GotRegistry (Err ((Http.BadBody errMsg) as err)) -> + ( { model | registry = Fetched.Failed err }, Cmd.none ) + decodeResult : Decode.Decoder (Result Elm.Error.Error String) decodeResult = @@ -238,10 +258,10 @@ view session toSessionMsg toMsg model = Nothing -> "Try Guida!" , body = - Layout.fullscreenView session toSessionMsg <| - [ Html.section [ Attr.class "h-full grid grid-cols-none grid-rows-2 sm:grid-cols-2 sm:grid-rows-none" ] - [ Html.aside - [ Attr.class "grid grid-cols-none grid-rows-2 overflow-y-hidden border-b sm:border-b-0 sm:border-r border-gray-200" + Layout.fullscreenView session toSessionMsg packagesDialogView <| + [ Html.section [ Attr.class "flex flex-col h-min-full md:h-full md:grid md:grid-cols-2 md:grid-rows-[1fr_min-content]" ] + [ Html.div + [ Attr.class "h-full overflow-auto border-b md:border-b-0 md:border-r border-gray-200" ] [ Html.node "wc-codemirror" [ Attr.id "editor" @@ -259,8 +279,24 @@ view session toSessionMsg toMsg model = [ Html.node "link" [ Attr.rel "stylesheet", Attr.href "/codemirror/theme/xq-dark.css" ] [] , Html.node "link" [ Attr.rel "stylesheet", Attr.href "/codemirror/theme/xq-light.css" ] [] ] - , Html.div [] - [ Button.view Button.Button Button.Primary Nothing [ Events.onClick (toMsg Rebuild) ] [ Html.text "rebuild" ] + ] + , Html.div [ Attr.class "flex justify-between col-start-1 row-start-2 border-gray-200 border-b md:border-b-0 md:border-r md:border-t" ] + [ Html.aside [] + [ Html.ul [ Aria.role "list", Attr.class "flex items-center gap-6" ] + [ Html.li [] + [ Html.button + [ Attr.class "flex gap-0.5 items-center py-0.5 px-2 text-sm/7 text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" + , Events.onClick (toMsg Rebuild) + ] + [ Html.text "Packages" + ] + ] + ] + ] + , Html.aside [] + [ Html.ul [ Aria.role "list", Attr.class "flex items-center gap-6" ] + [ Html.li [] [ rebuildButton toMsg model ] + ] ] ] , outputView toMsg model @@ -269,6 +305,160 @@ view session toSessionMsg toMsg model = } +packagesDialogView : Html msg +packagesDialogView = + let + openAttrs : List (Html.Attribute msg) + openAttrs = + if True then + [ Attr.attribute "open" "true" ] + + else + [] + in + Html.node "dialog" + (Attr.class "fixed inset-0 z-50" + :: openAttrs + ) + [ Html.div + [ Attr.class "fixed inset-0 top-14 bg-zinc-400/20 backdrop-blur-xs data-closed:opacity-0 data-enter:duration-300 data-enter:ease-out data-leave:duration-200 data-leave:ease-in dark:bg-black/40" + + -- , Events.onClick (toSessionMsg Session.ToggleMobileNavigation) + ] + [] + , Html.div [ Attr.class "fixed top-0 bottom-0 left-0 w-full overflow-y-auto bg-white px-4 pt-6 pb-4 ring-1 shadow-lg shadow-zinc-900/10 ring-zinc-900/7.5 duration-500 ease-in-out data-closed:-translate-x-full min-[416px]:max-w-sm sm:px-6 sm:pb-10 dark:bg-zinc-900 dark:ring-zinc-800" ] + [ Html.nav + [ Attr.class "relative flex flex-1 flex-col" + ] + [ Html.ul [ Aria.role "list", Attr.class "flex flex-1 flex-col gap-y-4" ] + [ Html.li [] + [ Html.div + [ Attr.class "text-xs/6 font-semibold text-gray-400" + ] + [ Html.text "Installed" ] + , Html.ul + [ Aria.role "list" + , Attr.class "mt-2 space-y-1" + ] + [ Html.li + [ Attr.class "group flex justify-between gap-x-3 rounded-md p-1 text-sm/6 font-semibold text-gray-700 hover:bg-gray-50 hover:text-amber-600 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-white" + ] + [ Html.span + [ Attr.class "truncate" + ] + [ Html.text "elm/random" ] + , Html.div [ Attr.class "flex gap-x-3" ] + [ Html.span [ Attr.class "flex size-6 shrink-0 items-center text-[0.625rem] font-medium text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] + [ Html.text "2.0.1" ] + , Button.view Button.Button Button.Text Nothing [] [ Icon.lockClosed [ SvgAttr.class "h-5 w-5 text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] ] + ] + ] + ] + ] + , Html.li [] + [ Html.div + [ Attr.class "text-xs/6 font-semibold text-gray-400" + ] + [ Html.text "Registry" ] + , Html.input + [ Attr.class "mt-2 block w-full rounded-md bg-white px-3 py-1.5 text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6 dark:bg-white/5 dark:text-white dark:outline-white/10 dark:placeholder:text-gray-500 dark:focus:outline-indigo-500" + , Attr.type_ "search" + , Attr.id "registry-search" + , Attr.placeholder "Search" + , Aria.ariaLabel "Search" + ] + [] + , Html.ul + [ Aria.role "list" + , Attr.class "mt-2 space-y-1" + ] + [ Html.li + [ Attr.class "group flex justify-between gap-x-3 rounded-md p-1 text-sm/6 font-semibold text-gray-700 hover:bg-gray-50 hover:text-amber-600 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-white" + ] + [ Html.span + [ Attr.class "truncate" + ] + [ Html.text "elm/html" ] + , Html.div [ Attr.class "flex gap-x-3" ] + [ Html.span [ Attr.class "flex size-6 shrink-0 items-center text-[0.625rem] font-medium text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] + [ Html.text "1.0.0" ] + , Button.view Button.Button Button.Text Nothing [] [ Icon.plus [ SvgAttr.class "h-5 w-5 text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] ] + ] + ] + , Html.li + [ Attr.class "group flex justify-between gap-x-3 rounded-md p-1 text-sm/6 font-semibold text-gray-700 hover:bg-gray-50 hover:text-amber-600 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-white" + ] + [ Html.span + [ Attr.class "truncate" + ] + [ Html.text "elm/random" ] + , Html.div [ Attr.class "flex gap-x-3" ] + [ Html.span [ Attr.class "flex size-6 shrink-0 items-center text-[0.625rem] font-medium text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] + [ Html.text "2.0.1" ] + , Button.view Button.Button Button.Text Nothing [] [ Icon.trash [ SvgAttr.class "h-5 w-5 text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] ] + ] + ] + , Html.li + [ Attr.class "group flex justify-between gap-x-3 rounded-md p-1 text-sm/6 font-semibold text-gray-700 hover:bg-gray-50 hover:text-amber-600 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-white" + ] + [ Html.span + [ Attr.class "truncate" + ] + [ Html.text "elm/random" ] + , Html.div [ Attr.class "flex gap-x-3" ] + [ Html.span [ Attr.class "flex size-6 shrink-0 items-center text-[0.625rem] font-medium text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] + [ Html.text "2.0.1" ] + , Button.view Button.Button Button.Text Nothing [] [ Icon.lockClosed [ SvgAttr.class "h-5 w-5 text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] ] + ] + ] + ] + ] + ] + ] + ] + ] + + +rebuildButton : (Msg -> msg) -> Model -> Html msg +rebuildButton toMsg model = + case model.status of + NotAsked -> + Html.button + [ Attr.class "flex gap-0.5 items-center py-0.5 px-2 text-sm/7 text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" + , Events.onClick (toMsg Rebuild) + ] + [ Icon.arrowPath [ SvgAttr.class "h-5 w-5" ] + , Html.text "Rebuild" + ] + + Compiling -> + Html.button + [ Attr.class "flex gap-0.5 items-center py-0.5 px-2 text-sm/7 text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" + , Attr.disabled True + ] + [ Icon.arrowPath [ SvgAttr.class "h-5 w-5 animate-spin" ] + , Html.text "Compiling..." + ] + + Success -> + Html.button + [ Attr.class "flex gap-0.5 items-center py-0.5 px-2 text-sm/7 text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" + , Events.onClick (toMsg Rebuild) + ] + [ Icon.check [ SvgAttr.class "h-5 w-5 text-green-600" ] + , Html.text "Success" + ] + + ProblemsFound -> + Html.button + [ Attr.class "flex gap-0.5 items-center py-0.5 px-2 text-sm/7 text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" + , Events.onClick (toMsg Rebuild) + ] + [ Icon.xMark [ SvgAttr.class "h-5 w-5 text-red-600" ] + , Html.text "Problems found" + ] + + outputView : (Msg -> msg) -> Model -> Html msg outputView toMsg model = case ( model.example, model.maybeResult ) of @@ -322,7 +512,7 @@ outputView toMsg model = Html.main_ [ Attr.class "overflow-y-auto" ] [] ( _, Just (Ok output) ) -> - Html.main_ [ Attr.class "overflow-y-auto bg-white" ] + Html.main_ [ Attr.class "row-span-2 overflow-y-auto bg-white" ] [ Html.iframe [ Attr.class "h-full w-full" , Attr.srcdoc output @@ -334,44 +524,3 @@ outputView toMsg model = Html.main_ [ Attr.class "overflow-y-auto bg-white" ] [ Errors.viewError error ] - - - --- headerMode : Model -> Layout.Header.Mode Msg --- headerMode model = --- Layout.Header.Custom (Just (rebuildButton model)) [] [] --- rebuildButton : Model -> Layout.Header.Item Msg --- rebuildButton model = --- case model.status of --- NotAsked -> --- Layout.Header.Button --- { label = --- [ Icon.arrowPath [ SvgAttr.class "-ml-0.5 size-5" ] --- , Html.text "Rebuild" --- ] --- , msg = Layout.Header.Enabled Rebuild --- } --- Compiling -> --- Layout.Header.Button --- { label = --- [ Icon.arrowPath [ SvgAttr.class "-ml-0.5 size-5 animate-spin" ] --- , Html.text "Compiling..." --- ] --- , msg = Layout.Header.Disabled --- } --- Success -> --- Layout.Header.Button --- { label = --- [ Icon.check [ SvgAttr.class "-ml-0.5 size-5" ] --- , Html.text "Success" --- ] --- , msg = Layout.Header.Enabled Rebuild --- } --- ProblemsFound -> --- Layout.Header.Button --- { label = --- [ Icon.xMark [ SvgAttr.class "-ml-0.5 size-5" ] --- , Html.text "Problems found" --- ] --- , msg = Layout.Header.Enabled Rebuild --- } From 2a27b0dac8ee2fb82218e8c8aca4e8824969f0b2 Mon Sep 17 00:00:00 2001 From: Decio Ferreira Date: Tue, 21 Oct 2025 22:54:50 +0100 Subject: [PATCH 11/11] WIP try and example pages --- elm.json | 11 +- lib/index.js | 59 +- package-lock.json | 1 + package.json | 3 +- public/assets/public-opinion.txt | 11405 ++++++++++++++++++++ public/example-files/animation.guida | 26 - public/example-files/book.guida | 89 - public/example-files/buttons.guida | 63 - public/example-files/cards.guida | 162 - public/example-files/clock.guida | 134 - public/example-files/crate.guida | 227 - public/example-files/cube.guida | 200 - public/example-files/drag-and-drop.guida | 140 - public/example-files/first-person.guida | 377 - public/example-files/forms.guida | 86 - public/example-files/groceries.guida | 22 - public/example-files/hello.guida | 7 - public/example-files/image-previews.guida | 162 - public/example-files/keyboard.guida | 25 - public/example-files/mario.guida | 102 - public/example-files/mouse.guida | 30 - public/example-files/numbers.guida | 84 - public/example-files/picture.guida | 17 - public/example-files/positions.guida | 96 - public/example-files/quotes.guida | 139 - public/example-files/shapes.guida | 79 - public/example-files/text-fields.guida | 60 - public/example-files/thwomp.guida | 304 - public/example-files/time.guida | 95 - public/example-files/triangle.guida | 140 - public/example-files/turtle.guida | 34 - public/example-files/upload.guida | 86 - public/images/mario/jump/left.gif | Bin 0 -> 287 bytes public/images/mario/jump/right.gif | Bin 0 -> 283 bytes public/images/mario/stand/left.gif | Bin 0 -> 357 bytes public/images/mario/stand/right.gif | Bin 0 -> 364 bytes public/images/mario/walk/left.gif | Bin 0 -> 1732 bytes public/images/mario/walk/right.gif | Bin 0 -> 1763 bytes public/images/thwomp-face.jpg | Bin 0 -> 12683 bytes public/images/thwomp-side.jpg | Bin 0 -> 6783 bytes public/images/turtle.gif | Bin 0 -> 19427 bytes public/images/wood-crate.jpg | Bin 0 -> 180105 bytes src/Components/Button.elm | 11 +- src/Components/ResourcePattern.elm | 10 +- src/Constant.elm | 6 + src/Data/Deps.elm | 50 + src/Data/Examples.elm | 3502 ++++++ src/Data/Exit.elm | 107 + src/Data/Fetched.elm | 71 + src/Data/Frame.elm | 251 + src/Data/Header.elm | 345 + src/Data/Hint.elm | 260 + src/Data/Http.elm | 58 + src/Data/Problem.elm | 254 + src/Data/Registry.elm | 289 + src/Data/Registry/Defaults.elm | 64 + src/Data/Registry/Package.elm | 65 + src/Data/Registry/Solution.elm | 78 + src/Data/Registry/Status.elm | 114 + src/Data/Status.elm | 196 + src/Data/Time.elm | 387 + src/Data/Version.elm | 53 + src/Data/Window.elm | 17 + src/Icon.elm | 40 +- src/Page/Try.elm | 402 +- src/Ui/Color.elm | 71 + src/Ui/ColumnDivider.elm | 313 + src/Ui/Editor.elm | 337 + src/Ui/Elements.elm | 77 + src/Ui/Font.elm | 19 + src/Ui/Icon.elm | 98 + src/Ui/Navigation.elm | 203 + src/Ui/Package.elm | 620 ++ src/Ui/Problem.elm | 283 + 74 files changed, 19955 insertions(+), 3161 deletions(-) create mode 100644 public/assets/public-opinion.txt delete mode 100644 public/example-files/animation.guida delete mode 100644 public/example-files/book.guida delete mode 100644 public/example-files/buttons.guida delete mode 100644 public/example-files/cards.guida delete mode 100644 public/example-files/clock.guida delete mode 100644 public/example-files/crate.guida delete mode 100644 public/example-files/cube.guida delete mode 100644 public/example-files/drag-and-drop.guida delete mode 100644 public/example-files/first-person.guida delete mode 100644 public/example-files/forms.guida delete mode 100644 public/example-files/groceries.guida delete mode 100644 public/example-files/hello.guida delete mode 100644 public/example-files/image-previews.guida delete mode 100644 public/example-files/keyboard.guida delete mode 100644 public/example-files/mario.guida delete mode 100644 public/example-files/mouse.guida delete mode 100644 public/example-files/numbers.guida delete mode 100644 public/example-files/picture.guida delete mode 100644 public/example-files/positions.guida delete mode 100644 public/example-files/quotes.guida delete mode 100644 public/example-files/shapes.guida delete mode 100644 public/example-files/text-fields.guida delete mode 100644 public/example-files/thwomp.guida delete mode 100644 public/example-files/time.guida delete mode 100644 public/example-files/triangle.guida delete mode 100644 public/example-files/turtle.guida delete mode 100644 public/example-files/upload.guida create mode 100755 public/images/mario/jump/left.gif create mode 100755 public/images/mario/jump/right.gif create mode 100755 public/images/mario/stand/left.gif create mode 100755 public/images/mario/stand/right.gif create mode 100755 public/images/mario/walk/left.gif create mode 100755 public/images/mario/walk/right.gif create mode 100644 public/images/thwomp-face.jpg create mode 100644 public/images/thwomp-side.jpg create mode 100644 public/images/turtle.gif create mode 100644 public/images/wood-crate.jpg create mode 100644 src/Constant.elm create mode 100644 src/Data/Deps.elm create mode 100644 src/Data/Examples.elm create mode 100644 src/Data/Exit.elm create mode 100644 src/Data/Fetched.elm create mode 100644 src/Data/Frame.elm create mode 100644 src/Data/Header.elm create mode 100644 src/Data/Hint.elm create mode 100644 src/Data/Http.elm create mode 100644 src/Data/Problem.elm create mode 100644 src/Data/Registry.elm create mode 100644 src/Data/Registry/Defaults.elm create mode 100644 src/Data/Registry/Package.elm create mode 100644 src/Data/Registry/Solution.elm create mode 100644 src/Data/Registry/Status.elm create mode 100644 src/Data/Status.elm create mode 100644 src/Data/Time.elm create mode 100644 src/Data/Version.elm create mode 100644 src/Data/Window.elm create mode 100644 src/Ui/Color.elm create mode 100644 src/Ui/ColumnDivider.elm create mode 100644 src/Ui/Editor.elm create mode 100644 src/Ui/Elements.elm create mode 100644 src/Ui/Font.elm create mode 100644 src/Ui/Icon.elm create mode 100644 src/Ui/Navigation.elm create mode 100644 src/Ui/Package.elm create mode 100644 src/Ui/Problem.elm diff --git a/elm.json b/elm.json index 5a0d9c8..6b45854 100644 --- a/elm.json +++ b/elm.json @@ -16,17 +16,22 @@ "elm/json": "1.1.3", "elm/parser": "1.1.0", "elm/project-metadata-utils": "1.0.2", + "elm/regex": "1.0.0", "elm/svg": "1.0.1", + "elm/time": "1.0.0", "elm/url": "1.0.0", "elm-community/result-extra": "2.4.0", "fapian/elm-html-aria": "1.4.0", - "krisajenkins/remotedata": "6.1.0" + "feathericons/elm-feather": "1.5.0", + "justinmimbs/time-extra": "1.2.0", + "krisajenkins/remotedata": "6.1.0", + "mdgriffith/elm-ui": "1.1.8", + "ryan-haskell/date-format": "1.0.0" }, "indirect": { "elm/file": "1.0.5", - "elm/regex": "1.0.0", - "elm/time": "1.0.0", "elm/virtual-dom": "1.0.3", + "justinmimbs/date": "4.1.0", "rtfeldman/elm-hex": "1.0.0" } }, diff --git a/lib/index.js b/lib/index.js index 4ecb27b..b8c31e7 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,11 +1,23 @@ import "@vanillawc/wc-codemirror"; import guida from "guida"; +import { createFs } from "indexeddb-fs"; import { Elm } from "../tmp/elm.js"; -(async () => { - const guidaApp = await guida.init({ +const fs = createFs({ databaseName: "guida-fs" }); + +const config = { + env: { GUIDA_REGISTRY: "https://guida-package-registry.fly.dev" - }); + }, + writeFile: fs.writeFile, + readFile: fs.readFile, + details: fs.details, + createDirectory: fs.createDirectory, + readDirectory: fs.readDirectory +}; + +(async () => { + await fs.createDirectory("root/src"); const theme = localStorage.getItem("theme"); const app = Elm.Main.init({ flags: { theme, year: new Date().getFullYear() } }); @@ -26,14 +38,37 @@ import { Elm } from "../tmp/elm.js"; } }); + app.ports.setupProject.subscribe(async ({ direct, indirect, content }) => { + const directDependencies = direct.map((d) => `"${d.author}/${d.project}": "${d.version}"`).join(", "); + const indirectDependencies = indirect.map((d) => `"${d.author}/${d.project}": "${d.version}"`).join(", "); + + fs.writeFile("root/elm.json", `{ + "type": "application", + "source-directories": [ "src" ], + "elm-version": "0.19.1", + "dependencies": { + "direct": { ${directDependencies} }, + "indirect": { ${indirectDependencies} } + }, + "test-dependencies": { "direct": {}, "indirect": {} } +}`); + + if (content) { + await setEditorContentAndRebuild(content); + } + }); + const setEditorContentAndRebuild = async (content) => { if (content) { document.getElementById("editor").value = content; } - const result = await guidaApp.make(content || document.getElementById("editor").value, { + const path = "root/src/Main.guida"; + await fs.writeFile(path, content || document.getElementById("editor").value); + + const result = await guida.make(config, path, { debug: false, - optimize: true, + optimize: false, sourcemaps: false }); @@ -48,4 +83,18 @@ import { Elm } from "../tmp/elm.js"; app.ports.rebuild.subscribe(async () => { await setEditorContentAndRebuild(null); }); + + app.ports.install.subscribe(async (info) => { + await guida.install(config, info.packageString); + const elmJsonContent = await fs.readFile("root/elm.json"); + const result = JSON.parse(elmJsonContent); + app.ports.installResult.send({ ...info, result }); + }); + + app.ports.uninstall.subscribe(async (info) => { + await guida.uninstall(config, info.packageString); + const elmJsonContent = await fs.readFile("root/elm.json"); + const result = JSON.parse(elmJsonContent); + app.ports.uninstallResult.send({ ...info, result }); + }); })(); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ffd28ab..f492356 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "esbuild": "0.25.2", "guida": "^1.0.0-alpha", "http-server": "^14.1.1", + "indexeddb-fs": "^2.1.5", "tailwindcss": "^4.0.7" }, "devDependencies": { diff --git a/package.json b/package.json index c11f817..1370c51 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "esbuild": "0.25.2", "guida": "^1.0.0-alpha", "http-server": "^14.1.1", + "indexeddb-fs": "^2.1.5", "tailwindcss": "^4.0.7" }, "devDependencies": { @@ -30,4 +31,4 @@ "globals": "^16.0.0", "npm-run-all": "^4.1.5" } -} \ No newline at end of file +} diff --git a/public/assets/public-opinion.txt b/public/assets/public-opinion.txt new file mode 100644 index 0000000..0ef09ed --- /dev/null +++ b/public/assets/public-opinion.txt @@ -0,0 +1,11405 @@ +The Project Gutenberg EBook of Public Opinion, by Walter Lippmann + +This eBook is for the use of anyone anywhere in the United States and most +other parts of the world at no cost and with almost no restrictions +whatsoever. You may copy it, give it away or re-use it under the terms of +the Project Gutenberg License included with this eBook or online at +www.gutenberg.org. If you are not located in the United States, you'll have +to check the laws of the country where you are located before using this ebook. + + +Title: Public Opinion + +Author: Walter Lippmann + +Posting Date: October 3, 2014 [EBook #6456] +Release Date: September, 2004 +[This file was first posted on December 15, 2002] + +Language: English + + +*** START OF THIS PROJECT GUTENBERG EBOOK PUBLIC OPINION *** + + + + +Produced by David Phillips, Charles Franks and the Online +Distributed Proofreading Team. + + + + + + + + +PUBLIC OPINION + +BY + +WALTER LIPPMANN + + +TO +FAYE LIPPMANN + +Wading River, +Long Island. +1921. + +_"Behold! human beings living in a sort of underground den, +which has a mouth open towards the light and reaching all across +the den; they have been here from their childhood, and have their +legs and necks chained so that they cannot move, and can only +see before them; for the chains are arranged in such a manner as +to prevent them from turning round their heads. At a distance +above and behind them the light of a fire is blazing, and between +the fire and the prisoners there is a raised way; and you will +see, if you look, a low wall built along the way, like the screen +which marionette players have before them, over which they show +the puppets. + +I see, he said. + +And do you see, I said, men passing along the wall carrying +vessels, which appear over the wall; also figures of men and +animals, made of wood and stone and various materials; and some +of the prisoners, as you would expect, are talking, and some of +them are silent? + +This is a strange image, he said, and they are strange prisoners. + +Like ourselves, I replied; and they see only their own shadows, +or the shadows of one another, which the fire throws on the +opposite wall of the cave? + +True, he said: how could they see anything but the shadows if +they were never allowed to move their heads? + +And of the objects which are being carried in like manner they +would see only the shadows? + +Yes, he said. + +And if they were able to talk with one another, would they not +suppose that they were naming what was actually before them?"_ +--The Republic of Plato, Book Seven. (Jowett Translation.) + + +CONTENTS + +PART I. INTRODUCTION + +I. The World Outside and the Pictures in Our Heads + +PART II. APPROACHES TO THE WORLD OUTSIDE + +II. Censorship and Privacy + +III. Contact and Opportunity + +IV. Time and Attention + +V. Speed, Words, and Clearness + +PART III. STEREOTYPES + +VI. Stereotypes + +VII. Stereotypes as Defense + +VIII. Blind Spots and Their Value + +IX. Codes and Their Enemies + +X. The Detection of Stereotypes + +PART IV. INTERESTS + +XI. The Enlisting of Interest + +XII. Self-Interest Reconsidered + +PART V. THE MAKING OF A COMMON WILL + +XIII. The Transfer of Interest + +XIV. Yes or No + +XV. Leaders and the Rank and File + +PART VI. THE IMAGE OF DEMOCRACY + +XVI. The Self-Centered Man + +XVII. The Self-Contained Community + +XVIII. The Role of Force, Patronage, and Privilege + +XIX. The Old Image in a New Form: Guild Socialism + +XX. A New Image + +PART VII. NEWSPAPERS + +XXI. The Buying Public + +XXII. The Constant Reader + +XXIII. The Nature of News + +XXIV. News, Truth, and a Conclusion + +PART VIII. ORGANIZED INTELLIGENCE + +XXV. The Entering Wedge + +XXVI. Intelligence Work + +XXVII. The Appeal to the Public + +XXVIII. The Appeal to Reason + + + + +PART I + +INTRODUCTION + +CHAPTER I + +THE WORLD OUTSIDE AND THE PICTURES +IN OUR HEADS + + + + +CHAPTER I. INTRODUCTION + +THE WORLD OUTSIDE AND THE PICTURES +IN OUR HEADS + +There is an island in the ocean where in 1914 a few Englishmen, +Frenchmen, and Germans lived. No cable reaches that island, and the +British mail steamer comes but once in sixty days. In September it had +not yet come, and the islanders were still talking about the latest +newspaper which told about the approaching trial of Madame Caillaux +for the shooting of Gaston Calmette. It was, therefore, with more than +usual eagerness that the whole colony assembled at the quay on a day +in mid-September to hear from the captain what the verdict had been. +They learned that for over six weeks now those of them who were +English and those of them who were French had been fighting in behalf +of the sanctity of treaties against those of them who were Germans. +For six strange weeks they had acted as if they were friends, when in +fact they were enemies. + +But their plight was not so different from that of most of the +population of Europe. They had been mistaken for six weeks, on the +continent the interval may have been only six days or six hours. There +was an interval. There was a moment when the picture of Europe on +which men were conducting their business as usual, did not in any way +correspond to the Europe which was about to make a jumble of their +lives. There was a time for each man when he was still adjusted to an +environment that no longer existed. All over the world as late as July +25th men were making goods that they would not be able to ship, buying +goods they would not be able to import, careers were being planned, +enterprises contemplated, hopes and expectations entertained, all in +the belief that the world as known was the world as it was. Men were +writing books describing that world. They trusted the picture in their +heads. And then over four years later, on a Thursday morning, came the +news of an armistice, and people gave vent to their unutterable relief +that the slaughter was over. Yet in the five days before the real +armistice came, though the end of the war had been celebrated, several +thousand young men died on the battlefields. + +Looking back we can see how indirectly we know the environment in +which nevertheless we live. We can see that the news of it comes to us +now fast, now slowly; but that whatever we believe to be a true +picture, we treat as if it were the environment itself. It is harder +to remember that about the beliefs upon which we are now acting, but +in respect to other peoples and other ages we flatter ourselves that +it is easy to see when they were in deadly earnest about ludicrous +pictures of the world. We insist, because of our superior hindsight, +that the world as they needed to know it, and the world as they did +know it, were often two quite contradictory things. We can see, too, +that while they governed and fought, traded and reformed in the world +as they imagined it to be, they produced results, or failed to produce +any, in the world as it was. They started for the Indies and found +America. They diagnosed evil and hanged old women. They thought they +could grow rich by always selling and never buying. A caliph, obeying +what he conceived to be the Will of Allah, burned the library at +Alexandria. + +Writing about the year 389, St. Ambrose stated the case for the +prisoner in Plato's cave who resolutely declines to turn his head. "To +discuss the nature and position of the earth does not help us in our +hope of the life to come. It is enough to know what Scripture states. +'That He hung up the earth upon nothing' (Job xxvi. 7). Why then argue +whether He hung it up in air or upon the water, and raise a +controversy as to how the thin air could sustain the earth; or why, if +upon the waters, the earth does not go crashing down to the bottom?... +Not because the earth is in the middle, as if suspended on even +balance, but because the majesty of God constrains it by the law of +His will, does it endure stable upon the unstable and the void." +[Footnote: Hexaemeron, i. cap 6, quoted in _The Mediæval Mind_, +by Henry Osborn Taylor, Vol. i, p. 73.] + +It does not help us in our hope of the life to come. It is enough to +know what Scripture states. Why then argue? But a century and a half +after St. Ambrose, opinion was still troubled, on this occasion by the +problem of the antipodes. A monk named Cosmas, famous for his +scientific attainments, was therefore deputed to write a Christian +Topography, or "Christian Opinion concerning the World." [Footnote: +Lecky, _Rationalism in Europe_, Vol. I, pp. 276-8.] It is clear +that he knew exactly what was expected of him, for he based all his +conclusions on the Scriptures as he read them. It appears, then, that +the world is a flat parallelogram, twice as broad from east to west as +it is long from north to south., In the center is the earth surrounded +by ocean, which is in turn surrounded by another earth, where men +lived before the deluge. This other earth was Noah's port of +embarkation. In the north is a high conical mountain around which +revolve the sun and moon. When the sun is behind the mountain it is +night. The sky is glued to the edges of the outer earth. It consists +of four high walls which meet in a concave roof, so that the earth is +the floor of the universe. There is an ocean on the other side of the +sky, constituting the "waters that are above the firmament." The space +between the celestial ocean and the ultimate roof of the universe +belongs to the blest. The space between the earth and sky is inhabited +by the angels. Finally, since St. Paul said that all men are made to +live upon the "face of the earth" how could they live on the back +where the Antipodes are supposed to be? With such a passage before +his eyes, a Christian, we are told, should not 'even speak of the +Antipodes.'" [Footnote: _Id._] + +Far less should he go to the Antipodes; nor should any Christian +prince give him a ship to try; nor would any pious mariner wish to +try. For Cosmas there was nothing in the least absurd about his map. +Only by remembering his absolute conviction that this was the map of +the universe can we begin to understand how he would have dreaded +Magellan or Peary or the aviator who risked a collision with the +angels and the vault of heaven by flying seven miles up in the air. In +the same way we can best understand the furies of war and politics by +remembering that almost the whole of each party believes absolutely in +its picture of the opposition, that it takes as fact, not what is, but +what it supposes to be the fact. And that therefore, like Hamlet, it +will stab Polonius behind the rustling curtain, thinking him the king, +and perhaps like Hamlet add: + + "Thou wretched, rash, intruding fool, farewell! + I took thee for thy better; take thy fortune." + +2 + +Great men, even during their lifetime, are usually known to the public +only through a fictitious personality. Hence the modicum of truth in +the old saying that no man is a hero to his valet. There is only a +modicum of truth, for the valet, and the private secretary, are often +immersed in the fiction themselves. Royal personages are, of course, +constructed personalities. Whether they themselves believe in their +public character, or whether they merely permit the chamberlain to +stage-manage it, there are at least two distinct selves, the public +and regal self, the private and human. The biographies of great people +fall more or less readily into the histories of these two selves. The +official biographer reproduces the public life, the revealing memoir +the other. The Charnwood Lincoln, for example, is a noble portrait, +not of an actual human being, but of an epic figure, replete with +significance, who moves on much the same level of reality as Aeneas or +St. George. Oliver's Hamilton is a majestic abstraction, the sculpture +of an idea, "an essay" as Mr. Oliver himself calls it, "on American +union." It is a formal monument to the state-craft of federalism, +hardly the biography of a person. Sometimes people create their own +facade when they think they are revealing the interior scene. The +Repington diaries and Margot Asquith's are a species of +self-portraiture in which the intimate detail is most revealing as an +index of how the authors like to think about themselves. + +But the most interesting kind of portraiture is that which arises +spontaneously in people's minds. When Victoria came to the throne, +says Mr. Strachey, [Footnote: Lytton Strachey, _Queen Victoria_, +p. 72.] "among the outside public there was a great wave of +enthusiasm. Sentiment and romance were coming into fashion; and the +spectacle of the little girl-queen, innocent, modest, with fair hair +and pink cheeks, driving through her capital, filled the hearts of the +beholders with raptures of affectionate loyalty. What, above all, +struck everybody with overwhelming force was the contrast between +Queen Victoria and her uncles. The nasty old men, debauched and +selfish, pigheaded and ridiculous, with their perpetual burden of +debts, confusions, and disreputabilities--they had vanished like the +snows of winter and here at last, crowned and radiant, was the +spring." + +M. Jean de Pierrefeu [Footnote: Jean de Pierrefeu, _G. Q. G. Trois +ans au Grand Quartier General_, pp 94-95.] saw hero-worship at +first hand, for he was an officer on Joffre's staff at the moment of +that soldier's greatest fame: + +"For two years, the entire world paid an almost divine homage to the +victor of the Marne. The baggage-master literally bent under the +weight of the boxes, of the packages and letters which unknown people +sent him with a frantic testimonial of their admiration. I think that +outside of General Joffre, no commander in the war has been able to +realize a comparable idea of what glory is. They sent him boxes of +candy from all the great confectioners of the world, boxes of +champagne, fine wines of every vintage, fruits, game, ornaments and +utensils, clothes, smoking materials, inkstands, paperweights. Every +territory sent its specialty. The painter sent his picture, the +sculptor his statuette, the dear old lady a comforter or socks, the +shepherd in his hut carved a pipe for his sake. All the manufacturers +of the world who were hostile to Germany shipped their products, +Havana its cigars, Portugal its port wine. I have known a hairdresser +who had nothing better to do than to make a portrait of the General +out of hair belonging to persons who were dear to him; a professional +penman had the same idea, but the features were composed of thousands +of little phrases in tiny characters which sang the praise of the +General. As to letters, he had them in all scripts, from all +countries, written in every dialect, affectionate letters, grateful, +overflowing with love, filled with adoration. They called him Savior +of the World, Father of his Country, Agent of God, Benefactor of +Humanity, etc.... And not only Frenchmen, but Americans, Argentinians, +Australians, etc. etc.... Thousands of little children, without their +parents' knowledge, took pen in hand and wrote to tell him their love: +most of them called him Our Father. And there was poignancy about +their effusions, their adoration, these sighs of deliverance that +escaped from thousands of hearts at the defeat of barbarism. To all +these naif little souls, Joffre seemed like St. George crushing the +dragon. Certainly he incarnated for the conscience of mankind the +victory of good over evil, of light over darkness. + +Lunatics, simpletons, the half-crazy and the crazy turned their +darkened brains toward him as toward reason itself. I have read the +letter of a person living in Sydney, who begged the General to save +him from his enemies; another, a New Zealander, requested him to send +some soldiers to the house of a gentleman who owed him ten pounds and +would not pay. + +Finally, some hundreds of young girls, overcoming the timidity of +their sex, asked for engagements, their families not to know about it; +others wished only to serve him." + +This ideal Joffre was compounded out of the victory won by him, his +staff and his troops, the despair of the war, the personal sorrows, +and the hope of future victory. But beside hero-worship there is the +exorcism of devils. By the same mechanism through which heroes are +incarnated, devils are made. If everything good was to come from +Joffre, Foch, Wilson, or Roosevelt, everything evil originated in the +Kaiser Wilhelm, Lenin and Trotsky. They were as omnipotent for evil as +the heroes were omnipotent for good. To many simple and frightened +minds there was no political reverse, no strike, no obstruction, no +mysterious death or mysterious conflagration anywhere in the world of +which the causes did not wind back to these personal sources of evil. + +3 + +Worldwide concentration of this kind on a symbolic personality is rare +enough to be clearly remarkable, and every author has a weakness for +the striking and irrefutable example. The vivisection of war reveals +such examples, but it does not make them out of nothing. In a more +normal public life, symbolic pictures are no less governant of +behavior, but each symbol is far less inclusive because there are so +many competing ones. Not only is each symbol charged with less feeling +because at most it represents only a part of the population, but even +within that part there is infinitely less suppression of individual +difference. The symbols of public opinion, in times of moderate +security, are subject to check and comparison and argument. They come +and go, coalesce and are forgotten, never organizing perfectly the +emotion of the whole group. There is, after all, just one human +activity left in which whole populations accomplish the union sacrée. +It occurs in those middle phases of a war when fear, pugnacity, and +hatred have secured complete dominion of the spirit, either to crush +every other instinct or to enlist it, and before weariness is felt. + +At almost all other times, and even in war when it is deadlocked, a +sufficiently greater range of feelings is aroused to establish +conflict, choice, hesitation, and compromise. The symbolism of public +opinion usually bears, as we shall see, [Footnote: Part V.] the marks +of this balancing of interest. Think, for example, of how rapidly, +after the armistice, the precarious and by no means successfully +established symbol of Allied Unity disappeared, how it was followed +almost immediately by the breakdown of each nation's symbolic picture +of the other: Britain the Defender of Public Law, France watching at +the Frontier of Freedom, America the Crusader. And think then of how +within each nation the symbolic picture of itself frayed out, as party +and class conflict and personal ambition began to stir postponed +issues. And then of how the symbolic pictures of the leaders gave way, +as one by one, Wilson, Clemenceau, Lloyd George, ceased to be the +incarnation of human hope, and became merely the negotiators and +administrators for a disillusioned world. + +Whether we regret this as one of the soft evils of peace or applaud it +as a return to sanity is obviously no matter here. Our first concern +with fictions and symbols is to forget their value to the existing +social order, and to think of them simply as an important part of the +machinery of human communication. Now in any society that is not +completely self-contained in its interests and so small that everyone +can know all about everything that happens, ideas deal with events +that are out of sight and hard to grasp. Miss Sherwin of Gopher +Prairie, [Footnote: See Sinclair Lewis, _Main Street_.] is aware +that a war is raging in France and tries to conceive it. She has never +been to France, and certainly she has never been along what is now the +battlefront. + +Pictures of French and German soldiers she has seen, but it is +impossible for her to imagine three million men. No one, in fact, can +imagine them, and the professionals do not try. They think of them as, +say, two hundred divisions. But Miss Sherwin has no access to the +order of battle maps, and so if she is to think about the war, she +fastens upon Joffre and the Kaiser as if they were engaged in a +personal duel. Perhaps if you could see what she sees with her mind's +eye, the image in its composition might be not unlike an Eighteenth +Century engraving of a great soldier. He stands there boldly unruffled +and more than life size, with a shadowy army of tiny little figures +winding off into the landscape behind. Nor it seems are great men +oblivious to these expectations. M. de Pierrefeu tells of a +photographer's visit to Joffre. The General was in his "middle class +office, before the worktable without papers, where he sat down to +write his signature. Suddenly it was noticed that there were no maps +on the walls. But since according to popular ideas it is not possible +to think of a general without maps, a few were placed in position for +the picture, and removed soon afterwards." [Footnote: _Op. cit._, +p. 99.] + +The only feeling that anyone can have about an event he does not +experience is the feeling aroused by his mental image of that event. +That is why until we know what others think they know, we cannot truly +understand their acts. I have seen a young girl, brought up in a +Pennsylvania mining town, plunged suddenly from entire cheerfulness +into a paroxysm of grief when a gust of wind cracked the kitchen +window-pane. For hours she was inconsolable, and to me incomprehensible. +But when she was able to talk, it transpired that if a window-pane +broke it meant that a close relative had died. She was, therefore, +mourning for her father, who had frightened her into running away +from home. The father was, of course, quite thoroughly alive as a +telegraphic inquiry soon proved. But until the telegram came, the +cracked glass was an authentic message to that girl. Why it was +authentic only a prolonged investigation by a skilled psychiatrist +could show. But even the most casual observer could see that the girl, +enormously upset by her family troubles, had hallucinated a complete +fiction out of one external fact, a remembered superstition, and a +turmoil of remorse, and fear and love for her father. + +Abnormality in these instances is only a matter of degree. When an +Attorney-General, who has been frightened by a bomb exploded on his +doorstep, convinces himself by the reading of revolutionary literature +that a revolution is to happen on the first of May 1920, we recognize +that much the same mechanism is at work. The war, of course, furnished +many examples of this pattern: the casual fact, the creative +imagination, the will to believe, and out of these three elements, a +counterfeit of reality to which there was a violent instinctive +response. For it is clear enough that under certain conditions men +respond as powerfully to fictions as they do to realities, and that in +many cases they help to create the very fictions to which they +respond. Let him cast the first stone who did not believe in the +Russian army that passed through England in August, 1914, did not +accept any tale of atrocities without direct proof, and never saw a +plot, a traitor, or a spy where there was none. Let him cast a stone +who never passed on as the real inside truth what he had heard someone +say who knew no more than he did. + +In all these instances we must note particularly one common factor. It +is the insertion between man and his environment of a pseudo-environment. +To that pseudo-environment his behavior is a response. But because it +_is_ behavior, the consequences, if they are acts, operate not in +the pseudo-environment where the behavior is stimulated, but in the +real environment where action eventuates. If the behavior is not a +practical act, but what we call roughly thought and emotion, it may +be a long time before there is any noticeable break in the texture of +the fictitious world. But when the stimulus of the pseudo-fact results +in action on things or other people, contradiction soon develops. +Then comes the sensation of butting one's head against a stone wall, +of learning by experience, and witnessing Herbert Spencer's tragedy +of the murder of a Beautiful Theory by a Gang of Brutal Facts, the +discomfort in short of a maladjustment. For certainly, at the level of +social life, what is called the adjustment of man to his environment +takes place through the medium of fictions. + +By fictions I do not mean lies. I mean a representation of the +environment which is in lesser or greater degree made by man himself. +The range of fiction extends all the way from complete hallucination +to the scientists' perfectly self-conscious use of a schematic model, +or his decision that for his particular problem accuracy beyond a +certain number of decimal places is not important. A work of fiction +may have almost any degree of fidelity, and so long as the degree of +fidelity can be taken into account, fiction is not misleading. In +fact, human culture is very largely the selection, the rearrangement, +the tracing of patterns upon, and the stylizing of, what William James +called "the random irradiations and resettlements of our +ideas." [Footnote: James, _Principles of Psychology_, Vol. II, p. +638] The alternative to the use of fictions is direct exposure to the +ebb and flow of sensation. That is not a real alternative, for however +refreshing it is to see at times with a perfectly innocent eye, +innocence itself is not wisdom, though a source and corrective of +wisdom. For the real environment is altogether too big, too complex, +and too fleeting for direct acquaintance. We are not equipped to deal +with so much subtlety, so much variety, so many permutations and +combinations. And although we have to act in that environment, we have +to reconstruct it on a simpler model before we can manage with it. To +traverse the world men must have maps of the world. Their persistent +difficulty is to secure maps on which their own need, or someone +else's need, has not sketched in the coast of Bohemia. + +4 + +The analyst of public opinion must begin then, by recognizing the +triangular relationship between the scene of action, the human picture +of that scene, and the human response to that picture working itself +out upon the scene of action. It is like a play suggested to the +actors by their own experience, in which the plot is transacted in the +real lives of the actors, and not merely in their stage parts. The +moving picture often emphasizes with great skill this double drama of +interior motive and external behavior. Two men are quarreling, +ostensibly about some money, but their passion is inexplicable. Then +the picture fades out and what one or the other of the two men sees +with his mind's eye is reënacted. Across the table they were +quarreling about money. In memory they are back in their youth when +the girl jilted him for the other man. The exterior drama is +explained: the hero is not greedy; the hero is in love. + +A scene not so different was played in the United States Senate. At +breakfast on the morning of September 29, 1919, some of the Senators +read a news dispatch in the _Washington Post_ about the landing +of American marines on the Dalmatian coast. The newspaper said: + +FACTS NOW ESTABLISHED + +"The following important facts appear already _established_. The +orders to Rear Admiral Andrews commanding the American naval forces in +the Adriatic, came from the British Admiralty via the War Council and +Rear Admiral Knapps in London. The approval or disapproval of the +American Navy Department was not asked.... + +WITHOUT DANIELS' KNOWLEDGE + +"Mr. Daniels was admittedly placed in a peculiar position when cables +reached here stating that the forces over which he is presumed to have +exclusive control were carrying on what amounted to naval warfare +without his knowledge. It was fully realized that the _British +Admiralty might desire to issue orders to Rear Admiral Andrews_ to +act on behalf of Great Britain and her Allies, because the situation +required sacrifice on the part of some nation if D'Annunzio's +followers were to be held in check. + +"It was further realized that _under the new league of nations plan +foreigners would be in a position to direct American Naval forces in +emergencies_ with or without the consent of the American Navy +Department...." etc. (Italics mine). + +The first Senator to comment is Mr. Knox of Pennsylvania. Indignantly +he demands investigation. In Mr. Brandegee of Connecticut, who spoke +next, indignation has already stimulated credulity. Where Mr. Knox +indignantly wishes to know if the report is true, Mr. Brandegee, a +half a minute later, would like to know what would have happened if +marines had been killed. Mr. Knox, interested in the question, forgets +that he asked for an inquiry, and replies. If American marines had +been killed, it would be war. The mood of the debate is still +conditional. Debate proceeds. Mr. McCormick of Illinois reminds the +Senate that the Wilson administration is prone to the waging of small +unauthorized wars. He repeats Theodore Roosevelt's quip about "waging +peace." More debate. Mr. Brandegee notes that the marines acted "under +orders of a Supreme Council sitting somewhere," but he cannot recall +who represents the United States on that body. The Supreme Council is +unknown to the Constitution of the United States. Therefore Mr. New of +Indiana submits a resolution calling for the facts. + +So far the Senators still recognize vaguely that they are discussing a +rumor. Being lawyers they still remember some of the forms of +evidence. But as red-blooded men they already experience all the +indignation which is appropriate to the fact that American marines +have been ordered into war by a foreign government and without the +consent of Congress. Emotionally they want to believe it, because they +are Republicans fighting the League of Nations. This arouses the +Democratic leader, Mr. Hitchcock of Nebraska. He defends the Supreme +Council: it was acting under the war powers. Peace has not yet been +concluded because the Republicans are delaying it. Therefore the +action was necessary and legal. Both sides now assume that the report +is true, and the conclusions they draw are the conclusions of their +partisanship. Yet this extraordinary assumption is in a debate over a +resolution to investigate the truth of the assumption. It reveals how +difficult it is, even for trained lawyers, to suspend response until +the returns are in. The response is instantaneous. The fiction is +taken for truth because the fiction is badly needed. + +A few days later an official report showed that the marines were not +landed by order of the British Government or of the Supreme Council. +They had not been fighting the Italians. They had been landed at the +request of the Italian Government to protect Italians, and the +American commander had been officially thanked by the Italian +authorities. The marines were not at war with Italy. They had acted +according to an established international practice which had nothing +to do with the League of Nations. + +The scene of action was the Adriatic. The picture of that scene in the +Senators' heads at Washington was furnished, in this case probably +with intent to deceive, by a man who cared nothing about the Adriatic, +but much about defeating the League. To this picture the Senate +responded by a strengthening of its partisan differences over the +League. + +5 + +Whether in this particular case the Senate was above or below its +normal standard, it is not necessary to decide. Nor whether the Senate +compares favorably with the House, or with other parliaments. At the +moment, I should like to think only about the world-wide spectacle of +men acting upon their environment, moved by stimuli from their +pseudo-environments. For when full allowance has been made for +deliberate fraud, political science has still to account for such +facts as two nations attacking one another, each convinced that it is +acting in self-defense, or two classes at war each certain that it +speaks for the common interest. They live, we are likely to say, in +different worlds. More accurately, they live in the same world, but +they think and feel in different ones. + +It is to these special worlds, it is to these private or group, or +class, or provincial, or occupational, or national, or sectarian +artifacts, that the political adjustment of mankind in the Great +Society takes place. Their variety and complication are impossible to +describe. Yet these fictions determine a very great part of men's +political behavior. We must think of perhaps fifty sovereign +parliaments consisting of at least a hundred legislative bodies. With +them belong at least fifty hierarchies of provincial and municipal +assemblies, which with their executive, administrative and legislative +organs, constitute formal authority on earth. But that does not begin +to reveal the complexity of political life. For in each of these +innumerable centers of authority there are parties, and these parties +are themselves hierarchies with their roots in classes, sections, +cliques and clans; and within these are the individual politicians, +each the personal center of a web of connection and memory and fear +and hope. + +Somehow or other, for reasons often necessarily obscure, as the result +of domination or compromise or a logroll, there emerge from these +political bodies commands, which set armies in motion or make peace, +conscript life, tax, exile, imprison, protect property or confiscate +it, encourage one kind of enterprise and discourage another, +facilitate immigration or obstruct it, improve communication or censor +it, establish schools, build navies, proclaim "policies," and +"destiny," raise economic barriers, make property or unmake it, bring +one people under the rule of another, or favor one class as against +another. For each of these decisions some view of the facts is taken +to be conclusive, some view of the circumstances is accepted as the +basis of inference and as the stimulus of feeling. What view of the +facts, and why that one? + +And yet even this does not begin to exhaust the real complexity. The +formal political structure exists in a social environment, where there +are innumerable large and small corporations and institutions, +voluntary and semi-voluntary associations, national, provincial, urban +and neighborhood groupings, which often as not make the decision that +the political body registers. On what are these decisions based? + +"Modern society," says Mr. Chesterton, "is intrinsically insecure +because it is based on the notion that all men will do the same thing +for different reasons.... And as within the head of any convict may be +the hell of a quite solitary crime, so in the house or under the hat +of any suburban clerk may be the limbo of a quite separate philosophy. +The first man may be a complete Materialist and feel his own body as a +horrible machine manufacturing his own mind. He may listen to his +thoughts as to the dull ticking of a clock. The man next door may be a +Christian Scientist and regard his own body as somehow rather less +substantial than his own shadow. He may come almost to regard his own +arms and legs as delusions like moving serpents in the dream of +delirium tremens. The third man in the street may not be a Christian +Scientist but, on the contrary, a Christian. He may live in a fairy +tale as his neighbors would say; a secret but solid fairy tale full of +the faces and presences of unearthly friends. The fourth man may be a +theosophist, and only too probably a vegetarian; and I do not see why +I should not gratify myself with the fancy that the fifth man is a +devil worshiper.... Now whether or not this sort of variety is +valuable, this sort of unity is shaky. To expect that all men for all +time will go on thinking different things, and yet doing the same +things, is a doubtful speculation. It is not founding society on a +communion, or even on a convention, but rather on a coincidence. Four +men may meet under the same lamp post; one to paint it pea green as +part of a great municipal reform; one to read his breviary in the +light of it; one to embrace it with accidental ardour in a fit of +alcoholic enthusiasm; and the last merely because the pea green post +is a conspicuous point of rendezvous with his young lady. But to +expect this to happen night after night is unwise...." [Footnote: G. +K. Chesterton, "The Mad Hatter and the Sane Householder," _Vanity +Fair_, January, 1921, p. 54] + +For the four men at the lamp post substitute the governments, the +parties, the corporations, the societies, the social sets, the trades +and professions, universities, sects, and nationalities of the world. +Think of the legislator voting a statute that will affect distant +peoples, a statesman coming to a decision. Think of the Peace +Conference reconstituting the frontiers of Europe, an ambassador in a +foreign country trying to discern the intentions of his own government +and of the foreign government, a promoter working a concession in a +backward country, an editor demanding a war, a clergyman calling on +the police to regulate amusement, a club lounging-room making up its +mind about a strike, a sewing circle preparing to regulate the +schools, nine judges deciding whether a legislature in Oregon may fix +the working hours of women, a cabinet meeting to decide on the +recognition of a government, a party convention choosing a candidate +and writing a platform, twenty-seven million voters casting their +ballots, an Irishman in Cork thinking about an Irishman in Belfast, a +Third International planning to reconstruct the whole of human +society, a board of directors confronted with a set of their +employees' demands, a boy choosing a career, a merchant estimating +supply and demand for the coming season, a speculator predicting the +course of the market, a banker deciding whether to put credit behind a +new enterprise, the advertiser, the reader of advertisments.... Think +of the different sorts of Americans thinking about their notions of +"The British Empire" or "France" or "Russia" or "Mexico." It is not so +different from Mr. Chesterton's four men at the pea green lamp post. + +6 + +And so before we involve ourselves in the jungle of obscurities about +the innate differences of men, we shall do well to fix our attention +upon the extraordinary differences in what men know of the world. +[Footnote: _Cf_. Wallas, _Our Social Heritage_, pp. 77 _et seq_.] +I do not doubt that there are important biological differences. Since +man is an animal it would be strange if there were not. But as +rational beings it is worse than shallow to generalize at all +about comparative behavior until there is a measurable similarity +between the environments to which behavior is a response. + +The pragmatic value of this idea is that it introduces a much needed +refinement into the ancient controversy about nature and nurture, +innate quality and environment. For the pseudo-environment is a hybrid +compounded of "human nature" and "conditions." To my mind it shows the +uselessness of pontificating about what man is and always will be from +what we observe man to be doing, or about what are the necessary +conditions of society. For we do not know how men would behave in +response to the facts of the Great Society. All that we really know is +how they behave in response to what can fairly be called a most +inadequate picture of the Great Society. No conclusion about man or +the Great Society can honestly be made on evidence like that. + +This, then, will be the clue to our inquiry. We shall assume that what +each man does is based not on direct and certain knowledge, but on +pictures made by himself or given to him. If his atlas tells him that +the world is flat he will not sail near what he believes to be the +edge of our planet for fear of falling off. If his maps include a +fountain of eternal youth, a Ponce de Leon will go in quest of it. If +someone digs up yellow dirt that looks like gold, he will for a time +act exactly as if he had found gold. The way in which the world is +imagined determines at any particular moment what men will do. It does +not determine what they will achieve. It determines their effort, +their feelings, their hopes, not their accomplishments and results. +The very men who most loudly proclaim their "materialism" and their +contempt for "ideologues," the Marxian communists, place their entire +hope on what? On the formation by propaganda of a class-conscious +group. But what is propaganda, if not the effort to alter the picture +to which men respond, to substitute one social pattern for another? +What is class consciousness but a way of realizing the world? National +consciousness but another way? And Professor Giddings' consciousness +of kind, but a process of believing that we recognize among the +multitude certain ones marked as our kind? + +Try to explain social life as the pursuit of pleasure and the +avoidance of pain. You will soon be saying that the hedonist begs the +question, for even supposing that man does pursue these ends, the +crucial problem of why he thinks one course rather than another likely +to produce pleasure, is untouched. Does the guidance of man's +conscience explain? How then does he happen to have the particular +conscience which he has? The theory of economic self-interest? But how +do men come to conceive their interest in one way rather than another? +The desire for security, or prestige, or domination, or what is +vaguely called self-realization? How do men conceive their security, +what do they consider prestige, how do they figure out the means of +domination, or what is the notion of self which they wish to realize? +Pleasure, pain, conscience, acquisition, protection, enhancement, +mastery, are undoubtedly names for some of the ways people act. There +may be instinctive dispositions which work toward such ends. But no +statement of the end, or any description of the tendencies to seek it, +can explain the behavior which results. The very fact that men +theorize at all is proof that their pseudo-environments, their +interior representations of the world, are a determining element in +thought, feeling, and action. For if the connection between reality +and human response were direct and immediate, rather than indirect and +inferred, indecision and failure would be unknown, and (if each of us +fitted as snugly into the world as the child in the womb), Mr. Bernard +Shaw would not have been able to say that except for the first nine +months of its existence no human being manages its affairs as well as +a plant. + +The chief difficulty in adapting the psychoanalytic scheme to +political thought arises in this connection. The Freudians are +concerned with the maladjustment of distinct individuals to other +individuals and to concrete circumstances. They have assumed that if +internal derangements could be straightened out, there would be little +or no confusion about what is the obviously normal relationship. But +public opinion deals with indirect, unseen, and puzzling facts, and +there is nothing obvious about them. The situations to which public +opinions refer are known only as opinions. The psychoanalyst, on the +other hand, almost always assumes that the environment is knowable, +and if not knowable then at least bearable, to any unclouded +intelligence. This assumption of his is the problem of public opinion. +Instead of taking for granted an environment that is readily known, +the social analyst is most concerned in studying how the larger +political environment is conceived, and how it can be conceived more +successfully. The psychoanalyst examines the adjustment to an X, +called by him the environment; the social analyst examines the X, +called by him the pseudo-environment. + +He is, of course, permanently and constantly in debt to the new +psychology, not only because when rightly applied it so greatly helps +people to stand on their own feet, come what may, but because the +study of dreams, fantasy and rationalization has thrown light on how +the pseudo-environment is put together. But he cannot assume as his +criterion either what is called a "normal biological career" +[Footnote: Edward J. Kempf, _Psychopathology_, p. 116.] within +the existing social order, or a career "freed from religious +suppression and dogmatic conventions" outside. [Footnote: _Id_., +p. 151.] What for a sociologist is a normal social career? Or one +freed from suppressions and conventions? Conservative critics do, to +be sure, assume the first, and romantic ones the second. But in +assuming them they are taking the whole world for granted. They are +saying in effect either that society is the sort of thing which +corresponds to their idea of what is normal, or the sort of thing +which corresponds to their idea of what is free. Both ideas are merely +public opinions, and while the psychoanalyst as physician may perhaps +assume them, the sociologist may not take the products of existing +public opinion as criteria by which to study public opinion. + +7 + +The world that we have to deal with politically is out of reach, out +of sight, out of mind. It has to be explored, reported, and imagined. +Man is no Aristotelian god contemplating all existence at one glance. +He is the creature of an evolution who can just about span a +sufficient portion of reality to manage his survival, and snatch what +on the scale of time are but a few moments of insight and happiness. +Yet this same creature has invented ways of seeing what no naked eye +could see, of hearing what no ear could hear, of weighing immense +masses and infinitesimal ones, of counting and separating more items +than he can individually remember. He is learning to see with his mind +vast portions of the world that he could never see, touch, smell, +hear, or remember. Gradually he makes for himself a trustworthy +picture inside his head of the world beyond his reach. + +Those features of the world outside which have to do with the behavior +of other human beings, in so far as that behavior crosses ours, is +dependent upon us, or is interesting to us, we call roughly public +affairs. The pictures inside the heads of these human beings, the +pictures of themselves, of others, of their needs, purposes, and +relationship, are their public opinions. Those pictures which are +acted upon by groups of people, or by individuals acting in the name +of groups, are Public Opinion with capital letters. And so in the +chapters which follow we shall inquire first into some of the reasons +why the picture inside so often misleads men in their dealings with +the world outside. Under this heading we shall consider first the +chief factors which limit their access to the facts. They are the +artificial censorships, the limitations of social contact, the +comparatively meager time available in each day for paying attention +to public affairs, the distortion arising because events have to be +compressed into very short messages, the difficulty of making a small +vocabulary express a complicated world, and finally the fear of facing +those facts which would seem to threaten the established routine of +men's lives. + +The analysis then turns from these more or less external limitations +to the question of how this trickle of messages from the outside is +affected by the stored up images, the preconceptions, and prejudices +which interpret, fill them out, and in their turn powerfully direct +the play of our attention, and our vision itself. From this it +proceeds to examine how in the individual person the limited messages +from outside, formed into a pattern of stereotypes, are identified +with his own interests as he feels and conceives them. In the +succeeding sections it examines how opinions are crystallized into +what is called Public Opinion, how a National Will, a Group Mind, a +Social Purpose, or whatever you choose to call it, is formed. + +The first five parts constitute the descriptive section of the book. +There follows an analysis of the traditional democratic theory of +public opinion. The substance of the argument is that democracy in its +original form never seriously faced the problem which arises because +the pictures inside people's heads do not automatically correspond +with the world outside. And then, because the democratic theory is +under criticism by socialist thinkers, there follows an examination of +the most advanced and coherent of these criticisms, as made by the +English Guild Socialists. My purpose here is to find out whether these +reformers take into account the main difficulties of public opinion. +My conclusion is that they ignore the difficulties, as completely as +did the original democrats, because they, too, assume, and in a much +more complicated civilization, that somehow mysteriously there exists +in the hearts of men a knowledge of the world beyond their reach. + +I argue that representative government, either in what is ordinarily +called politics, or in industry, cannot be worked successfully, no +matter what the basis of election, unless there is an independent, +expert organization for making the unseen facts intelligible to those +who have to make the decisions. I attempt, therefore, to argue that +the serious acceptance of the principle that personal representation +must be supplemented by representation of the unseen facts would alone +permit a satisfactory decentralization, and allow us to escape from +the intolerable and unworkable fiction that each of us must acquire a +competent opinion about all public affairs. It is argued that the +problem of the press is confused because the critics and the +apologists expect the press to realize this fiction, expect it to make +up for all that was not foreseen in the theory of democracy, and that +the readers expect this miracle to be performed at no cost or trouble +to themselves. The newspapers are regarded by democrats as a panacea +for their own defects, whereas analysis of the nature of news and of +the economic basis of journalism seems to show that the newspapers +necessarily and inevitably reflect, and therefore, in greater or +lesser measure, intensify, the defective organization of public +opinion. My conclusion is that public opinions must be organized for +the press if they are to be sound, not by the press as is the case +today. This organization I conceive to be in the first instance the +task of a political science that has won its proper place as +formulator, in advance of real decision, instead of apologist, critic, +or reporter after the decision has been made. I try to indicate that +the perplexities of government and industry are conspiring to give +political science this enormous opportunity to enrich itself and to +serve the public. And, of course, I hope that these pages will help a +few people to realize that opportunity more vividly, and therefore to +pursue it more consciously. + + + + +PART II + +APPROACHES TO THE WORLD OUTSIDE + + +CHAPTER 2. CENSORSHIP AND PRIVACY + " 3. CONTACT AND OPPORTUNITY + " 4. TIME AND ATTENTION + " 5. SPEED, WORDS, AND CLEARNESS + + + + +CHAPTER II + +CENSORSHIP AND PRIVACY + +1 + +The picture of a general presiding over an editorial conference at the +most terrible hour of one of the great battles of history seems more +like a scene from The Chocolate Soldier than a page from life. Yet we +know at first hand from the officer who edited the French communiqués +that these conferences were a regular part of the business of war; +that in the worst moment of Verdun, General Joffre and his cabinet met +and argued over the nouns, adjectives, and verbs that were to be +printed in the newspapers the next morning. + +"The evening communiqué of the twenty-third (February 1916)" says M. +de Pierrefeu, [Footnote: _G. Q. G_., pp. 126-129.] "was edited in +a dramatic atmosphere. M. Berthelot, of the Prime Minister's office, +had just telephoned by order of the minister asking General Pellé to +strengthen the report and to emphasize the proportions of the enemy's +attack. It was necessary to prepare the public for the worst outcome +in case the affair turned into a catastrophe. This anxiety showed +clearly that neither at G. H. Q. nor at the Ministry of War had the +Government found reason for confidence. As M. Berthelot spoke, General +Pellé made notes. He handed me the paper on which he had written the +Government's wishes, together with the order of the day issued by +General von Deimling and found on some prisoners, in which it was +stated that this attack was the supreme offensive to secure peace. +Skilfully used, all this was to demonstrate that Germany was letting +loose a gigantic effort, an effort without precedent, and that from +its success she hoped for the end of the war. The logic of this was +that nobody need be surprised at our withdrawal. When, a half hour +later, I went down with my manuscript, I found gathered together in +Colonel Claudel's office, he being away, the major-general, General +Janin, Colonel Dupont, and Lieutenant-Colonel Renouard. Fearing that I +would not succeed in giving the desired impression, General Pellé had +himself prepared a proposed communiqué. I read what I had just done. +It was found to be too moderate. General Pellé's, on the other hand, +seemed too alarming. I had purposely omitted von Deimling's order of +the day. To put it into the communiqué _would be to break with the +formula to which the public was accustomed_, would be to transform +it into a kind of pleading. It would seem to say: 'How do you suppose +we can resist?' There was reason to fear that the public would be +distracted by this change of tone and would believe that everything +was lost. I explained my reasons and suggested giving Deimling's text +to the newspapers in the form of a separate note. + +"Opinion being divided, General Pellé went to ask General de Castelnau +to come and decide finally. The General arrived smiling, quiet and +good humored, said a few pleasant words about this new kind of +literary council of war, and looked at the texts. He chose the simpler +one, gave more weight to the first phrase, inserted the words 'as had +been anticipated,' which supply a reassuring quality, and was flatly +against inserting von Deimling's order, but was for transmitting it to +the press in a special note ... " General Joffre that evening read the +communiqué carefully and approved it. + +Within a few hours those two or three hundred words would be read all +over the world. They would paint a picture in men's minds of what was +happening on the slopes of Verdun, and in front of that picture people +would take heart or despair. The shopkeeper in Brest, the peasant in +Lorraine, the deputy in the Palais Bourbon, the editor in Amsterdam or +Minneapolis had to be kept in hope, and yet prepared to accept +possible defeat without yielding to panic. They are told, therefore, +that the loss of ground is no surprise to the French Command. They are +taught to regard the affair as serious, but not strange. Now, as a +matter of fact, the French General Staff was not fully prepared for +the German offensive. Supporting trenches had not been dug, +alternative roads had not been built, barbed wire was lacking. But to +confess that would have aroused images in the heads of civilians that +might well have turned a reverse into a disaster. The High Command +could be disappointed, and yet pull itself together; the people at +home and abroad, full of uncertainties, and with none of the +professional man's singleness of purpose, might on the basis of a +complete story have lost sight of the war in a melee of faction and +counter-faction about the competence of the officers. Instead, +therefore, of letting the public act on all the facts which the +generals knew, the authorities presented only certain facts, and these +only in such a way as would be most likely to steady the people. + +In this case the men who arranged the pseudo-environment knew what the +real one was. But a few days later an incident occurred about which +the French Staff did not know the truth. The Germans announced +[Footnote: On February 26, 1916. Pierrefeu, _G. Q. G._, pp. 133 +_et seq_.] that on the previous afternoon they had taken Fort +Douaumont by assault. At French headquarters in Chantilly no one +could understand this news. For on the morning of the twenty-fifth, +after the engagement of the XXth corps, the battle had taken a turn +for the better. Reports from the front said nothing about Douaumont. +But inquiry showed that the German report was true, though no one as +yet knew how the fort had been taken. In the meantime, the German +communiqué was being flashed around the world, and the French had to +say something. So headquarters explained. "In the midst of total +ignorance at Chantilly about the way the attack had taken place, we +imagined, in the evening communiqué of the 26th, a plan of the attack +which certainly had a thousand to one chance of being true." The +communiqué of this imaginary battle read: + +"A bitter struggle is taking place around Fort de Douaumont which is +an advanced post of the old defensive organization of Verdun. The +position taken this morning by the enemy, _after several +unsuccessful assaults that cost him very heavy losses_, has been +reached again and passed by our troops whom the enemy has not been +able to drive back." [Footnote: This is my own translation: the +English translation from London published in the New York Times of +Sunday, Feb. 27, is as follows: + +London, Feb. 26 (1916). A furious struggle has been in progress around +Fort de Douaumont which is an advance element of the old defensive +organization of Verdun fortresses. The position captured this morning +by the enemy after several fruitless assaults which cost him extremely +heavy losses, [Footnote: The French text says "pertes tres elevees." +Thus the English translation exaggerates the original text.] was +reached again and gone beyond by our troops, which all the attempts of +the enemy have not been able to push back."] + +What had actually happened differed from both the French and German +accounts. While changing troops in the line, the position had somehow +been forgotten in a confusion of orders. Only a battery commander and +a few men remained in the fort. Some German soldiers, seeing the door +open, had crawled into the fort, and taken everyone inside prisoner. A +little later the French who were on the slopes of the hill were +horrified at being shot at from the fort. There had been no battle at +Douaumont and no losses. Nor had the French troops advanced beyond it +as the communiqués seemed to say. They were beyond it on either side, +to be sure, but the fort was in enemy hands. + +Yet from the communiqué everyone believed that the fort was half +surrounded. The words did not explicitly say so, but "the press, as +usual, forced the pace." Military writers concluded that the Germans +would soon have to surrender. In a few days they began to ask +themselves why the garrison, since it lacked food, had not yet +surrendered. "It was necessary through the press bureau to request +them to drop the encirclement theme." [Footnote: Pierrefeu, _op. +cit._, pp. 134-5.] + +2 + +The editor of the French communiqué tells us that as the battle +dragged out, his colleagues and he set out to neutralize the +pertinacity of the Germans by continual insistence on their terrible +losses. It is necessary to remember that at this time, and in fact +until late in 1917, the orthodox view of the war for all the Allied +peoples was that it would be decided by "attrition." Nobody believed +in a war of movement. It was insisted that strategy did not count, or +diplomacy. It was simply a matter of killing Germans. The general +public more or less believed the dogma, but it had constantly to be +reminded of it in face of spectacular German successes. + +"Almost no day passed but the communiqué.... ascribed to the Germans +with some appearance of justice heavy losses, extremely heavy, spoke +of bloody sacrifices, heaps of corpses, hecatombs. Likewise the +wireless constantly used the statistics of the intelligence bureau at +Verdun, whose chief, Major Cointet, had invented a method of +calculating German losses which obviously produced marvelous results. +Every fortnight the figures increased a hundred thousand or so. These +300,000, 400,000, 500,000 casualties put out, divided into daily, +weekly, monthly losses, repeated in all sorts of ways, produced a +striking effect. Our formulae varied little: 'according to prisoners +the German losses in the course of the attack have been considerable' ... +'it is proved that the losses' ... 'the enemy exhausted by his losses +has not renewed the attack' ... Certain formulae, later abandoned +because they had been overworked, were used each day: 'under +our artillery and machine gun fire' ... 'mowed down by our artillery +and machine gun fire' ... Constant repetition impressed the neutrals +and Germany itself, and helped to create a bloody background in spite +of the denials from Nauen (the German wireless) which tried vainly to +destroy the bad effect of this perpetual repetition." [Footnote: _Op. +cit._, pp. 138-139.] + +The thesis of the French Command, which it wished to establish +publicly by these reports, was formulated as follows for the guidance +of the censors: + +"This offensive engages the active forces of our opponent whose +manpower is declining. We have learned that the class of 1916 is +already at the front. There will remain the 1917 class already being +called up, and the resources of the third category (men above +forty-five, or convalescents). In a few weeks, the German forces +exhausted by this effort, will find themselves confronted with all the +forces of the coalition (ten millions against seven millions)." +[Footnote: _Op. cit._, p. 147.] + +According to M. de Pierrefeu, the French command had converted itself +to this belief. "By an extraordinary aberration of mind, only the +attrition of the enemy was seen; it appeared that our forces were not +subject to attrition. General Nivelle shared these ideas. We saw the +result in 1917." + +We have learned to call this propaganda. A group of men, who can +prevent independent access to the event, arrange the news of it to +suit their purpose. That the purpose was in this case patriotic does +not affect the argument at all. They used their power to make the +Allied publics see affairs as they desired them to be seen. The +casualty figures of Major Cointet which were spread about the world +are of the same order. They were intended to provoke a particular kind +of inference, namely that the war of attrition was going in favor of +the French. But the inference is not drawn in the form of argument. It +results almost automatically from the creation of a mental picture of +endless Germans slaughtered on the hills about Verdun. By putting the +dead Germans in the focus of the picture, and by omitting to mention +the French dead, a very special view of the battle was built up. It +was a view designed to neutralize the effects of German territorial +advances and the impression of power which the persistence of the +offensive was making. It was also a view that tended to make the +public acquiesce in the demoralizing defensive strategy imposed upon +the Allied armies. For the public, accustomed to the idea that war +consists of great strategic movements, flank attacks, encirclements, +and dramatic surrenders, had gradually to forget that picture in favor +of the terrible idea that by matching lives the war would be won. +Through its control over all news from the front, the General Staff +substituted a view of the facts that comported with this strategy. + +The General Staff of an army in the field is so placed that within +wide limits it can control what the public will perceive. It controls +the selection of correspondents who go to the front, controls their +movements at the front, reads and censors their messages from the +front, and operates the wires. The Government behind the army by its +command of cables and passports, mails and custom houses and blockades +increases the control. It emphasizes it by legal power over +publishers, over public meetings, and by its secret service. But in +the case of an army the control is far from perfect. There is always +the enemy's communiqué, which in these days of wireless cannot be kept +away from neutrals. Above all there is the talk of the soldiers, which +blows back from the front, and is spread about when they are on +leave. [Footnote: For weeks prior to the American attack at St. Mihiel +and in the Argonne-Meuse, everybody in France told everybody else the +deep secret.] An army is an unwieldy thing. And that is why the naval +and diplomatic censorship is almost always much more complete. Fewer +people know what is going on, and their acts are more easily +supervised. + +3 + +Without some form of censorship, propaganda in the strict sense of the +word is impossible. In order to conduct a propaganda there must be +some barrier between the public and the event. Access to the real +environment must be limited, before anyone can create a +pseudo-environment that he thinks wise or desirable. For while people +who have direct access can misconceive what they see, no one else can +decide how they shall misconceive it, unless he can decide where they +shall look, and at what. The military censorship is the simplest form +of barrier, but by no means the most important, because it is known to +exist, and is therefore in certain measure agreed to and discounted. + +At different times and for different subjects some men impose and +other men accept a particular standard of secrecy. The frontier +between what is concealed because publication is not, as we say, +"compatible with the public interest" fades gradually into what is +concealed because it is believed to be none of the public's business. +The notion of what constitutes a person's private affairs is elastic. +Thus the amount of a man's fortune is considered a private affair, and +careful provision is made in the income tax law to keep it as private +as possible. The sale of a piece of land is not private, but the price +may be. Salaries are generally treated as more private than wages, +incomes as more private than inheritances. A person's credit rating is +given only a limited circulation. The profits of big corporations are +more public than those of small firms. Certain kinds of conversation, +between man and wife, lawyer and client, doctor and patient, priest +and communicant, are privileged. Directors' meetings are generally +private. So are many political conferences. Most of what is said at a +cabinet meeting, or by an ambassador to the Secretary of State, or at +private interviews, or dinner tables, is private. Many people regard +the contract between employer and employee as private. There was a +time when the affairs of all corporations were held to be as private +as a man's theology is to-day. There was a time before that when his +theology was held to be as public a matter as the color of his eyes. +But infectious diseases, on the other hand, were once as private as +the processes of a man's digestion. The history of the notion of +privacy would be an entertaining tale. Sometimes the notions violently +conflict, as they did when the bolsheviks published the secret +treaties, or when Mr. Hughes investigated the life insurance +companies, or when somebody's scandal exudes from the pages of Town +Topics to the front pages of Mr. Hearst's newspapers. + +Whether the reasons for privacy are good or bad, the barriers exist. +Privacy is insisted upon at all kinds of places in the area of what is +called public affairs. It is often very illuminating, therefore, to +ask yourself how you got at the facts on which you base your opinion. +Who actually saw, heard, felt, counted, named the thing, about which +you have an opinion? Was it the man who told you, or the man who told +him, or someone still further removed? And how much was he permitted +to see? When he informs you that France thinks this and that, what +part of France did he watch? How was he able to watch it? Where was he +when he watched it? What Frenchmen was he permitted to talk to, what +newspapers did he read, and where did they learn what they say? You +can ask yourself these questions, but you can rarely answer them. They +will remind you, however, of the distance which often separates your +public opinion from the event with which it deals. And the reminder is +itself a protection. + + + + +CHAPTER III + +CONTACT AND OPPORTUNITY + +1 + +While censorship and privacy intercept much information at its source, +a very much larger body of fact never reaches the whole public at all, +or only very slowly. For there are very distinct limits upon the +circulation of ideas. + +A rough estimate of the effort it takes to reach "everybody" can be +had by considering the Government's propaganda during the war. +Remembering that the war had run over two years and a half before +America entered it, that millions upon millions of printed pages had +been circulated and untold speeches had been delivered, let us turn to +Mr. Creel's account of his fight "for the minds of men, for the +conquest of their convictions" in order that "the gospel of +Americanism might be carried to every corner of the globe." +[Footnote: George Creel, _How We Advertised America._] + +Mr. Creel had to assemble machinery which included a Division of News +that issued, he tells us, more than six thousand releases, had to +enlist seventy-five thousand Four Minute Men who delivered at least +seven hundred and fifty-five thousand, one hundred and ninety speeches +to an aggregate of over three hundred million people. Boy scouts +delivered annotated copies of President Wilson's addresses to the +householders of America. Fortnightly periodicals were sent to six +hundred thousand teachers. Two hundred thousand lantern slides were +furnished for illustrated lectures. Fourteen hundred and thirty-eight +different designs were turned out for posters, window cards, newspaper +advertisements, cartoons, seals and buttons. The chambers of commerce, +the churches, fraternal societies, schools, were used as channels of +distribution. Yet Mr. Creel's effort, to which I have not begun to do +justice, did not include Mr. McAdoo's stupendous organization for the +Liberty Loans, nor Mr. Hoover's far reaching propaganda about food, +nor the campaigns of the Red Cross, the Y. M. C. A., Salvation Army, +Knights of Columbus, Jewish Welfare Board, not to mention the +independent work of patriotic societies, like the League to Enforce +Peace, the League of Free Nations Association, the National Security +League, nor the activity of the publicity bureaus of the Allies and of +the submerged nationalities. + +Probably this is the largest and the most intensive effort to carry +quickly a fairly uniform set of ideas to all the people of a nation. +The older proselyting worked more slowly, perhaps more surely, but +never so inclusively. Now if it required such extreme measures to +reach everybody in time of crisis, how open are the more normal +channels to men's minds? The Administration was trying, and while the +war continued it very largely succeeded, I believe, in creating +something that might almost be called one public opinion all over +America. But think of the dogged work, the complicated ingenuity, the +money and the personnel that were required. Nothing like that exists +in time of peace, and as a corollary there are whole sections, there +are vast groups, ghettoes, enclaves and classes that hear only vaguely +about much that is going on. + +They live in grooves, are shut in among their own affairs, barred out +of larger affairs, meet few people not of their own sort, read little. +Travel and trade, the mails, the wires, and radio, railroads, +highways, ships, motor cars, and in the coming generation aeroplanes, +are, of course, of the utmost influence on the circulation of ideas. +Each of these affects the supply and the quality of information and +opinion in a most intricate way. Each is itself affected by technical, +by economic, by political conditions. Every time a government relaxes +the passport ceremonies or the customs inspection, every time a new +railway or a new port is opened, a new shipping line established, +every time rates go up or down, the mails move faster or more slowly, +the cables are uncensored and made less expensive, highways built, or +widened, or improved, the circulation of ideas is influenced. Tariff +schedules and subsidies affect the direction of commercial enterprise, +and therefore the nature of human contracts. And so it may well +happen, as it did for example in the case of Salem, Massachusetts, +that a change in the art of shipbuilding will reduce a whole city from +a center where international influences converge to a genteel +provincial town. All the immediate effects of more rapid transit are +not necessarily good. It would be difficult to say, for example, that +the railroad system of France, so highly centralized upon Paris, has +been an unmixed blessing to the French people. + +It is certainly true that problems arising out of the means of +communication are of the utmost importance, and one of the most +constructive features of the program of the League of Nations has been +the study given to railroad transit and access to the sea. The +monopolizing of cables, [Footnote: Hence the wisdom of taking Yap +seriously.] of ports, fuel stations, mountain passes, canals, straits, +river courses, terminals, market places means a good deal more than +the enrichment of a group of business men, or the prestige of a +government. It means a barrier upon the exchange of news and opinion. +But monopoly is not the only barrier. Cost and available supply are +even greater ones, for if the cost of travelling or trading is +prohibitive, if the demand for facilities exceeds the supply, the +barriers exist even without monopoly. + +2 + +The size of a man's income has considerable effect on his access to +the world beyond his neighborhood. With money he can overcome almost +every tangible obstacle of communication, he can travel, buy books and +periodicals, and bring within the range of his attention almost any +known fact of the world. The income of the individual, and the income +of the community determine the amount of communication that is +possible. But men's ideas determine how that income shall be spent, +and that in turn affects in the long run the amount of income they +will have. Thus also there are limitations, none the less real, +because they are often self-imposed and self-indulgent. + +There are portions of the sovereign people who spend most of their +spare time and spare money on motoring and comparing motor cars, on +bridge-whist and post-mortems, on moving-pictures and potboilers, +talking always to the same people with minute variations on the same +old themes. They cannot really be said to suffer from censorship, or +secrecy, the high cost or the difficulty of communication. They suffer +from anemia, from lack of appetite and curiosity for the human scene. +Theirs is no problem of access to the world outside. Worlds of +interest are waiting for them to explore, and they do not enter. + +They move, as if on a leash, within a fixed radius of acquaintances +according to the law and the gospel of their social set. Among men the +circle of talk in business and at the club and in the smoking car is +wider than the set to which they belong. Among women the social set +and the circle of talk are frequently almost identical. It is in the +social set that ideas derived from reading and lectures and from the +circle of talk converge, are sorted out, accepted, rejected, judged +and sanctioned. There it is finally decided in each phase of a +discussion which authorities and which sources of information are +admissible, and which not. + +Our social set consists of those who figure as people in the phrase +"people are saying"; they are the people whose approval matters most +intimately to us. In big cities among men and women of wide interests +and with the means for moving about, the social set is not so rigidly +defined. But even in big cities, there are quarters and nests of +villages containing self-sufficing social sets. In smaller communities +there may exist a freer circulation, a more genuine fellowship from +after breakfast to before dinner. But few people do not know, +nevertheless, which set they really belong to, and which not. + +Usually the distinguishing mark of a social set is the presumption +that the children may intermarry. To marry outside the set involves, +at the very least, a moment of doubt before the engagement can be +approved. Each social set has a fairly clear picture of its relative +position in the hierarchy of social sets. Between sets at the same +level, association is easy, individuals are quickly accepted, +hospitality is normal and unembarrassed. But in contact between sets +that are "higher" or "lower," there is always reciprocal hesitation, a +faint malaise, and a consciousness of difference. To be sure in a +society like that of the United States, individuals move somewhat +freely out of one set into another, especially where there is no +racial barrier and where economic position changes so rapidly. + +Economic position, however, is not measured by the amount of income. +For in the first generation, at least, it is not income that +determines social standing, but the character of a man's work, and it +may take a generation or two before this fades out of the family +tradition. Thus banking, law, medicine, public utilities, newspapers, +the church, large retailing, brokerage, manufacture, are rated at a +different social value from salesmanship, superintendence, expert +technical work, nursing, school teaching, shop keeping; and those, in +turn, are rated as differently from plumbing, being a chauffeur, +dressmaking, subcontracting, or stenography, as these are from being a +butler, lady's maid, a moving picture operator, or a locomotive +engineer. And yet the financial return does not necessarily coincide +with these gradations. + +3 + +Whatever the tests of admission, the social set when formed is not a +mere economic class, but something which more nearly resembles a +biological clan. Membership is intimately connected with love, +marriage and children, or, to speak more exactly, with the attitudes +and desires that are involved. In the social set, therefore, opinions +encounter the canons of Family Tradition, Respectability, Propriety, +Dignity, Taste and Form, which make up the social set's picture of +itself, a picture assiduously implanted in the children. In this +picture a large space is tacitly given to an authorized version of +what each set is called upon inwardly to accept as the social standing +of the others. The more vulgar press for an outward expression of the +deference due, the others are decently and sensitively silent about +their own knowledge that such deference invisibly exists. But that +knowledge, becoming overt when there is a marriage, a war, or a social +upheaval, is the nexus of a large bundle of dispositions classified by +Trotter [Footnote: W. Trotter, Instincts of the Herd in War and Peace.] +under the general term instinct of the herd. + +Within each social set there are augurs like the van der Luydens and +Mrs. Manson Mingott in "The Age of Innocence," [Footnote: Edith +Wharton, _The Age of Innocence._] who are recognized as the +custodians and the interpreters of its social pattern. You are made, +they say, if the van der Luydens take you up. The invitations to their +functions are the high sign of arrival and status. The elections to +college societies, carefully graded and the gradations universally +accepted, determine who is who in college. The social leaders, +weighted with the ultimate eugenic responsibility, are peculiarly +sensitive. Not only must they be watchfully aware of what makes for +the integrity of their set, but they have to cultivate a special gift +for knowing what other social sets are doing. They act as a kind of +ministry of foreign affairs. Where most of the members of a set live +complacently within the set, regarding it for all practical purposes +as the world, the social leaders must combine an intimate knowledge of +the anatomy of their own set with a persistent sense of its place in +the hierarchy of sets. + +The hierarchy, in fact, is bound together by the social leaders. At +any one level there is something which might almost be called a social +set of the social leaders. But vertically the actual binding together +of society, in so far as it is bound together at all by social +contact, is accomplished by those exceptional people, frequently +suspect, who like Julius Beaufort and Ellen Olenska in "The Age of +Innocence" move in and out. Thus there come to be established personal +channels from one set to another, through which Tarde's laws of +imitation operate. But for large sections of the population there are +no such channels. For them the patented accounts of society and the +moving pictures of high life have to serve. They may develop a social +hierarchy of their own, almost unnoticed, as have the Negroes and the +"foreign element," but among that assimilated mass which always +considers itself the "nation," there is in spite of the great +separateness of sets, a variety of personal contacts through which a +circulation of standards takes place. + +Some of the sets are so placed that they become what Professor Ross +has called "radiant points of conventionality." [Footnote: Ross, +_Social Psychology_, Ch. IX, X, XI.] Thus the social superior is +likely to be imitated by the social inferior, the holder of power is +imitated by subordinates, the more successful by the less successful, +the rich by the poor, the city by the country. But imitation does not +stop at frontiers. The powerful, socially superior, successful, rich, +urban social set is fundamentally international throughout the western +hemisphere, and in many ways London is its center. It counts among its +membership the most influential people in the world, containing as it +does the diplomatic set, high finance, the upper circles of the army +and the navy, some princes of the church, a few great newspaper +proprietors, their wives and mothers and daughters who wield the +scepter of invitation. It is at once a great circle of talk and a real +social set. But its importance comes from the fact that here at last +the distinction between public and private affairs practically +disappears. The private affairs of this set are public matters, and +public matters are its private, often its family affairs. The +confinements of Margot Asquith like the confinements of royalty are, +as the philosophers say, in much the same universe of discourse as a +tariff bill or a parliamentary debate. + +There are large areas of governments in which this social set is not +interested, and in America, at least, it has exercised only a +fluctuating control over the national government. But its power in +foreign affairs is always very great, and in war time its prestige is +enormously enhanced. That is natural enough because these +cosmopolitans have a contact with the outer world that most people do +not possess. They have dined with each other in the capitals, and +their sense of national honor is no mere abstraction; it is a concrete +experience of being snubbed or approved by their friends. To Dr. +Kennicott of Gopher Prairie it matters mighty little what Winston +thinks and a great deal what Ezra Stowbody thinks, but to Mrs. Mingott +with a daughter married to the Earl of Swithin it matters a lot when +she visits her daughter, or entertains Winston himself. Dr. Kennicott +and Mrs. Mingott are both socially sensitive, but Mrs. Mingott is +sensitive to a social set that governs the world, while Dr. +Kennicott's social set governs only in Gopher Prairie. But in matters +that effect the larger relationships of the Great Society, Dr. +Kennicott will often be found holding what he thinks is purely his own +opinion, though, as a matter of fact, it has trickled down to Gopher +Prairie from High Society, transmuted on its passage through the +provincial social sets. + +4 + +It is no part of our inquiry to attempt an account of the social +tissue. We need only fix in mind how big is the part played by the +social set in our spiritual contact with the world, how it tends to +fix what is admissible, and to determine how it shall be judged. +Affairs within its immediate competence each set more or less +determines for itself. Above all it determines the detailed +administration of the judgment. But the judgment itself is formed on +patterns [Footnote: _Cf_. Part III] that may be inherited from +the past, transmitted or imitated from other social sets. The highest +social set consists of those who embody the leadership of the Great +Society. As against almost every other social set where the bulk of +the opinions are first hand only about local affairs, in this Highest +Society the big decisions of war and peace, of social strategy and the +ultimate distribution of political power, are intimate experiences +within a circle of what, potentially at least, are personal +acquaintances. + +Since position and contact play so big a part in determining what can +be seen, heard, read, and experienced, as well as what it is +permissible to see, hear, read, and know, it is no wonder that moral +judgment is so much more common than constructive thought. Yet in +truly effective thinking the prime necessity is to liquidate +judgments, regain an innocent eye, disentangle feelings, be curious +and open-hearted. Man's history being what it is, political opinion on +the scale of the Great Society requires an amount of selfless +equanimity rarely attainable by any one for any length of time. We are +concerned in public affairs, but immersed in our private ones. The +time and attention are limited that we can spare for the labor of not +taking opinions for granted, and we are subject to constant +interruption. + + + + +CHAPTER IV + +TIME AND ATTENTION + +NATURALLY it is possible to make a rough estimate only of the amount +of attention people give each day to informing themselves about public +affairs. Yet it is interesting that three estimates that I have +examined agree tolerably well, though they were made at different +times, in different places, and by different methods. [Footnote: July, +1900. D. F. Wilcox, _The American Newspaper: A Study in Social +Psychology_, Annals of the American Academy of Political and Social +Science, vol. xvi, p. 56. (The statistical tables are reproduced in +James Edward Rogers, _The American Newspaper_.) + +1916 (?) W. D. Scott, _The Psychology of Advertising_, pp. +226-248. See also Henry Foster Adams, _Advertising and its Mental +Laws_, Ch. IV. + +1920 _Newspaper Reading Habits of College Students_, by Prof. +George Burton Hotchkiss and Richard B. Franken, published by the +Association of National Advertisers, Inc., 15 East 26th Street, New +York City.] + +A questionnaire was sent by Hotchkiss and Franken to 1761 men and +women college students in New York City, and answers came from all but +a few. Scott used a questionnaire on four thousand prominent business +and professional men in Chicago and received replies from twenty-three +hundred. Between seventy and seventy-five percent of all those who +replied to either inquiry thought they spent a quarter of an hour a +day reading newspapers. Only four percent of the Chicago group guessed +at less than this and twenty-five percent guessed at more. Among the +New Yorkers a little over eight percent figured their newspaper +reading at less than fifteen minutes, and seventeen and a half at +more. + +Very few people have an accurate idea of fifteen minutes, so the +figures are not to be taken literally. Moreover, business men, +professional people, and college students are most of them liable to a +curious little bias against appearing to spend too much time over the +newspapers, and perhaps also to a faint suspicion of a desire to be +known as rapid readers. All that the figures can justly be taken to +mean is that over three quarters of those in the selected groups rate +rather low the attention they give to printed news of the outer world. + +These time estimates are fairly well confirmed by a test which is less +subjective. Scott asked his Chicagoans how many papers they read each +day, and was told that + + 14 percent read but one paper + 46 " " two papers + 21 " " three papers + 10 " " four papers + 3 " " five papers + 2 " " six papers + 3 " " all the papers (eight + at the time of this inquiry). + +The two- and three-paper readers are sixty-seven percent, which comes +fairly close to the seventy-one percent in Scott's group who rate +themselves at fifteen minutes a day. The omnivorous readers of from +four to eight papers coincide roughly with the twenty-five percent who +rated themselves at more than fifteen minutes. + +2 + +It is still more difficult to guess how the time is distributed. The +college students were asked to name "the five features which interest +you most." Just under twenty percent voted for "general news," just +under fifteen for editorials, just under twelve for "politics," a +little over eight for finance, not two years after the armistice a +little over six for foreign news, three and a half for local, nearly +three for business, and a quarter of one percent for news about +"labor." A scattering said they were most interested in sports, +special articles, the theatre, advertisements, cartoons, book reviews, +"accuracy," music, "ethical tone," society, brevity, art, stories, +shipping, school news, "current news," print. Disregarding these, +about sixty-seven and a half percent picked as the most interesting +features news and opinion that dealt with public affairs. + +This was a mixed college group. The girls professed greater interest +than the boys in general news, foreign news, local news, politics, +editorials, the theatre, music, art, stories, cartoons, +advertisements, and "ethical tone." The boys on the other hand were +more absorbed in finance, sports, business page, "accuracy" and +"brevity." These discriminations correspond a little too closely with +the ideals of what is cultured and moral, manly and decisive, not to +make one suspect the utter objectivity of the replies. + +Yet they agree fairly well with the replies of Scott's Chicago +business and professional men. They were asked, not what features +interested them most, but why they preferred one newspaper to another. +Nearly seventy-one percent based their conscious preference on local +news (17.8%), or political (15.8%) or financial (11.3%), or foreign +(9.5%), or general (7.2%), or editorials (9%). The other thirty +percent decided on grounds not connected with public affairs. They +ranged from not quite seven who decided for ethical tone, down to one +twentieth of one percent who cared most about humor. + +How do these preferences correspond with the space given by newspapers +to various subjects? Unfortunately there are no data collected on this +point for the newspapers read by the Chicago and New York groups at +the time the questionnaires were made. But there is an interesting +analysis made over twenty years ago by Wilcox. He studied one hundred +and ten newspapers in fourteen large cities, and classified the +subject matter of over nine thousand columns. + +Averaged for the whole country the various newspaper matter was found +to fill: + + { (a) War News 17.9 + { { Foreign 1.2 + { (b) General " 21.8 { Politics 6.4 +I. News 55.3 { { Crime 3.1 + { { Misc. 11.1 + { + { { Business 8.2 + { (c) Special " 15.6 { Sport 5.1 + { Society 2.3 + +II. Illustrations 3.1 + +III. Literature 2.4 + { (a) Editorials 3.9 +IV. Opinion 7.1 { (b) Letters & Exchange 3.2 + +V. Advertisements 32.1 + + +In order to bring this table into a fair comparison, it is necessary +to exclude the space given to advertisements, and recompute the +percentages. For the advertisements occupied only an infinitesimal +part of the conscious preference of the Chicago group or the college +group. I think this is justifiable for our purposes because the press +prints what advertisements it can get, [Footnote: Except those which it +regards as objectionable, and those which, in rare instances, are +crowded out.] whereas the rest of the paper is designed to the taste +of its readers. The table would then read: + + {War News 26.4- + { {Foreign 1.8- + I. News 81.4+{General News 32.0+ {Political 9.4+ + { {Crime 4.6- + { {Misc. 16.3+ + { + { {Business 12.1- + {Special " 23.0- {Sporting 7.5+ + {Society 3.3- + II. Illustrations 4.6- +III. Literature 3.5+ + IV. Opinion 10.5- {Editorials 5.8- + {Letters 4.7+ + + +In this revised table if you add up the items which may be supposed to +deal with public affairs, that is to say war, foreign, political, +miscellaneous, business news, and opinion, you find a total of 76.5% +of the edited space devoted in 1900 to the 70.6% of reasons given by +Chicago business men in 1916 for preferring a particular newspaper, +and to the five features which most interested 67.5% of the New York +College students in 1920. + +This would seem to show that the tastes of business men and college +students in big cities to-day still correspond more or less to the +averaged judgments of newspaper editors in big cities twenty years +ago. Since that time the proportion of features to news has +undoubtedly increased, and so has the circulation and the size of +newspapers. Therefore, if to-day you could secure accurate replies +from more typical groups than college students or business and +professional men, you would expect to find a smaller percentage of +time devoted to public affairs, as well as a smaller percentage of +space. On the other hand you would expect to find that the average man +spends more than the quarter of an hour on his newspaper, and that +while the percentage of space given to public affairs is less than +twenty years ago the net amount is greater. + +No elaborate deductions are to be drawn from these figures. They help +merely to make somewhat more concrete our notions of the effort that +goes day by day into acquiring the data of our opinions. The +newspapers are, of course, not the only means, but they are certainly +the principal ones. Magazines, the public forum, the chautauqua, the +church, political gatherings, trade union meetings, women's clubs, and +news serials in the moving picture houses supplement the press. But +taking it all at the most favorable estimate, the time each day is +small when any of us is directly exposed to information from our +unseen environment. + + + + +CHAPTER V + +SPEED, WORDS, AND CLEARNESS + +1 + +The unseen environment is reported to us chiefly by words. These words +are transmitted by wire or radio from the reporters to the editors who +fit them into print. Telegraphy is expensive, and the facilities are +often limited. Press service news is, therefore, usually coded. Thus a +dispatch which reads,-- + +"Washington, D. C. June I.--The United States regards the question of +German shipping seized in this country at the outbreak of hostilities +as a closed incident," + +may pass over the wires in the following form: + +"Washn i. The Uni Stas rgds tq of Ger spg seized in ts cou at t outbk +o hox as a clod incident." [Footnote: Phillip's Code.] + +A news item saying: + +"Berlin, June 1, Chancellor Wirth told the Reichstag to-day in +outlining the Government's program that 'restoration and +reconciliation would be the keynote of the new Government's policy.' +He added that the Cabinet was determined disarmament should be carried +out loyally and that disarmament would not be the occasion of the +imposition of further penalties by the Allies." + +may be cabled in this form: + +"Berlin 1. Chancellor Wirth told t Reichstag tdy in outlining the gvts +pgn tt qn restoration & reconciliation wd b the keynote f new gvts +policy. qj He added ttt cabinet ws dtmd disarmament sd b carried out +loyally & tt disarmament wd n b. the ocan f imposition of further +penalties bi t alis." + +In this second item the substance has been culled from a long speech +in a foreign tongue, translated, coded, and then decoded. The +operators who receive the messages transcribe them as they go along, +and I am told that a good operator can write fifteen thousand or even +more words per eight hour day, with a half an hour out for lunch and +two ten minute periods for rest. + +2 + +A few words must often stand for a whole succession of acts, thoughts, +feelings and consequences. We read: + +"Washington, Dec. 23--A statement charging Japanese military +authorities with deeds more 'frightful and barbarous' than anything +ever alleged to have occurred in Belgium during the war was issued +here to-day by the Korean Commission, based, the Commission said, on +authentic reports received by it from Manchuria." + +Here eyewitnesses, their accuracy unknown, report to the makers of +'authentic reports'; they in turn transmit these to a commission five +thousand miles away. It prepares a statement, probably much too long +for publication, from which a correspondent culls an item of print +three and a half inches long. The meaning has to be telescoped in such +a way as to permit the reader to judge how much weight to give to the +news. + +It is doubtful whether a supreme master of style could pack all the +elements of truth that complete justice would demand into a hundred +word account of what had happened in Korea during the course of +several months. For language is by no means a perfect vehicle of +meanings. Words, like currency, are turned over and over again, to +evoke one set of images to-day, another to-morrow. There is no +certainty whatever that the same word will call out exactly the same +idea in the reader's mind as it did in the reporter's. Theoretically, +if each fact and each relation had a name that was unique, and if +everyone had agreed on the names, it would be possible to communicate +without misunderstanding. In the exact sciences there is an approach +to this ideal, and that is part of the reason why of all forms of +world-wide cooperation, scientific inquiry is the most effective. + +Men command fewer words than they have ideas to express, and language, +as Jean Paul said, is a dictionary of faded metaphors. [Footnote: +Cited by White, _Mechanisms of Character Formation._] The +journalist addressing half a million readers of whom he has only a dim +picture, the speaker whose words are flashed to remote villages and +overseas, cannot hope that a few phrases will carry the whole burden +of their meaning. "The words of Lloyd George, badly understood and +badly transmitted," said M. Briand to the Chamber of Deputies, +[Footnote: Special Cable to _The New York Times,_ May 25, 1921, +by Edwin L, James. ] "seemed to give the Pan-Germanists the idea that +the time had come to start something." A British Prime Minister, +speaking in English to the whole attentive world, speaks his own +meaning in his own words to all kinds of people who will see their +meaning in those words. No matter how rich or subtle--or rather the +more rich and the more subtle that which he has to say, the more his +meaning will suffer as it is sluiced into standard speech and then +distributed again among alien minds. [Footnote: In May of 1921, +relations between England and France were strained by the insurrection +of M. Korfanty in Upper Silesia. The London Correspondence of the +_Manchester Guardian_ (May 20, 1921), contained the following +item: + +"The Franco-English Exchange in Words. + +"In quarters well acquainted with French ways and character I find a +tendency to think that undue sensibility has been shown by our press +and public opinion in the lively and at times intemperate language of +the French press through the present crisis. The point was put to me +by a well-informed neutral observer in the following manner. + +"Words, like money, are tokens of value. They represent meaning, +therefore, and just as money, their representative value goes up and +down. The French word 'etonnant' was used by Bossuet with a terrible +weight of meaning which it has lost to-day. A similar thing can be +observed with the English word 'awful.' Some nations constitutionally +tend to understate, others to overstate. What the British Tommy called +an unhealthy place could only be described by an Italian soldier by +means of a rich vocabulary aided with an exuberant mimicry. Nations +that understate keep their word-currency sound. Nations that overstate +suffer from inflation in their language. + +"Expressions such as 'a distinguished scholar,' 'a clever writer,' +must be translated into French as 'a great savant,' 'an exquisite +master.' It is a mere matter of exchange, just as in France one pound +pays 46 francs, and yet one knows that that does not increase its +value at home. Englishmen reading the French press should endeavour to +work out a mental operation similar to that of the banker who puts +back francs into pounds, and not forget in so doing that while in +normal times the change was 25 it is now 46 on account of the war. For +there is a war fluctuation on word exchanges as well as on money +exchanges. + +"The argument, one hopes, works both ways, and Frenchmen do not fail +to realize that there is as much value behind English reticence as +behind their own exuberance of expression."] + +Millions of those who are watching him can read hardly at all. +Millions more can read the words but cannot understand them. Of those +who can both read and understand, a good three-quarters we may assume +have some part of half an hour a day to spare for the subject. To them +the words so acquired are the cue for a whole train of ideas on which +ultimately a vote of untold consequences may be based. Necessarily the +ideas which we allow the words we read to evoke form the biggest part +of the original data of our opinions. The world is vast, the +situations that concern us are intricate, the messages are few, the +biggest part of opinion must be constructed in the imagination. + +When we use the word "Mexico" what picture does it evoke in a resident +of New York? Likely as not, it is some composite of sand, cactus, oil +wells, greasers, rum-drinking Indians, testy old cavaliers flourishing +whiskers and sovereignty, or perhaps an idyllic peasantry à la Jean +Jacques, assailed by the prospect of smoky industrialism, and fighting +for the Rights of Man. What does the word "Japan" evoke? Is it a vague +horde of slant-eyed yellow men, surrounded by Yellow Perils, picture +brides, fans, Samurai, banzais, art, and cherry blossoms? Or the word +"alien"? According to a group of New England college students, writing +in the year 1920, an alien was the following: [Footnote: _The New +Republic_: December 29, 1920, p. 142. ] + +"A person hostile to this country." +"A person against the government." +"A person who is on the opposite side." +"A native of an unfriendly country." +"A foreigner at war." +"A foreigner who tries to do harm to the country he is in." +"An enemy from a foreign land." +"A person against a country." etc.... + +Yet the word alien is an unusually exact legal term, far more exact +than words like sovereignty, independence, national honor, rights, +defense, aggression, imperialism, capitalism, socialism, about which +we so readily take sides "for" or "against." + +3 + +The power to dissociate superficial analogies, attend to differences +and appreciate variety is lucidity of mind. It is a relative faculty. +Yet the differences in lucidity are extensive, say as between a newly +born infant and a botanist examining a flower. To the infant there is +precious little difference between his own toes, his father's watch, +the lamp on the table, the moon in the sky, and a nice bright yellow +edition of Guy de Maupassant. To many a member of the Union League +Club there is no remarkable difference between a Democrat, a +Socialist, an anarchist, and a burglar, while to a highly +sophisticated anarchist there is a whole universe of difference +between Bakunin, Tolstoi, and Kropotkin. These examples show how +difficult it might be to secure a sound public opinion about de +Maupassant among babies, or about Democrats in the Union League Club. + +A man who merely rides in other people's automobiles may not rise to +finer discrimination than between a Ford, a taxicab, and an +automobile. But let that same man own a car and drive it, let him, as +the psychoanalysts would say, project his libido upon automobiles, and +he will describe a difference in carburetors by looking at the rear +end of a car a city block away. That is why it is often such a relief +when the talk turns from "general topics" to a man's own hobby. It is +like turning from the landscape in the parlor to the ploughed field +outdoors. It is a return to the three dimensional world, after a +sojourn in the painter's portrayal of his own emotional response to +his own inattentive memory of what he imagines he ought to have seen. + +We easily identify, says Ferenczi, two only partially similar things: +[Footnote: Internat. Zeitschr, f. Arztl. Psychoanalyse, 1913. +Translated and republished by Dr. Ernest Jones in S. Ferenczi, +_Contributions to Psychoanalysis_, Ch. VIII, _Stages in the +Development of the Sense of Reality_.] the child more easily than +the adult, the primitive or arrested mind more readily than the +mature. As it first appears in the child, consciousness seems to be an +unmanageable mixture of sensations. The child has no sense of time, +and almost none of space, it reaches for the chandelier with the same +confidence that it reaches for its mother's breast, and at first with +almost the same expectation. Only very gradually does function define +itself. To complete inexperience this is a coherent and +undifferentiated world, in which, as someone has said of a school of +philosophers, all facts are born free and equal. Those facts which +belong together in the world have not yet been separated from those +which happen to lie side by side in the stream of consciousness. + +At first, says Ferenczi, the baby gets some of the things it wants by +crying for them. This is "the period of magical hallucinatory +omnipotence." In its second phase the child points to the things it +wants, and they are given to it. "Omnipotence by the help of magic +gestures." Later, the child learns to talk, asks for what it wishes, +and is partially successful. "The period of magic thoughts and magic +words." Each phase may persist for certain situations, though overlaid +and only visible at times, as for example, in the little harmless +superstitions from which few of us are wholly free. In each phase, +partial success tends to confirm that way of acting, while failure +tends to stimulate the development of another. Many individuals, +parties, and even nations, rarely appear to transcend the magical +organization of experience. But in the more advanced sections of the +most advanced peoples, trial and error after repeated failure has led +to the invention of a new principle. The moon, they learn, is not +moved by baying at it. Crops are not raised from the soil by spring +festivals or Republican majorities, but by sunlight, moisture, seeds, +fertilizer, and cultivation. [Footnote: Ferenczi, being a pathologist, +does not describe this maturer period where experience is organized as +equations, the phase of realism on the basis of science.] + +Allowing for the purely schematic value of Ferenczi's categories of +response, the quality which we note as critical is the power to +discriminate among crude perceptions and vague analogies. This power +has been studied under laboratory conditions. [Footnote: See, for +example, Diagnostische Assoziation Studien, conducted at the +Psychiatric University Clinic in Zurich under the direction of Dr. C. +G. Jung. These tests were carried on principally under the so-called +Krapelin-Aschaffenburg classification. They show reaction time, +classify response to the stimulant word as inner, outer, and clang, +show separate results for the first and second hundred words, for +reaction time and reaction quality when the subject is distracted by +holding an idea in mind, or when he replies while beating time with a +metronome. Some of the results are summarized in Jung, _Analytical +Psychology_, Ch. II, transl. by Dr. Constance E. Long.] The Zurich +Association Studies indicate clearly that slight mental fatigue, an +inner disturbance of attention or an external distraction, tend to +"flatten" the quality of the response. An example of the very "flat" +type is the clang association (cat-hat), a reaction to the sound and +not to the sense of the stimulant word. One test, for example, shows a +9% increase of clang in the second series of a hundred reactions. Now +the clang is almost a repetition, a very primitive form of analogy. + +4 + +If the comparatively simple conditions of a laboratory can so readily +flatten out discrimination, what must be the effect of city life? In +the laboratory the fatigue is slight enough, the distraction rather +trivial. Both are balanced in measure by the subject's interest and +self-consciousness. Yet if the beat of a metronome will depress +intelligence, what do eight or twelve hours of noise, odor, and heat +in a factory, or day upon day among chattering typewriters and +telephone bells and slamming doors, do to the political judgments +formed on the basis of newspapers read in street-cars and subways? Can +anything be heard in the hubbub that does not shriek, or be seen in +the general glare that does not flash like an electric sign? The life +of the city dweller lacks solitude, silence, ease. The nights are +noisy and ablaze. The people of a big city are assaulted by incessant +sound, now violent and jagged, now falling into unfinished rhythms, +but endless and remorseless. Under modern industrialism thought goes +on in a bath of noise. If its discriminations are often flat and +foolish, here at least is some small part of the reason. The sovereign +people determines life and death and happiness under conditions where +experience and experiment alike show thought to be most difficult. +"The intolerable burden of thought" is a burden when the conditions +make it burdensome. It is no burden when the conditions are favorable. +It is as exhilarating to think as it is to dance, and just as natural. + +Every man whose business it is to think knows that he must for part of +the day create about himself a pool of silence. But in that +helter-skelter which we flatter by the name of civilization, the +citizen performs the perilous business of government under the worst +possible conditions. A faint recognition of this truth inspires the +movement for a shorter work day, for longer vacations, for light, air, +order, sunlight and dignity in factories and offices. But if the +intellectual quality of our life is to be improved that is only the +merest beginning. So long as so many jobs are an endless and, for the +worker, an aimless routine, a kind of automatism using one set of +muscles in one monotonous pattern, his whole life will tend towards an +automatism in which nothing is particularly to be distinguished from +anything else unless it is announced with a thunderclap. So long as he +is physically imprisoned in crowds by day and even by night his +attention will flicker and relax. It will not hold fast and define +clearly where he is the victim of all sorts of pother, in a home which +needs to be ventilated of its welter of drudgery, shrieking children, +raucous assertions, indigestible food, bad air, and suffocating +ornament. + +Occasionally perhaps we enter a building which is composed and +spacious; we go to a theatre where modern stagecraft has cut away +distraction, or go to sea, or into a quiet place, and we remember how +cluttered, how capricious, how superfluous and clamorous is the +ordinary urban life of our time. We learn to understand why our addled +minds seize so little with precision, why they are caught up and +tossed about in a kind of tarantella by headlines and catch-words, why +so often they cannot tell things apart or discern identity in apparent +differences. + +5 + +But this external disorder is complicated further by internal. +Experiment shows that the speed, the accuracy, and the intellectual +quality of association is deranged by what we are taught to call +emotional conflicts. Measured in fifths of a second, a series of a +hundred stimuli containing both neutral and hot words may show a +variation as between 5 and 32 or even a total failure to respond at +all. [Footnote: Jung, _Clark Lectures_.] Obviously our public +opinion is in intermittent contact with complexes of all sorts; with +ambition and economic interest, personal animosity, racial prejudice, +class feeling and what not. They distort our reading, our thinking, +our talking and our behavior in a great variety of ways. + +And finally since opinions do not stop at the normal members of +society, since for the purposes of an election, a propaganda, a +following, numbers constitute power, the quality of attention is still +further depressed. The mass of absolutely illiterate, of +feeble-minded, grossly neurotic, undernourished and frustrated +individuals, is very considerable, much more considerable there is +reason to think than we generally suppose. Thus a wide popular appeal +is circulated among persons who are mentally children or barbarians, +people whose lives are a morass of entanglements, people whose +vitality is exhausted, shut-in people, and people whose experience has +comprehended no factor in the problem under discussion. The stream of +public opinion is stopped by them in little eddies of misunderstanding, +where it is discolored with prejudice and far fetched analogy. + +A "broad appeal" takes account of the quality of association, and is +made to those susceptibilities which are widely distributed. A +"narrow" or a "special" appeal is one made to those susceptibilities +which are uncommon. But the same individual may respond with very +different quality to different stimuli, or to the same stimuli at +different times. Human susceptibilities are like an alpine country. +There are isolated peaks, there are extensive but separated plateaus, +and there are deeper strata which are quite continuous for nearly all +mankind. Thus the individuals whose susceptibilities reach the +rarefied atmosphere of those peaks where there exists an exquisitive +difference between Frege and Peano, or between Sassetta's earlier and +later periods, may be good stanch Republicans at another level of +appeal, and when they are starving and afraid, indistinguishable from +any other starving and frightened person. No wonder that the magazines +with the large circulations prefer the face of a pretty girl to any +other trade mark, a face, pretty enough to be alluring, but innocent +enough to be acceptable. For the "psychic level" on which the stimulus +acts determines whether the public is to be potentially a large or a +small one. + +6 + +Thus the environment with which our public opinions deal is refracted +in many ways, by censorship and privacy at the source, by physical and +social barriers at the other end, by scanty attention, by the poverty +of language, by distraction, by unconscious constellations of feeling, +by wear and tear, violence, monotony. These limitations upon our +access to that environment combine with the obscurity and complexity +of the facts themselves to thwart clearness and justice of perception, +to substitute misleading fictions for workable ideas, and to deprive +us of adequate checks upon those who consciously strive to mislead. + + + + +PART III + +STEREOTYPES + +CHAPTER 6. STEREOTYPES + " 7. STEREOTYPES AS DEFENSE + " 8. BLIND SPOTS AND THEIR VALUE + " 9. CODES AND THEIR ENEMIES + " 10. THE DETECTION OF STEREOTYPES + + + + +CHAPTER VI + +STEREOTYPES + +1 + +Each of us lives and works on a small part of the earth's surface, +moves in a small circle, and of these acquaintances knows only a few +intimately. Of any public event that has wide effects we see at best +only a phase and an aspect. This is as true of the eminent insiders +who draft treaties, make laws, and issue orders, as it is of those who +have treaties framed for them, laws promulgated to them, orders given +at them. Inevitably our opinions cover a bigger space, a longer reach +of time, a greater number of things, than we can directly observe. +They have, therefore, to be pieced together out of what others have +reported and what we can imagine. + +Yet even the eyewitness does not bring back a naéve picture of the +scene. [Footnote: _E. g. cf._ Edmond Locard, _L'Enquête Criminelle +et les Méthodes Scientifiques._ A great deal of interesting material has +been gathered in late years on the credibility of the witness, which +shows, as an able reviewer of Dr. Locard's book says in _The +Times_ (London) Literary Supplement (August 18, 1921), that +credibility varies as to classes of witnesses and classes of events, +and also as to type of perception. Thus, perceptions of touch, odor, +and taste have low evidential value. Our hearing is defective and +arbitrary when it judges the source and direction of sound, and in +listening to the talk of other people "words which are not heard will +be supplied by the witness in all good faith. He will have a theory of +the purport of the conversation, and will arrange the sounds he heard +to fit it." Even visual perceptions are liable to great error, as in +identification, recognition, judgment of distance, estimates of +numbers, for example, the size of a crowd. In the untrained observer, +the sense of time is highly variable. All these original weaknesses +are complicated by tricks of memory, and the incessant creative +quality of the imagination. _Cf_. also Sherrington, _The Integrative +Action of the Nervous System_, pp. 318-327. + +The late Professor Hugo Münsterberg wrote a popular book on this +subject called _On the Witness Stand_.] For experience seems to +show that he himself brings something to the scene which later he +takes away from it, that oftener than not what he imagines to be the +account of an event is really a transfiguration of it. Few facts in +consciousness seem to be merely given. Most facts in consciousness +seem to be partly made. A report is the joint product of the knower +and known, in which the role of the observer is always selective and +usually creative. The facts we see depend on where we are placed, and +the habits of our eyes. + +An unfamiliar scene is like the baby's world, "one great, blooming, +buzzing confusion." [Footnote: Wm. James, _Principles of +Psychology_, Vol. I, p. 488.] This is the way, says Mr. John Dewey, +[Footnote: John Dewey, _How We Think_, pg 121.] that any new +thing strikes an adult, so far as the thing is really new and strange. +"Foreign languages that we do not understand always seem jibberings, +babblings, in which it is impossible to fix a definite, clear-cut, +individualized group of sounds. The countryman in the crowded street, +the landlubber at sea, the ignoramus in sport at a contest between +experts in a complicated game, are further instances. Put an +inexperienced man in a factory, and at first the work seems to him a +meaningless medley. All strangers of another race proverbially look +alike to the visiting stranger. Only gross differences of size or +color are perceived by an outsider in a flock of sheep, each of which +is perfectly individualized to the shepherd. A diffusive blur and an +indiscriminately shifting suction characterize what we do not +understand. The problem of the acquisition of meaning by things, or +(stated in another way) of forming habits of simple apprehension, is +thus the problem of introducing (1) _definiteness_ and _distinction_ +and (2) _consistency_ or _stability_ of meaning into what is +otherwise vague and wavering." + +But the kind of definiteness and consistency introduced depends upon +who introduces them. In a later passage [Footnote: _op. cit._, p. +133.] Dewey gives an example of how differently an experienced layman +and a chemist might define the word metal. "Smoothness, hardness, +glossiness, and brilliancy, heavy weight for its size ... the +serviceable properties of capacity for being hammered and pulled +without breaking, of being softened by heat and hardened by cold, of +retaining the shape and form given, of resistance to pressure and +decay, would probably be included" in the layman's definition. But the +chemist would likely as not ignore these esthetic and utilitarian +qualities, and define a metal as "any chemical element that enters +into combination with oxygen so as to form a base." + +For the most part we do not first see, and then define, we define +first and then see. In the great blooming, buzzing confusion of the +outer world we pick out what our culture has already defined for us, +and we tend to perceive that which we have picked out in the form +stereotyped for us by our culture. Of the great men who assembled at +Paris to settle the affairs of mankind, how many were there who were +able to see much of the Europe about them, rather than their +commitments about Europe? Could anyone have penetrated the mind of M. +Clemenceau, would he have found there images of the Europe of 1919, or +a great sediment of stereotyped ideas accumulated and hardened in a +long and pugnacious existence? Did he see the Germans of 1919, or the +German type as he had learned to see it since 1871? He saw the type, +and among the reports that came to him from Germany, he took to heart +those reports, and, it seems, those only, which fitted the type that +was in his mind. If a junker blustered, that was an authentic German; +if a labor leader confessed the guilt of the empire, he was not an +authentic German. + +At a Congress of Psychology in Göttingen an interesting experiment was +made with a crowd of presumably trained observers. [Footnote: A. von +Gennep, _La formation des légendes_, pp. 158-159. Cited F. van +Langenhove, _The Growth of a Legend_, pp. 120-122.] + +"Not far from the hall in which the Congress was sitting there was a +public fete with a masked ball. Suddenly the door of the hall was +thrown open and a clown rushed in madly pursued by a negro, revolver +in hand. They stopped in the middle of the room fighting; the clown +fell, the negro leapt upon him, fired, and then both rushed out of the +hall. The whole incident hardly lasted twenty seconds. + +"The President asked those present to write immediately a report since +there was sure to be a judicial inquiry. Forty reports were sent in. +Only one had less than 20% of mistakes in regard to the principal +facts; fourteen had 20% to 40% of mistakes; twelve from 40% to 50%; +thirteen more than 50%. Moreover in twenty-four accounts 10% of the +details were pure inventions and this proportion was exceeded in ten +accounts and diminished in six. Briefly a quarter of the accounts were +false. + +"It goes without saying that the whole scene had been arranged and +even photographed in advance. The ten false reports may then be +relegated to the category of tales and legends; twenty-four accounts +are half legendary, and six have a value approximating to exact +evidence." + +Thus out of forty trained observers writing a responsible account of a +scene that had just happened before their eyes, more than a majority +saw a scene that had not taken place. What then did they see? One +would suppose it was easier to tell what had occurred, than to invent +something which had not occurred. They saw their stereotype of such a +brawl. All of them had in the course of their lives acquired a series +of images of brawls, and these images flickered before their eyes. In +one man these images displaced less than 20% of the actual scene, in +thirteen men more than half. In thirty-four out of the forty observers +the stereotypes preempted at least one-tenth of the scene. + +A distinguished art critic has said [Footnote: Bernard Berenson, +_The Central Italian Painters of the Renaissance_, pp. 60, _et +seq_.] that "what with the almost numberless shapes assumed by an +object. ... What with our insensitiveness and inattention, things +scarcely would have for us features and outlines so determined and +clear that we could recall them at will, but for the stereotyped +shapes art has lent them." The truth is even broader than that, for +the stereotyped shapes lent to the world come not merely from art, in +the sense of painting and sculpture and literature, but from our moral +codes and our social philosophies and our political agitations as +well. Substitute in the following passage of Mr. Berenson's the words +'politics,' 'business,' and 'society,' for the word 'art' and the +sentences will be no less true: "... unless years devoted to the study +of all schools of art have taught us also to see with our own eyes, we +soon fall into the habit of moulding whatever we look at into the +forms borrowed from the one art with which we are acquainted. There is +our standard of artistic reality. Let anyone give us shapes and colors +which we cannot instantly match in our paltry stock of hackneyed forms +and tints, and we shake our heads at his failure to reproduce things +as we know they certainly are, or we accuse him of insincerity." + +Mr. Berenson speaks of our displeasure when a painter "does not +visualize objects exactly as we do," and of the difficulty of +appreciating the art of the Middle Ages because since then "our manner +of visualizing forms has changed in a thousand ways." [Footnote: +_Cf._ also his comment on _Dante's Visual Images, and his Early +Illustrators_ in _The Study and Criticism of Italian Art_ (First +Series), p. 13. "_We_ cannot help dressing Virgil as a Roman, +and giving him a 'classical profile' and 'statuesque carriage,' but +Dante's visual image of Virgil was probably no less mediaeval, no +more based on a critical reconstruction of antiquity, than his entire +conception of the Roman poet. Fourteenth Century illustrators make +Virgil look like a mediaeval scholar, dressed in cap and gown, and +there is no reason why Dante's visual image of him should have been +other than this."] He goes on to show how in regard to the human +figure we have been taught to see what we do see. "Created by +Donatello and Masaccio, and sanctioned by the Humanists, the new canon +of the human figure, the new cast of features ... presented to the +ruling classes of that time the type of human being most likely to win +the day in the combat of human forces... Who had the power to break +through this new standard of vision and, out of the chaos of things, +to select shapes more definitely expressive of reality than those +fixed by men of genius? No one had such power. People had perforce to +see things in that way and in no other, and to see only the shapes +depicted, to love only the ideals presented...." [Footnote: _The +Central Italian Painters_, pp. 66-67.] + +2 + +If we cannot fully understand the acts of other people, until we know +what they think they know, then in order to do justice we have to +appraise not only the information which has been at their disposal, +but the minds through which they have filtered it. For the accepted +types, the current patterns, the standard versions, intercept +information on its way to consciousness. Americanization, for example, +is superficially at least the substitution of American for European +stereotypes. Thus the peasant who might see his landlord as if he were +the lord of the manor, his employer as he saw the local magnate, is +taught by Americanization to see the landlord and employer according +to American standards. This constitutes a change of mind, which is, in +effect, when the inoculation succeeds, a change of vision. His eye +sees differently. One kindly gentlewoman has confessed that the +stereotypes are of such overweening importance, that when hers are not +indulged, she at least is unable to accept the brotherhood of man and +the fatherhood of God: "we are strangely affected by the clothes we +wear. Garments create a mental and social atmosphere. What can be +hoped for the Americanism of a man who insists on employing a London +tailor? One's very food affects his Americanism. What kind of American +consciousness can grow in the atmosphere of sauerkraut and Limburger +cheese? Or what can you expect of the Americanism of the man whose +breath always reeks of garlic?" [Footnote: Cited by Mr. Edward Hale +Bierstadt, _New Republic_, June 1 1921 p. 21.] + +This lady might well have been the patron of a pageant which a friend +of mine once attended. It was called the Melting Pot, and it was given +on the Fourth of July in an automobile town where many foreign-born +workers are employed. In the center of the baseball park at second +base stood a huge wooden and canvas pot. There were flights of steps +up to the rim on two sides. After the audience had settled itself, and +the band had played, a procession came through an opening at one side +of the field. It was made up of men of all the foreign nationalities +employed in the factories. They wore their native costumes, they were +singing their national songs; they danced their folk dances, and +carried the banners of all Europe. The master of ceremonies was the +principal of the grade school dressed as Uncle Sam. He led them to the +pot. He directed them up the steps to the rim, and inside. He called +them out again on the other side. They came, dressed in derby hats, +coats, pants, vest, stiff collar and polka-dot tie, undoubtedly, said +my friend, each with an Eversharp pencil in his pocket, and all +singing the Star-Spangled Banner. + +To the promoters of this pageant, and probably to most of the actors, +it seemed as if they had managed to express the most intimate +difficulty to friendly association between the older peoples of +America and the newer. The contradiction of their stereotypes +interfered with the full recognition of their common humanity. The +people who change their names know this. They mean to change +themselves, and the attitude of strangers toward them. + +There is, of course, some connection between the scene outside and the +mind through which we watch it, just as there are some long-haired men +and short-haired women in radical gatherings. But to the hurried +observer a slight connection is enough. If there are two bobbed heads +and four beards in the audience, it will be a bobbed and bearded +audience to the reporter who knows beforehand that such gatherings are +composed of people with these tastes in the management of their hair. +There is a connection between our vision and the facts, but it is +often a strange connection. A man has rarely looked at a landscape, +let us say, except to examine its possibilities for division into +building lots, but he has seen a number of landscapes hanging in the +parlor. And from them he has learned to think of a landscape as a rosy +sunset, or as a country road with a church steeple and a silver moon. +One day he goes to the country, and for hours he does not see a single +landscape. Then the sun goes down looking rosy. At once he recognizes +a landscape and exclaims that it is beautiful. But two days later, +when he tries to recall what he saw, the odds are that he will +remember chiefly some landscape in a parlor. + +Unless he has been drunk or dreaming or insane he did see a sunset, +but he saw in it, and above all remembers from it, more of what the +oil painting taught him to observe, than what an impressionist +painter, for example, or a cultivated Japanese would have seen and +taken away with him. And the Japanese and the painter in turn will +have seen and remembered more of the form they had learned, unless +they happen to be the very rare people who find fresh sight for +mankind. In untrained observation we pick recognizable signs out of +the environment. The signs stand for ideas, and these ideas we fill +out with our stock of images. We do not so much see this man and that +sunset; rather we notice that the thing is man or sunset, and then see +chiefly what our mind is already full of on those subjects. + +3 + +There is economy in this. For the attempt to see all things freshly +and in detail, rather than as types and generalities, is exhausting, +and among busy affairs practically out of the question. In a circle of +friends, and in relation to close associates or competitors, there is +no shortcut through, and no substitute for, an individualized +understanding. Those whom we love and admire most are the men and +women whose consciousness is peopled thickly with persons rather than +with types, who know us rather than the classification into which we +might fit. For even without phrasing it to ourselves, we feel +intuitively that all classification is in relation to some purpose not +necessarily our own; that between two human beings no association has +final dignity in which each does not take the other as an end in +himself. There is a taint on any contact between two people which does +not affirm as an axiom the personal inviolability of both. + +But modern life is hurried and multifarious, above all physical +distance separates men who are often in vital contact with each other, +such as employer and employee, official and voter. There is neither +time nor opportunity for intimate acquaintance. Instead we notice a +trait which marks a well known type, and fill in the rest of the +picture by means of the stereotypes we carry about in our heads. He is +an agitator. That much we notice, or are told. Well, an agitator is +this sort of person, and so _he_ is this sort of person. He is an +intellectual. He is a plutocrat. He is a foreigner. He is a "South +European." He is from Back Bay. He is a Harvard Man. How different +from the statement: he is a Yale Man. He is a regular fellow. He is a +West Pointer. He is an old army sergeant. He is a Greenwich Villager: +what don't we know about him then, and about her? He is an +international banker. He is from Main Street. + +The subtlest and most pervasive of all influences ere those which +create and maintain the repertory of stereotypes. We are told about +the world before we see it. We imagine most things before we +experience them. And those preconceptions, unless education has made +us acutely aware, govern deeply the whole process of perception. They +mark out certain objects as familiar or strange, emphasizing the +difference, so that the slightly familiar is seen as very familiar, +and the somewhat strange as sharply alien. They are aroused by small +signs, which may vary from a true index to a vague analogy. Aroused, +they flood fresh vision with older images, and project into the world +what has been resurrected in memory. Were there no practical +uniformities in the environment, there would be no economy and only +error in the human habit of accepting foresight for sight. But there +are uniformities sufficiently accurate, and the need of economizing +attention is so inevitable, that the abandonment of all stereotypes +for a wholly innocent approach to experience would impoverish human +life. + +What matters is the character of the stereotypes, and the gullibility +with which we employ them. And these in the end depend upon those +inclusive patterns which constitute our philosophy of life. If in that +philosophy we assume that the world is codified according to a code +which we possess, we are likely to make our reports of what is going +on describe a world run by our code. But if our philosophy tells us +that each man is only a small part of the world, that his intelligence +catches at best only phases and aspects in a coarse net of ideas, +then, when we use our stereotypes, we tend to know that they are only +stereotypes, to hold them lightly, to modify them gladly. We tend, +also, to realize more and more clearly when our ideas started, where +they started, how they came to us, why we accepted them. All useful +history is antiseptic in this fashion. It enables us to know what +fairy tale, what school book, what tradition, what novel, play, +picture, phrase, planted one preconception in this mind, another in +that mind. + +4 + +Those who wish to censor art do not at least underestimate this +influence. They generally misunderstand it, and almost always they are +absurdly bent on preventing other people from discovering anything not +sanctioned by them. But at any rate, like Plato in his argument about +the poets, they feel vaguely that the types acquired through fiction +tend to be imposed on reality. Thus there can be little doubt that the +moving picture is steadily building up imagery which is then evoked by +the words people read in their newspapers. In the whole experience of +the race there has been no aid to visualization comparable to the +cinema. If a Florentine wished to visualize the saints, he could go to +the frescoes in his church, where he might see a vision of saints +standardized for his time by Giotto. If an Athenian wished to +visualize the gods he went to the temples. But the number of objects +which were pictured was not great. And in the East, where the spirit +of the second commandment was widely accepted, the portraiture of +concrete things was even more meager, and for that reason perhaps the +faculty of practical decision was by so much reduced. In the western +world, however, during the last few centuries there has been an +enormous increase in the volume and scope of secular description, the +word picture, the narrative, the illustrated narrative, and finally +the moving picture and, perhaps, the talking picture. + +Photographs have the kind of authority over imagination to-day, which +the printed word had yesterday, and the spoken word before that. They +seem utterly real. They come, we imagine, directly to us without human +meddling, and they are the most effortless food for the mind +conceivable. Any description in words, or even any inert picture, +requires an effort of memory before a picture exists in the mind. But +on the screen the whole process of observing, describing, reporting, +and then imagining, has been accomplished for you. Without more +trouble than is needed to stay awake the result which your imagination +is always aiming at is reeled off on the screen. The shadowy idea +becomes vivid; your hazy notion, let us say, of the Ku Klux Klan, +thanks to Mr. Griffiths, takes vivid shape when you see the Birth of a +Nation. Historically it may be the wrong shape, morally it may be a +pernicious shape, but it is a shape, and I doubt whether anyone who +has seen the film and does not know more about the Ku Klux Klan than +Mr. Griffiths, will ever hear the name again without seeing those +white horsemen. + +5 + +And so when we speak of the mind of a group of people, of the French +mind, the militarist mind, the bolshevik mind, we are liable to +serious confusion unless we agree to separate the instinctive +equipment from the stereotypes, the patterns, and the formulae which +play so decisive a part in building up the mental world to which the +native character is adapted and responds. Failure to make this +distinction accounts for oceans of loose talk about collective minds, +national souls, and race psychology. To be sure a stereotype may be so +consistently and authoritatively transmitted in each generation from +parent to child that it seems almost like a biological fact. In some +respects, we may indeed have become, as Mr. Wallas says, [Footnote: +Graham Wallas, _Our Social Heritage_, p. 17.] biologically +parasitic upon our social heritage. But certainly there is not the +least scientific evidence which would enable anyone to argue that men +are born with the political habits of the country in which they are +born. In so far as political habits are alike in a nation, the first +places to look for an explanation are the nursery, the school, the +church, not in that limbo inhabited by Group Minds and National Souls. +Until you have thoroughly failed to see tradition being handed on from +parents, teachers, priests, and uncles, it is a solecism of the worst +order to ascribe political differences to the germ plasm. + +It is possible to generalize tentatively and with a decent humility +about comparative differences within the same category of education +and experience. Yet even this is a tricky enterprise. For almost no +two experiences are exactly alike, not even of two children in the +same household. The older son never does have the experience of being +the younger. And therefore, until we are able to discount the +difference in nurture, we must withhold judgment about differences of +nature. As well judge the productivity of two soils by comparing their +yield before you know which is in Labrador and which in Iowa, whether +they have been cultivated and enriched, exhausted, or allowed to run +wild. + + + + +CHAPTER VII + +STEREOTYPES AS DEFENSE + +1 + +THERE is another reason, besides economy of effort, why we so often +hold to our stereotypes when we might pursue a more disinterested +vision. The systems of stereotypes may be the core of our personal +tradition, the defenses of our position in society. + +They are an ordered, more or less consistent picture of the world, to +which our habits, our tastes, our capacities, our comforts and our +hopes have adjusted themselves. They may not be a complete picture of +the world, but they are a picture of a possible world to which we are +adapted. In that world people and things have their well-known places, +and do certain expected things. We feel at home there. We fit in. We +are members. We know the way around. There we find the charm of the +familiar, the normal, the dependable; its grooves and shapes are where +we are accustomed to find them. And though we have abandoned much that +might have tempted us before we creased ourselves into that mould, +once we are firmly in, it fits as snugly as an old shoe. + +No wonder, then, that any disturbance of the stereotypes seems like an +attack upon the foundations of the universe. It is an attack upon the +foundations of _our_ universe, and, where big things are at +stake, we do not readily admit that there is any distinction between +our universe and the universe. A world which turns out to be one in +which those we honor are unworthy, and those we despise are noble, is +nerve-racking. There is anarchy if our order of precedence is not the +only possible one. For if the meek should indeed inherit the earth, if +the first should be last, if those who are without sin alone may cast +a stone, if to Caesar you render only the things that are Caesar's, +then the foundations of self-respect would be shaken for those who +have arranged their lives as if these maxims were not true. A pattern +of stereotypes is not neutral. It is not merely a way of substituting +order for the great blooming, buzzing confusion of reality. It is not +merely a short cut. It is all these things and something more. It is +the guarantee of our self-respect; it is the projection upon the world +of our own sense of our own value, our own position and our own +rights. The stereotypes are, therefore, highly charged with the +feelings that are attached to them. They are the fortress of our +tradition, and behind its defenses we can continue to feel ourselves +safe in the position we occupy. + +2 + +When, for example, in the fourth century B. C., Aristotle wrote his +defense of slavery in the face of increasing skepticism, [Footnote: +Zimmern: _Greek Commonwealth_. See his footnote, p. 383.] the +Athenian slaves were in great part indistinguishable from free +citizens Mr. Zimmern quotes an amusing passage from the Old Oligarch +explaining the good treatment of the slaves. "Suppose it were legal +for a slave to be beaten by a citizen, it would frequently happen that +an Athenian might be mistaken for a slave or an alien and receive a +beating;--since the Athenian people is not better clothed than the +slave or alien, nor in personal appearance is there any superiority." +This absence of distinction would naturally tend to dissolve the +institution. If free men and slaves looked alike, what basis was there +for treating them so differently? It was this confusion which +Aristotle set himself to clear away in the first book of his Politics. +With unerring instinct he understood that to justify slavery he must +teach the Greeks a way of _seeing_ their slaves that comported +with the continuance of slavery. + +So, said Aristotle, there are beings who are slaves by nature. +[Footnote: _Politics_, Bk. 1, Ch. 5.] "He then is by nature +formed a slave, who is fitted to become the chattel of another person, +_and on that account is so_." All this really says is that +whoever happens to be a slave is by nature intended to be one. +Logically the statement is worthless, but in fact it is not a +proposition at all, and logic has nothing to do with it. It is a +stereotype, or rather it is part of a stereotype. The rest follows +almost immediately. After asserting that slaves perceive reason, but +are not endowed with the use of it, Aristotle insists that "it is the +intention of nature to make the bodies of slaves and free men +different from each other, that the one should be robust for their +necessary purposes, but the other erect; useless indeed for such +servile labours, but fit for civil life... It is clear then that some +men are free by nature, and others are slaves. ..." + +If we ask ourselves what is the matter with Aristotle's argument, we +find that he has begun by erecting a great barrier between himself and +the facts. When he had said that those who are slaves are by nature +intended to be slaves, he at one stroke excluded the fatal question +whether those particular men who happened to be slaves were the +particular men intended by nature to be slaves. For that question +would have tainted each case of slavery with doubt. And since the fact +of being a slave was not evidence that a man was destined to be one, +no certain test would have remained. Aristotle, therefore, excluded +entirely that destructive doubt. Those who are slaves are intended to +be slaves. Each slave holder was to look upon his chattels as natural +slaves. When his eye had been trained to see them that way, he was to +note as confirmation of their servile character the fact that they +performed servile work, that they were competent to do servile work, +and that they had the muscles to do servile work. + +This is the perfect stereotype. Its hallmark is that it precedes the +use of reason; is a form of perception, imposes a certain character on +the data of our senses before the data reach the intelligence. The +stereotype is like the lavender window-panes on Beacon Street, like +the door-keeper at a costume ball who judges whether the guest has an +appropriate masquerade. There is nothing so obdurate to education or +to criticism as the stereotype. It stamps itself upon the evidence in +the very act of securing the evidence. That is why the accounts of +returning travellers are often an interesting tale of what the +traveller carried abroad with him on his trip. If he carried chiefly +his appetite, a zeal for tiled bathrooms, a conviction that the +Pullman car is the acme of human comfort, and a belief that it is +proper to tip waiters, taxicab drivers, and barbers, but under no +circumstances station agents and ushers, then his Odyssey will be +replete with good meals and bad meals, bathing adventures, +compartment-train escapades, and voracious demands for money. Or if he +is a more serious soul he may while on tour have found himself at +celebrated spots. Having touched base, and cast one furtive glance at +the monument, he buried his head in Baedeker, read every word through, +and moved on to the next celebrated spot; and thus returned with a +compact and orderly impression of Europe, rated one star, or two. + +In some measure, stimuli from the outside, especially when they are +printed or spoken words, evoke some part of a system of stereotypes, +so that the actual sensation and the preconception occupy +consciousness at the same time. The two are blended, much as if we +looked at red through blue glasses and saw green. If what we are +looking at corresponds successfully with what we anticipated, the +stereotype is reinforced for the future, as it is in a man who knows +in advance that the Japanese are cunning and has the bad luck to run +across two dishonest Japanese. + +If the experience contradicts the stereotype, one of two things +happens. If the man is no longer plastic, or if some powerful interest +makes it highly inconvenient to rearrange his stereotypes, he pooh- +poohs the contradiction as an exception that proves the rule, +discredits the witness, finds a flaw somewhere, and manages to forget +it. But if he is still curious and open-minded, the novelty is taken +into the picture, and allowed to modify it. Sometimes, if the incident +is striking enough, and if he has felt a general discomfort with his +established scheme, he may be shaken to such an extent as to distrust +all accepted ways of looking at life, and to expect that normally a +thing will not be what it is generally supposed to be. In the extreme +case, especially if he is literary, he may develop a passion for +inverting the moral canon by making Judas, Benedict Arnold, or Caesar +Borgia the hero of his tale. + +3 + +The role played by the stereotype can be seen in the German tales +about Belgian snipers. Those tales curiously enough were first refuted +by an organization of German Catholic priests known as Pax. [Footnote: +Fernand van Langenhove, _The Growth of a Legend._ The author is a +Belgian sociologist.] The existence of atrocity stories is itself not +remarkable, nor that the German people gladly believed them. But it is +remarkable that a great conservative body of patriotic Germans should +have set out as early as August 16, 1914, to contradict a collection +of slanders on the enemy, even though such slanders were of the utmost +value in soothing the troubled conscience of their fellow countrymen. +Why should the Jesuit order in particular have set out to destroy a +fiction so important to the fighting morale of Germany? + +I quote from M. van Langenhove's account: + +"Hardly had the German armies entered Belgium when strange rumors +began to circulate. They spread from place to place, they were +reproduced by the press, and they soon permeated the whole of Germany. +It was said that the Belgian people, _instigated by the clergy,_ +had intervened perfidiously in the hostilities; had attacked by +surprise isolated detachments; had indicated to the enemy the +positions occupied by the troops; that old men, and even children, had +been guilty of horrible atrocities upon wounded and defenseless German +soldiers, tearing out their eyes and cutting off fingers, nose or +ears; _that the priests from their pulpits had exhorted the people +to commit these crimes, promising them as a reward the kingdom of +heaven, and had even taken the lead in this barbarity._ + +"Public credulity accepted these stories. The highest powers in the +state welcomed them without hesitation and endorsed them with their +authority... + +"In this way public opinion in Germany was disturbed and a lively +indignation manifested itself, _directed especially against the +priests_ who were held responsible for the barbarities attributed +to the Belgians... By a natural diversion _the anger_ to which +they were a prey _was directed_ by the Germans _against the +Catholic clergy generally._ Protestants allowed the old religious +hatred to be relighted in their minds and delivered themselves to +attacks against Catholics. A new _Kulturkampf_ was let loose. + +"The Catholics did not delay in taking action against this hostile +attitude." (Italics mine) [Footnote: _Op. cit._, pp. 5-7] + +There may have been some sniping. It would be extraordinary if every +angry Belgian had rushed to the library, opened a manual of +international law, and had informed himself whether he had a right to +take potshot at the infernal nuisance tramping through his streets. It +would be no less extraordinary if an army that had never been under +fire, did not regard every bullet that came its way as unauthorized, +because it was inconvenient, and indeed as somehow a violation of the +rules of the Kriegspiel, which then constituted its only experience of +war. One can imagine the more sensitive bent on convincing themselves +that the people to whom they were doing such terrible things must be +terrible people. And so the legend may have been spun until it reached +the censors and propagandists, who, whether they believed it or not, +saw its value, and let it loose on the German civilians. They too were +not altogether sorry to find that the people they were outraging were +sub-human. And, above all, since the legend came from their heroes, +they were not only entitled to believe it, they were unpatriotic if +they did not. + +But where so much is left to the imagination because the scene of +action is lost in the fog of war, there is no check and no control. +The legend of the ferocious Belgian priests soon tapped an old hatred. +For in the minds of most patriotic protestant Germans, especially of +the upper classes, the picture of Bismarck's victories included a long +quarrel with the Roman Catholics. By a process of association, Belgian +priests became priests, and hatred of Belgians a vent for all their +hatreds. These German protestants did what some Americans did when +under the stress of war they created a compound object of hatred out +of the enemy abroad and all their opponents at home. Against this +synthetic enemy, the Hun in Germany and the Hun within the Gate, they +launched all the animosity that was in them. + +The Catholic resistance to the atrocity tales was, of course, +defensive. It was aimed at those particular fictions which aroused +animosity against all Catholics, rather than against Belgian Catholics +alone. The _Informations Pax_, says M. van Langenhove, had only +an ecclesiastical bearing and "confined their attention almost +exclusively to the reprehensible acts attributed to the priests." And +yet one cannot help wondering a little about what was set in motion in +the minds of German Catholics by this revelation of what Bismarck's +empire meant in relation to them; and also whether there was any +obscure connection between that knowledge and the fact that the +prominent German politician who was willing in the armistice to sign +the death warrant of the empire was Erzberger, [Footnote: Since this +was written, Erzberger has been assassinated.] the leader of the +Catholic Centre Party. + + + + +CHAPTER VIII + +BLIND SPOTS AND THEIR VALUE + +1 + +I HAVE been speaking of stereotypes rather than ideals, because the +word ideal is usually reserved for what we consider the good, the true +and the beautiful. Thus it carries the hint that here is something to +be copied or attained. But our repertory of fixed impressions is wider +than that. It contains ideal swindlers, ideal Tammany politicians, +ideal jingoes, ideal agitators, ideal enemies. Our stereotyped world +is not necessarily the world we should like it to be. It is simply the +kind of world we expect it to be. If events correspond there is a +sense of familiarity, and we feel that we are moving with the movement +of events. Our slave must be a slave by nature, if we are Athenians +who wish to have no qualms. If we have told our friends that we do +eighteen holes of golf in 95, we tell them after doing the course in +110, that we are not ourselves to-day. That is to say, we are not +acquainted with the duffer who foozled fifteen strokes. + +Most of us would deal with affairs through a rather haphazard and +shifting assortment of stereotypes, if a comparatively few men in each +generation were not constantly engaged in arranging, standardizing, +and improving them into logical systems, known as the Laws of +Political Economy, the Principles of Politics, and the like. Generally +when we write about culture, tradition, and the group mind, we are +thinking of these systems perfected by men of genius. Now there is no +disputing the necessity of constant study and criticism of these +idealized versions, but the historian of people, the politician, and +the publicity man cannot stop there. For what operates in history is +not the systematic idea as a genius formulated it, but shifting +imitations, replicas, counterfeits, analogies, and distortions in +individual minds. + +Thus Marxism is not necessarily what Karl Marx wrote in Das Kapital, +but whatever it is that all the warring sects believe, who claim to be +the faithful. From the gospels you cannot deduce the history of +Christianity, nor from the Constitution the political history of +America. It is Das Kapital as conceived, the gospels as preached and +the preachment as understood, the Constitution as interpreted and +administered, to which you have to go. For while there is a +reciprocating influence between the standard version and the current +versions, it is these current versions as distributed among men which +affect their behavior. [Footnote: But unfortunately it is ever so much +harder to know this actual culture than it is to summarize and to +comment upon the works of genius. The actual culture exists in people +far too busy to indulge in the strange trade of formulating their +beliefs. They record them only incidentally, and the student rarely +knows how typical are his data. Perhaps the best he can do is to +follow Lord Bryce's suggestion [_Modern Democracies_, Vol. i, p. +156] that he move freely "among all sorts and conditions of men," to +seek out the unbiassed persons in every neighborhood who have skill in +sizing up. "There is a _flair_ which long practise and 'sympathetic +touch' bestow. The trained observer learns how to profit by small +indications, as an old seaman discerns, sooner than the landsman, +the signs of coming storm." There is, in short, a vast amount of +guess work involved, and it is no wonder that scholars, who enjoy +precision, so often confine their attentions to the neater formulations +of other scholars.] + +"The theory of Relativity," says a critic whose eyelids, like the Lady +Lisa's, are a little weary, "promises to develop into a principle as +adequate to universal application as was the theory of Evolution. This +latter theory, from being a technical biological hypothesis, became an +inspiring guide to workers in practically every branch of knowledge: +manners and customs, morals, religions, philosophies, arts, steam +engines, electric tramways--everything had 'evolved.' 'Evolution' +became a very general term; it also became imprecise until, in many +cases, the original, definite meaning of the word was lost, and the +theory it had been evoked to describe was misunderstood. We are hardy +enough to prophesy a similar career and fate for the theory of +Relativity. The technical physical theory, at present imperfectly +understood, will become still more vague and dim. History repeats +itself, and Relativity, like Evolution, after receiving a number of +intelligible but somewhat inaccurate popular expositions in its +scientific aspect, will be launched on a world-conquering career. We +suggest that, by that time, it will probably be called _Relativismus_. +Many of these larger applications will doubtless be justified; some will +be absurd and a considerable number will, we imagine, reduce to truisms. +And the physical theory, the mere seed of this mighty growth, will become +once more the purely technical concern of scientific men." [Footnote: +_The Times_ (London), _Literary Supplement_, June 2, 1921, p. +352. Professor Einstein said when he was in America in 1921 that +people tended to overestimate the influence of his theory, and to +under-estimate its certainty.] + +But for such a world-conquering career an idea must correspond, +however imprecisely, to something. Professor Bury shows for how long a +time the idea of progress remained a speculative toy. "It is not +easy," he writes, [Footnote: J. B. Bury, _The Idea of Progress_, +p. 324.] "for a new idea of the speculative order to penetrate and +inform the general consciousness of a community until it has assumed +some external and concrete embodiment, or is recommended by some +striking material evidence. In the case of Progress both these +conditions were fulfilled (in England) in the period 1820-1850." The +most striking evidence was furnished by the mechanical revolution. +"Men who were born at the beginning of the century had seen, before +they had passed the age of thirty, the rapid development of steam +navigation, the illumination of towns and houses by gas, the opening +of the first railway." In the consciousness of the average householder +miracles like these formed the pattern of his belief in the +perfectibility of the human race. + +Tennyson, who was in philosophical matters a fairly normal person, +tells us that when he went by the first train from Liverpool to +Manchester (1830) he thought that the wheels ran in grooves. Then he +wrote this line: + +"Let the great world spin forever down the ringing grooves of +change." [Footnote: 2 Tennyson, _Memoir by his Son_, Vol. I, p. +195. Cited by Bury, _op. cit_., p. 326.] + +And so a notion more or less applicable to a journey between Liverpool +and Manchester was generalized into a pattern of the universe "for +ever." This pattern, taken up by others, reinforced by dazzling +inventions, imposed an optimistic turn upon the theory of evolution. +That theory, of course, is, as Professor Bury says, neutral between +pessimism and optimism. But it promised continual change, and the +changes visible in the world marked such extraordinary conquests of +nature, that the popular mind made a blend of the two. Evolution first +in Darwin himself, and then more elaborately in Herbert Spencer, was a +"progress towards perfection." + +2 + +The stereotype represented by such words as "progress" and +"perfection" was composed fundamentally of mechanical inventions. And +mechanical it has remained, on the whole, to this day. In America more +than anywhere else, the spectacle of mechanical progress has made so +deep an impression, that it has suffused the whole moral code. An +American will endure almost any insult except the charge that he is +not progressive. Be he of long native ancestry, or a recent immigrant, +the aspect that has always struck his eye is the immense physical +growth of American civilization. That constitutes a fundamental +stereotype through which he views the world: the country village will +become the great metropolis, the modest building a skyscraper, what is +small shall be big; what is slow shall be fast; what is poor shall be +rich; what is few shall be many; whatever is shall be more so. + +Not every American, of course, sees the world this way. Henry Adams +didn't, and William Allen White doesn't. But those men do, who in the +magazines devoted to the religion of success appear as Makers of +America. They mean just about that when they preach evolution, +progress, prosperity, being constructive, the American way of doing +things. It is easy to laugh, but, in fact, they are using a very great +pattern of human endeavor. For one thing it adopts an impersonal +criterion; for another it adopts an earthly criterion; for a third it +is habituating men to think quantitatively. To be sure the ideal +confuses excellence with size, happiness with speed, and human nature +with contraption. Yet the same motives are at work which have ever +actuated any moral code, or ever will. The desire for the biggest, the +fastest, the highest, or if you are a maker of wristwatches or +microscopes the smallest; the love in short of the superlative and the +"peerless," is in essence and possibility a noble passion. + +Certainly the American version of progress has fitted an extraordinary +range of facts in the economic situation and in human nature. It +turned an unusual amount of pugnacity, acquisitiveness, and lust of +power into productive work. Nor has it, until more recently perhaps, +seriously frustrated the active nature of the active members of the +community. They have made a civilization which provides them who made +it with what they feel to be ample satisfaction in work, mating and +play, and the rush of their victory over mountains, wildernesses, +distance, and human competition has even done duty for that part of +religious feeling which is a sense of communion with the purpose of +the universe. The pattern has been a success so nearly perfect in the +sequence of ideals, practice, and results, that any challenge to it is +called un-American. + +And yet, this pattern is a very partial and inadequate way of +representing the world. The habit of thinking about progress as +"development" has meant that many aspects of the environment were +simply neglected. With the stereotype of "progress" before their eyes, +Americans have in the mass seen little that did not accord with that +progress. They saw the expansion of cities, but not the accretion of +slums; they cheered the census statistics, but refused to consider +overcrowding; they pointed with pride to their growth, but would not +see the drift from the land, or the unassimilated immigration. They +expanded industry furiously at reckless cost to their natural +resources; they built up gigantic corporations without arranging for +industrial relations. They grew to be one of the most powerful nations +on earth without preparing their institutions or their minds for the +ending of their isolation. They stumbled into the World War morally +and physically unready, and they stumbled out again, much +disillusioned, but hardly more experienced. + +In the World War the good and the evil influence of the American +stereotype was plainly visible. The idea that the war could be won by +recruiting unlimited armies, raising unlimited credits, building an +unlimited number of ships, producing unlimited munitions, and +concentrating without limit on these alone, fitted the traditional +stereotype, and resulted in something like a physical miracle. +[Footnote: I have in mind the transportation and supply of two million +troops overseas. Prof. Wesley Mitchell points out that the total +production of goods after our entrance into the war did not greatly +increase in volume over that of the year 1916; but that production for +war purposes did increase.] But among those most affected by the +stereotype, there was no place for the consideration of what the +fruits of victory were, or how they were to be attained. Therefore, +aims were ignored, or regarded as automatic, and victory was +conceived, because the stereotype demanded it, as nothing but an +annihilating victory in the field. In peace time you did not ask what +the fastest motor car was for, and in war you did not ask what the +completest victory was for. Yet in Paris the pattern did not fit the +facts. In peace you can go on endlessly supplanting small things with +big ones, and big ones with bigger ones; in war when you have won +absolute victory, you cannot go on to a more absolute victory. You +have to do something on an entirely different pattern. And if you lack +such a pattern, the end of the war is to you what it was to so many +good people, an anticlimax in a dreary and savorless world. + +This marks the point where the stereotype and the facts, that cannot +be ignored, definitely part company. There is always such a point, +because our images of how things behave are simpler and more fixed +than the ebb and flow of affairs. There comes a time, therefore, when +the blind spots come from the edge of vision into the center. Then +unless there are critics who have the courage to sound an alarm, and +leaders capable of understanding the change, and a people tolerant by +habit, the stereotype, instead of economizing effort, and focussing +energy as it did in 1917 and 1918, may frustrate effort and waste +men's energy by blinding them, as it did for those people who cried +for a Carthaginian peace in 1919 and deplored the Treaty of Versailles +in 1921. + +3 + +Uncritically held, the stereotype not only censors out much that needs +to be taken into account, but when the day of reckoning comes, and the +stereotype is shattered, likely as not that which it did wisely take +into account is ship-wrecked with it. That is the punishment assessed +by Mr. Bernard Shaw against Free Trade, Free Contract, Free +Competition, Natural Liberty, Laissez-faire, and Darwinism. A hundred +years ago, when he would surely have been one of the tartest advocates +of these doctrines, he would not have seen them as he sees them +to-day, in the Infidel Half Century, [Footnote: _Back to +Methuselah_. Preface.] to be excuses for "'doing the other fellow +down' with impunity, all interference by a guiding government, all +organization except police organization to protect legalized fraud +against fisticuffs, all attempt to introduce human purpose and design +and forethought into the industrial welter being 'contrary to the laws +of political economy'" He would have seen, then, as one of the +pioneers of the march to the plains of heaven [Footnote: _The +Quintessence of Ibsenism_] that, of the kind of human purpose and +design and forethought to be found in a government like that of Queen +Victoria's uncles, the less the better. He would have seen, not the +strong doing the weak down, but the foolish doing the strong down. He +would have seen purposes, designs and forethoughts at work, +obstructing invention, obstructing enterprise, obstructing what he +would infallibly have recognized as the next move of Creative +Evolution. + +Even now Mr. Shaw is none too eager for the guidance of any guiding +government he knows, but in theory he has turned a full loop against +laissez-faire. Most advanced thinking before the war had made the same +turn against the established notion that if you unloosed everything, +wisdom would bubble up, and establish harmony. Since the war, with its +definite demonstration of guiding governments, assisted by censors, +propagandists, and spies, Roebuck Ramsden and Natural Liberty have +been readmitted to the company of serious thinkers. + +One thing is common to these cycles. There is in each set of +stereotypes a point where effort ceases and things happen of their own +accord, as you would like them to. The progressive stereotype, +powerful to incite work, almost completely obliterates the attempt to +decide what work and why that work. Laissez-faire, a blessed release +from stupid officialdom, assumes that men will move by spontaneous +combustion towards a pre-established harmony. Collectivism, an +antidote to ruthless selfishness, seems, in the Marxian mind, to +suppose an economic determinism towards efficiency and wisdom on the +part of socialist officials. Strong government, imperialism at home +and abroad, at its best deeply conscious of the price of disorder, +relies at last on the notion that all that matters to the governed +will be known by the governors. In each theory there is a spot of +blind automatism. + +That spot covers up some fact, which if it were taken into account, +would check the vital movement that the stereotype provokes. If the +progressive had to ask himself, like the Chinaman in the joke, what he +wanted to do with the time he saved by breaking the record, if the +advocate of laissez-faire had to contemplate not only free and +exuberant energies of men, but what some people call their human +nature, if the collectivist let the center of his attention be +occupied with the problem of how he is to secure his officials, if the +imperialist dared to doubt his own inspiration, you would find more +Hamlet and less Henry the Fifth. For these blind spots keep away +distracting images, which with their attendant emotions, might cause +hesitation and infirmity of purpose. Consequently the stereotype not +only saves time in a busy life and is a defense of our position in +society, but tends to preserve us from all the bewildering effect of +trying to see the world steadily and see it whole. + + + + +CHAPTER IX + +CODES AND THEIR ENEMIES + +ANYONE who has stood at the end of a railroad platform waiting for a +friend, will recall what queer people he mistook for him. The shape of +a hat, a slightly characteristic gait, evoked the vivid picture in his +mind's eye. In sleep a tinkle may sound like the pealing of a great +bell; the distant stroke of a hammer like a thunderclap. For our +constellations of imagery will vibrate to a stimulus that is perhaps +but vaguely similar to some aspect of them. They may, in +hallucination, flood the whole consciousness. They may enter very +little into perception, though I am inclined to think that such an +experience is extremely rare and highly sophisticated, as when we gaze +blankly at a familiar word or object, and it gradually ceases to be +familiar. Certainly for the most part, the way we see things is a +combination of what is there and of what we expected to find. The +heavens are not the same to an astronomer as to a pair of lovers; a +page of Kant will start a different train of thought in a Kantian and +in a radical empiricist; the Tahitian belle is a better looking person +to her Tahitian suitor than to the readers of the _National +Geographic Magazine_. + +Expertness in any subject is, in fact, a multiplication of the number +of aspects we are prepared to discover, plus the habit of discounting +our expectations. Where to the ignoramus all things look alike, and +life is just one thing after another, to the specialist things are +highly individual. For a chauffeur, an epicure, a connoisseur, a +member of the President's cabinet, or a professor's wife, there are +evident distinctions and qualities, not at all evident to the casual +person who discusses automobiles, wines, old masters, Republicans, and +college faculties. + +But in our public opinions few can be expert, while life is, as Mr. +Bernard Shaw has made plain, so short. Those who are expert are so on +only a few topics. Even among the expert soldiers, as we learned +during the war, expert cavalrymen were not necessarily brilliant with +trench-warfare and tanks. Indeed, sometimes a little expertness on a +small topic may simply exaggerate our normal human habit of trying to +squeeze into our stereotypes all that can be squeezed, and of casting +into outer darkness that which does not fit. + +Whatever we recognize as familiar we tend, if we are not very careful, +to visualize with the aid of images already in our mind. Thus in the +American view of Progress and Success there is a definite picture of +human nature and of society. It is the kind of human nature and the +kind of society which logically produce the kind of progress that is +regarded as ideal. And then, when we seek to describe or explain +actually successful men, and events that have really happened, we read +back into them the qualities that are presupposed in the stereotypes. + + +These qualities were standardized rather innocently by the older +economists. They set out to describe the social system under which +they lived, and found it too complicated for words. So they +constructed what they sincerely hoped was a simplified diagram, not so +different in principle and in veracity from the parallelogram with +legs and head in a child's drawing of a complicated cow. The scheme +consisted of a capitalist who had diligently saved capital from his +labor, an entrepreneur who conceived a socially useful demand and +organized a factory, a collection of workmen who freely contracted, +take it or leave it, for their labor, a landlord, and a group of +consumers who bought in the cheapest market those goods which by the +ready use of the pleasure-pain calculus they knew would give them the +most pleasure. The model worked. The kind of people, which the model +assumed, living in the sort of world the model assumed, invariably +coöperated harmoniously in the books where the model was described. + +With modification and embroidery, this pure fiction, used by +economists to simplify their thinking, was retailed and popularized +until for large sections of the population it prevailed as the +economic mythology of the day. It supplied a standard version of +capitalist, promoter, worker and consumer in a society that was +naturally more bent on achieving success than on explaining it. The +buildings which rose, and the bank accounts which accumulated, were +evidence that the stereotype of how the thing had been done was +accurate. And those who benefited most by success came to believe they +were the kind of men they were supposed to be. No wonder that the +candid friends of successful men, when they read the official +biography and the obituary, have to restrain themselves from asking +whether this is indeed their friend. + +2 + +To the vanquished and the victims, the official portraiture was, of +course, unrecognizable. For while those who exemplified progress did +not often pause to inquire whether they had arrived according to the +route laid down by the economists, or by some other just as +creditable, the unsuccessful people did inquire. "No one," says +William James, [Footnote: _The Letters of William James,_ Vol. I, +p.65] "sees further into a generalization than his own knowledge of +detail extends." The captains of industry saw in the great trusts +monuments of (their) success; their defeated competitors saw the +monuments of (their) failure. So the captains expounded the economies +and virtues of big business, asked to be let alone, said they were the +agents of prosperity, and the developers of trade. The vanquished +insisted upon the wastes and brutalities of the trusts, and called +loudly upon the Department of Justice to free business from +conspiracies. In the same situation one side saw progress, economy, +and a splendid development; the other, reaction, extravagance, and a +restraint of trade. Volumes of statistics, anecdotes about the real +truth and the inside truth, the deeper and the larger truth, were +published to prove both sides of the argument. + +For when a system of stereotypes is well fixed, our attention is +called to those facts which support it, and diverted from those which +contradict. So perhaps it is because they are attuned to find it, that +kindly people discover so much reason for kindness, malicious people +so much malice. We speak quite accurately of seeing through +rose-colored spectacles, or with a jaundiced eye. If, as Philip +Littell once wrote of a distinguished professor, we see life as +through a class darkly, our stereotypes of what the best people and +the lower classes are like will not be contaminated by understanding. +What is alien will be rejected, what is different will fall upon +unseeing eyes. We do not see what our eyes are not accustomed to take +into account. Sometimes consciously, more often without knowing it, we +are impressed by those facts which fit our philosophy. + +3 + +This philosophy is a more or less organized series of images for +describing the unseen world. But not only for describing it. For +judging it as well. And, therefore, the stereotypes are loaded with +preference, suffused with affection or dislike, attached to fears, +lusts, strong wishes, pride, hope. Whatever invokes the stereotype is +judged with the appropriate sentiment. Except where we deliberately +keep prejudice in suspense, we do not study a man and judge him to be +bad. We see a bad man. We see a dewy morn, a blushing maiden, a +sainted priest, a humorless Englishman, a dangerous Red, a carefree +bohemian, a lazy Hindu, a wily Oriental, a dreaming Slav, a volatile +Irishman, a greedy Jew, a 100% American. In the workaday world that is +often the real judgment, long in advance of the evidence, and it +contains within itself the conclusion which the evidence is pretty +certain to confirm. Neither justice, nor mercy, nor truth, enter into +such a judgment, for the judgment has preceded the evidence. Yet a +people without prejudices, a people with altogether neutral vision, is +so unthinkable in any civilization of which it is useful to think, +that no scheme of education could be based upon that ideal. Prejudice +can be detected, discounted, and refined, but so long as finite men +must compress into a short schooling preparation for dealing with a +vast civilization, they must carry pictures of it around with them, +and have prejudices. The quality of their thinking and doing will +depend on whether those prejudices are friendly, friendly to other +people, to other ideas, whether they evoke love of what is felt to be +positively good, rather than hatred of what is not contained in their +version of the good. + +Morality, good taste and good form first standardize and then +emphasize certain of these underlying prejudices. As we adjust +ourselves to our code, we adjust the facts we see to that code. +Rationally, the facts are neutral to all our views of right and wrong. +Actually, our canons determine greatly what we shall perceive and how. + +For a moral code is a scheme of conduct applied to a number of typical +instances. To behave as the code directs is to serve whatever purpose +the code pursues. It may be God's will, or the king's, individual +salvation in a good, solid, three dimensional paradise, success on +earth, or the service of mankind. In any event the makers of the code +fix upon certain typical situations, and then by some form of +reasoning or intuition, deduce the kind of behavior which would +produce the aim they acknowledge. The rules apply where they apply. + +But in daily living how does a man know whether his predicament is the +one the law-giver had in mind? He is told not to kill. But if his +children are attacked, may he kill to stop a killing? The Ten +Commandments are silent on the point. Therefore, around every code +there is a cloud of interpreters who deduce more specific cases. +Suppose, then, that the doctors of the law decide that he may kill in +self-defense. For the next man the doubt is almost as great; how does +he know that he is defining self-defense correctly, or that he has not +misjudged the facts, imagined the attack, and is really the aggressor? +Perhaps he has provoked the attack. But what is a provocation? Exactly +these confusions infected the minds of most Germans in August, 1914. + +Far more serious in the modern world than any difference of moral code +is the difference in the assumptions about facts to which the code is +applied. Religious, moral and political formulae are nothing like so +far apart as the facts assumed by their votaries. Useful discussion, +then, instead of comparing ideals, reexamines the visions of the +facts. Thus the rule that you should do unto others as you would have +them do unto you rests on the belief that human nature is uniform. Mr. +Bernard Shaw's statement that you should not do unto others what you +would have them do unto you, because their tastes may be different, +rests on the belief that human nature is not uniform. The maxim that +competition is the life of trade consists of a whole tome of +assumptions about economic motives, industrial relations, and the +working of a particular commercial system. The claim that America will +never have a merchant marine, unless it is privately owned and +managed, assumes a certain proved connection between a certain kind of +profit-making and incentive. The justification by the bolshevik +propagandist of the dictatorship, espionage, and the terror, because +"every state is an apparatus of violence" [Footnote: See _Two Years +of Conflict on the Internal Front_, published by the Russian +Socialist Federated Soviet Republic, Moscow, 1920. Translated by +Malcolm W. Davis for the _New York Evening Post_, January 15, +1921.] is an historical judgment, the truth of which is by no means +self-evident to a non-communist. + +At the core of every moral code there is a picture of human nature, a +map of the universe, and a version of history. To human nature (of the +sort conceived), in a universe (of the kind imagined), after a history +(so understood), the rules of the code apply. So far as the facts of +personality, of the environment and of memory are different, by so far +the rules of the code are difficult to apply with success. Now every +moral code has to conceive human psychology, the material world, and +tradition some way or other. But in the codes that are under the +influence of science, the conception is known to be an hypothesis, +whereas in the codes that come unexamined from the past or bubble up +from the caverns of the mind, the conception is not taken as an +hypothesis demanding proof or contradiction, but as a fiction accepted +without question. In the one case, man is humble about his beliefs, +because he knows they are tentative and incomplete; in the other he is +dogmatic, because his belief is a completed myth. The moralist who +submits to the scientific discipline knows that though he does not +know everything, he is in the way of knowing something; the dogmatist, +using a myth, believes himself to share part of the insight of +omniscience, though he lacks the criteria by which to tell truth from +error. For the distinguishing mark of a myth is that truth and error, +fact and fable, report and fantasy, are all on the same plane of +credibility. + +The myth is, then, not necessarily false. It might happen to be wholly +true. It may happen to be partly true. If it has affected human +conduct a long time, it is almost certain to contain much that is +profoundly and importantly true. What a myth never contains is the +critical power to separate its truths from its errors. For that power +comes only by realizing that no human opinion, whatever its supposed +origin, is too exalted for the test of evidence, that every opinion is +only somebody's opinion. And if you ask why the test of evidence is +preferable to any other, there is no answer unless you are willing to +use the test in order to test it. + +4 + +The statement is, I think, susceptible of overwhelming proof, that +moral codes assume a particular view of the facts. Under the term +moral codes I include all kinds: personal, family, economic, +professional, legal, patriotic, international. At the center of each +there is a pattern of stereotypes about psychology, sociology, and +history. The same view of human nature, institutions or tradition +rarely persists through all our codes. Compare, for example, the +economic and the patriotic codes. There is a war supposed to affect +all alike. Two men are partners in business. One enlists, the other +takes a war contract. The soldier sacrifices everything, perhaps even +his life. He is paid a dollar a day, and no one says, no one believes, +that you could make a better soldier out of him by any form of +economic incentive. That motive disappears out of his human nature. +The contractor sacrifices very little, is paid a handsome profit over +costs, and few say or believe that he would produce the munitions if +there were no economic incentive. That may be unfair to him. The point +is that the accepted patriotic code assumes one kind of human nature, +the commercial code another. And the codes are probably founded on +true expectations to this extent, that when a man adopts a certain +code he tends to exhibit the kind of human nature which the code +demands. + +That is one reason why it is so dangerous to generalize about human +nature. A loving father can be a sour boss, an earnest municipal +reformer, and a rapacious jingo abroad. His family life, his business +career, his politics, and his foreign policy rest on totally different +versions of what others are like and of how he should act. These +versions differ by codes in the same person, the codes differ somewhat +among persons in the same social set, differ widely as between social +sets, and between two nations, or two colors, may differ to the point +where there is no common assumption whatever. That is why people +professing the same stock of religious beliefs can go to war. The +element of their belief which determines conduct is that view of the +facts which they assume. + +That is where codes enter so subtly and so pervasively into the making +of public opinion. The orthodox theory holds that a public opinion +constitutes a moral judgment on a group of facts. The theory I am +suggesting is that, in the present state of education, a public +opinion is primarily a moralized and codified version of the facts. I +am arguing that the pattern of stereotypes at the center of our codes +largely determines what group of facts we shall see, and in what light +we shall see them. That is why, with the best will in the world, the +news policy of a journal tends to support its editorial policy; why a +capitalist sees one set of facts, and certain aspects of human nature, +literally sees them; his socialist opponent another set and other +aspects, and why each regards the other as unreasonable or perverse, +when the real difference between them is a difference of perception. +That difference is imposed by the difference between the capitalist +and socialist pattern of stereotypes. "There are no classes in +America," writes an American editor. "The history of all hitherto +existing society is the history of class struggles," says the +Communist Manifesto. If you have the editor's pattern in your mind, +you will see vividly the facts that confirm it, vaguely and +ineffectively those that contradict. If you have the communist +pattern, you will not only look for different things, but you will see +with a totally different emphasis what you and the editor happen to +see in common. + +5 + +And since my moral system rests on my accepted version of the facts, +he who denies either my moral judgments or my version of the facts, is +to me perverse, alien, dangerous. How shall I account for him? The +opponent has always to be explained, and the last explanation that we +ever look for is that he sees a different set of facts. Such an +explanation we avoid, because it saps the very foundation of our own +assurance that we have seen life steadily and seen it whole. It is +only when we are in the habit of recognizing our opinions as a partial +experience seen through our stereotypes that we become truly tolerant +of an opponent. Without that habit, we believe in the absolutism of +our own vision, and consequently in the treacherous character of all +opposition. For while men are willing to admit that there are two +sides to a "question," they do not believe that there are two sides to +what they regard as a "fact." And they never do believe it until after +long critical education, they are fully conscious of how second-hand +and subjective is their apprehension of their social data. + +So where two factions see vividly each its own aspect, and contrive +their own explanations of what they see, it is almost impossible for +them to credit each other with honesty. If the pattern fits their +experience at a crucial point, they no longer look upon it as an +interpretation. They look upon it as "reality." It may not resemble +the reality, except that it culminates in a conclusion which fits a +real experience. I may represent my trip from New York to Boston by a +straight line on a map, just as a man may regard his triumph as the +end of a straight and narrow path. The road by which I actually went +to Boston may have involved many detours, much turning and twisting, +just as his road may have involved much besides pure enterprise, labor +and thrift. But provided I reach Boston and he succeeds, the airline +and the straight path will serve as ready made charts. Only when +somebody tries to follow them, and does not arrive, do we have to +answer objections. If we insist on our charts, and he insists on +rejecting them, we soon tend to regard him as a dangerous fool, and he +to regard us as liars and hypocrites. Thus we gradually paint +portraits of each other. For the opponent presents himself as the man +who says, evil be thou my good. He is an annoyance who does not fit +into the scheme of things. Nevertheless he interferes. And since that +scheme is based in our minds on incontrovertible fact fortified by +irresistible logic, some place has to be found for him in the scheme. +Rarely in politics or industrial disputes is a place made for him by +the simple admission that he has looked upon the same reality and seen +another aspect of it. That would shake the whole scheme. + +Thus to the Italians in Paris Fiume was Italian It was not merely a +city that it would be desirable to include within the Italian kingdom. +It was Italian. They fixed their whole mind upon the Italian majority +within the legal boundaries of the city itself. The American +delegates, having seen more Italians in New York than there are in +Fiume, without regarding New York as Italian, fixed their eyes on +Fiume as a central European port of entry. They saw vividly the +Jugoslavs in the suburbs and the non-Italian hinterland. Some of the +Italians in Paris were therefore in need of a convincing explanation +of the American perversity. They found it in a rumor which started, no +one knows where, that an influential American diplomat was in the +snares of a Jugoslav mistress. She had been seen.... He had been +seen.... At Versailles just off the boulevard. ... The villa with the +large trees. + +This is a rather common way of explaining away opposition. In their +more libelous form such charges rarely reach the printed page, and a +Roosevelt may have to wait years, or a Harding months, before he can +force an issue, and end a whispering campaign that has reached into +every circle of talk. Public men have to endure a fearful amount of +poisonous clubroom, dinner table, boudoir slander, repeated, +elaborated, chuckled over, and regarded as delicious. While this sort +of thing is, I believe, less prevalent in America than in Europe, yet +rare is the American official about whom somebody is not repeating a +scandal. + +Out of the opposition we make villains and conspiracies. If prices go +up unmercifully the profiteers have conspired; if the newspapers +misrepresent the news, there is a capitalist plot; if the rich are too +rich, they have been stealing; if a closely fought election is lost, +the electorate was corrupted; if a statesman does something of which +you disapprove, he has been bought or influenced by some discreditable +person. If workingmen are restless, they are the victims of agitators; +if they are restless over wide areas, there is a conspiracy on foot. +If you do not produce enough aeroplanes, it is the work of spies; if +there is trouble in Ireland, it is German or Bolshevik "gold." And if +you go stark, staring mad looking for plots, you see all strikes, the +Plumb plan, Irish rebellion, Mohammedan unrest, the restoration of +King Constantine, the League of Nations, Mexican disorder, the +movement to reduce armaments, Sunday movies, short skirts, evasion of +the liquor laws, Negro self-assertion, as sub-plots under some +grandiose plot engineered either by Moscow, Rome, the Free Masons, the +Japanese, or the Elders of Zion. + + + + +CHAPTER X + +THE DETECTION OF STEREOTYPES + +1 + +Skilled diplomatists, compelled to talk out loud to the warring +peoples, learned how to use a large repertory of stereotypes. They +were dealing with a precarious alliance of powers, each of which was +maintaining its war unity only by the most careful leadership. The +ordinary soldier and his wife, heroic and selfless beyond anything in +the chronicles of courage, were still not heroic enough to face death +gladly for all the ideas which were said by the foreign offices of +foreign powers to be essential to the future of civilization. There +were ports, and mines, rocky mountain passes, and villages that few +soldiers would willingly have crossed No Man's Land to obtain for +their allies. + +Now it happened in one nation that the war party which was in control +of the foreign office, the high command, and most of the press, had +claims on the territory of several of its neighbors. These claims were +called the Greater Ruritania by the cultivated classes who regarded +Kipling, Treitschke, and Maurice Barres as one hundred percent +Ruritanian. But the grandiose idea aroused no enthusiasm abroad. So +holding this finest flower of the Ruritanian genius, as their poet +laureate said, to their hearts, Ruritania's statesmen went forth to +divide and conquer. They divided the claim into sectors. For each +piece they invoked that stereotype which some one or more of their +allies found it difficult to resist, because that ally had claims for +which it hoped to find approval by the use of this same stereotype. + +The first sector happened to be a mountainous region inhabited by +alien peasants. Ruritania demanded it to complete her natural +geographical frontier. If you fixed your attention long enough on the +ineffable value of what is natural, those alien peasants just +dissolved into fog, and only the slope of the mountains was visible. +The next sector was inhabited by Ruritanians, and on the principle +that no people ought to live under alien rule, they were re-annexed. +Then came a city of considerable commercial importance, not inhabited +by Ruritanians. But until the Eighteenth Century it had been part of +Ruritania, and on the principle of Historic Right it was annexed. +Farther on there was a splendid mineral deposit owned by aliens and +worked by aliens. On the principle of reparation for damage it was +annexed. Beyond this there was a territory inhabited 97% by aliens, +constituting the natural geographical frontier of another nation, +never historically a part of Ruritania. But one of the provinces which +had been federated into Ruritania had formerly traded in those +markets, and the upper class culture was Ruritanian. On the principle +of cultural superiority and the necessity of defending civilization, +the lands were claimed. Finally, there was a port wholly disconnected +from Ruritania geographically, ethnically, economically, historically, +traditionally. It was demanded on the ground that it was needed for +national defense. + +In the treaties that concluded the Great War you can multiply examples +of this kind. Now I do not wish to imply that I think it was possible +to resettle Europe consistently on any one of these principles. I am +certain that it was not. The very use of these principles, so +pretentious and so absolute, meant that the spirit of accommodation +did not prevail and that, therefore, the substance of peace was not +there. For the moment you start to discuss factories, mines, +mountains, or even political authority, as perfect examples of some +eternal principle or other, you are not arguing, you are fighting. +That eternal principle censors out all the objections, isolates the +issue from its background and its context, and sets going in you some +strong emotion, appropriate enough to the principle, highly +inappropriate to the docks, warehouses, and real estate. And having +started in that mood you cannot stop. A real danger exists. To meet it +you have to invoke more absolute principles in order to defend what is +open to attack. Then you have to defend the defenses, erect buffers, +and buffers for the buffers, until the whole affair is so scrambled +that it seems less dangerous to fight than to keep on talking. + +There are certain clues which often help in detecting the false +absolutism of a stereotype. In the case of the Ruritanian propaganda +the principles blanketed each other so rapidly that one could readily +see how the argument had been constructed. The series of +contradictions showed that for each sector that stereotype was +employed which would obliterate all the facts that interfered with the +claim. Contradiction of this sort is often a good clue. + +2 + +Inability to take account of space is another. In the spring of 1918, +for example, large numbers of people, appalled by the withdrawal of +Russia, demanded the "reestablishment of an Eastern Front." The war, +as they had conceived it, was on two fronts, and when one of them +disappeared there was an instant demand that it be recreated. The +unemployed Japanese army was to man the front, substituting for the +Russian. But there was one insuperable obstacle. Between Vladivostok +and the eastern battleline there were five thousand miles of country, +spanned by one broken down railway. Yet those five thousand miles +would not stay in the minds of the enthusiasts. So overwhelming was +their conviction that an eastern front was needed, and so great their +confidence in the valor of the Japanese army, that, mentally, they had +projected that army from Vladivostok to Poland on a magic carpet. In +vain our military authorities argued that to land troops on the rim of +Siberia had as little to do with reaching the Germans, as climbing +from the cellar to the roof of the Woolworth building had to do with +reaching the moon. + +The stereotype in this instance was the war on two fronts. Ever since +men had begun to imagine the Great War they had conceived Germany held +between France and Russia. One generation of strategists, and perhaps +two, had lived with that visual image as the starting point of all +their calculations. For nearly four years every battle-map they saw +had deepened the impression that this was the war. When affairs took a +new turn, it was not easy to see them as they were then. They were +seen through the stereotype, and facts which conflicted with it, such +as the distance from Japan to Poland, were incapable of coming vividly +into consciousness. + +It is interesting to note that the American authorities dealt with the +new facts more realistically than the French. In part, this was +because (previous to 1914) they had no preconception of a war upon the +continent; in part because the Americans, engrossed in the +mobilization of their forces, had a vision of the western front which +was itself a stereotype that excluded from _their_ consciousness +any very vivid sense of the other theatres of war. In the spring of +1918 this American view could not compete with the traditional French +view, because while the Americans believed enormously in their own +powers, the French at that time (before Cantigny and the Second Marne) +had the gravest doubts. The American confidence suffused the American +stereotype, gave it that power to possess consciousness, that +liveliness and sensible pungency, that stimulating effect upon the +will, that emotional interest as an object of desire, that congruity +with the activity in hand, which James notes as characteristic of what +we regard as "real." [Footnote: _Principles of Psychology_, Vol. +II, p. 300.] The French in despair remained fixed on their accepted +image. And when facts, gross geographical facts, would not fit with +the preconception, they were either censored out of mind, or the facts +were themselves stretched out of shape. Thus the difficulty of the +Japanese reaching the Germans five thousand miles away was, in +measure, overcome by bringing the Germans more than half way to meet +them. Between March and June 1918, there was supposed to be a German +army operating in Eastern Siberia. This phantom army consisted of some +German prisoners actually seen, more German prisoners thought about, +and chiefly of the delusion that those five thousand intervening miles +did not really exist. [Footnote: See in this connection Mr. Charles +Grasty's interview with Marshal Foch, _New York Times_, February +26, 1918. "Germany is walking through Russia. America and Japan, who +are in a position to do so, should go to meet her in Siberia." See +also the resolution by Senator King of Utah, June 10, 1918, and Mr. +Taft's statement in the _New York Times_, June 11, 1918, and the +appeal to America on May 5, 1918, by Mr. A. J. Sack, Director of the +Russian Information Bureau: "If Germany were in the Allied place... +she would have 3,000,000 fighting on the East front within a year."] + +3 + +A true conception of space is not a simple matter. If I draw a +straight line on a map between Bombay and Hong Kong and measure the +distance, I have learned nothing whatever about the distance I should +have to cover on a voyage. And even if I measure the actual distance +that I must traverse, I still know very little until I know what ships +are in the service, when they run, how fast they go, whether I can +secure accommodation and afford to pay for it. In practical life space +is a matter of available transportation, not of geometrical planes, as +the old railroad magnate knew when he threatened to make grass grow in +the streets of a city that had offended him. If I am motoring and ask +how far it is to my destination, I curse as an unmitigated booby the +man who tells me it is three miles, and does not mention a six mile +detour. It does me no good to be told that it is three miles if you +walk. I might as well be told it is one mile as the crow flies. I do +not fly like a crow, and I am not walking either. I must know that it +is nine miles for a motor car, and also, if that is the case, that six +of them are ruts and puddles. I call the pedestrian a nuisance who +tells me it is three miles and think evil of the aviator who told me +it was one mile. Both of them are talking about the space they have to +cover, not the space I must cover. + +In the drawing of boundary lines absurd complications have arisen +through failure to conceive the practical geography of a region. Under +some general formula like self-determination statesmen have at various +times drawn lines on maps, which, when surveyed on the spot, ran +through the middle of a factory, down the center of a village street, +diagonally across the nave of a church, or between the kitchen and +bedroom of a peasant's cottage. There have been frontiers in a grazing +country which separated pasture from water, pasture from market, and +in an industrial country, railheads from railroad. On the colored +ethnic map the line was ethnically just, that is to say, just in the +world of that ethnic map. + +4 + +But time, no less than space, fares badly. A common example is that of +the man who tries by making an elaborate will to control his money +long after his death. "It had been the purpose of the first William +James," writes his great-grandson Henry James, [Footnote: _The +Letters of William James_, Vol. I, p. 6.] "to provide that his +children (several of whom were under age when he died) should qualify +themselves by industry and experience to enjoy the large patrimony +which he expected to bequeath to them, and with that in view he left a +will which was a voluminous compound of restraints and instructions. +He showed thereby how great were both his confidence in his own +judgment and his solicitude for the moral welfare of his descendants." +The courts upset the will. For the law in its objection to +perpetuities recognizes that there are distinct limits to the +usefulness of allowing anyone to impose his moral stencil upon an +unknown future. But the desire to impose it is a very human trait, so +human that the law permits it to operate for a limited time after +death. + +The amending clause of any constitution is a good index of the +confidence the authors entertained about the reach of their opinions +in the succeeding generations. There are, I believe, American state +constitutions which are almost incapable of amendment. The men who +made them could have had but little sense of the flux of time: to them +the Here and Now was so brilliantly certain, the Hereafter so vague or +so terrifying, that they had the courage to say how life should run +after they were gone. And then because constitutions are difficult to +amend, zealous people with a taste for mortmain have loved to write on +this imperishable brass all kinds of rules and restrictions that, +given any decent humility about the future, ought to be no more +permanent than an ordinary statute. + +A presumption about time enters widely into our opinions. To one +person an institution which has existed for the whole of his conscious +life is part of the permanent furniture of the universe: to another it +is ephemeral. Geological time is very different from biological time. +Social time is most complex. The statesman has to decide whether to +calculate for the emergency or for the long run. Some decisions have +to be made on the basis of what will happen in the next two hours; +others on what will happen in a week, a month, a season, a decade, +when the children have grown up, or their children's children. An +important part of wisdom is the ability to distinguish the +time-conception that properly belongs to the thing in hand. The person +who uses the wrong time-conception ranges from the dreamer who ignores +the present to the philistine who can see nothing else. A true scale +of values has a very acute sense of relative time. + +Distant time, past and future, has somehow to be conceived. But as +James says, "of the longer duration we have no direct 'realizing' +sense." [Footnote: _Principles of Psychology_, Vol. I, p. 638.] +The longest duration which we immediately feel is what is called the +"specious present." It endures, according to Titchener, for about six +seconds. [Footnote: Cited by Warren, _Human Psychology_, p. 255.] +"All impressions within this period of time are present to us _at +once_. This makes it possible for us to perceive changes and events +as well as stationary objects. The perceptual present is supplemented +by the ideational present. Through the combination of perceptions with +memory images, entire days, months, and even years of the past are +brought together into the present." + +In this ideational present, vividness, as James said, is proportionate +to the number of discriminations we perceive within it. Thus a +vacation in which we were bored with nothing to do passes slowly while +we are in it, but seems very short in memory. Great activity kills +time rapidly, but in memory its duration is long. On the relation +between the amount we discriminate and our time perspective James has +an interesting passage: [Footnote: _Op. cit._, Vol. I, p. 639.] + +"We have every reason to think that creatures may possibly differ +enormously in the amounts of duration which they intuitively feel, and +in the fineness of the events that may fill it. Von Baer has indulged +in some interesting computations of the effect of such differences in +changing the aspect of Nature. Suppose we were able, within the length +of a second, to note 10,000 events distinctly, instead of barely 10 as +now; [Footnote: In the moving picture this effect is admirably produced +by the ultra-rapid camera.] if our life were then destined to hold the +same number of impressions, it might be 1000 times as short. We should +live less than a month, and personally know nothing of the change of +seasons. If born in winter, we should believe in summer as we now +believe in the heats of the carboniferous era. The motions of organic +beings would be so slow to our senses as to be inferred, not seen. The +sun would stand still in the sky, the moon be almost free from change, +and so on. But now reverse the hypothesis and suppose a being to get +only one 1000th part of the sensations we get in a given time, and +consequently to live 1000 times as long. Winters and summers will be +to him like quarters of an hour. Mushrooms and the swifter growing +plants will shoot into being so rapidly as to appear instantaneous +creations; annual shrubs will rise and fall from the earth like +restless boiling water springs; the motions of animals will be as +invisible as are to us the movements of bullets and cannon-balls; the +sun will scour through the sky like a meteor, leaving a fiery trail +behind him, etc." + +5 + +In his Outline of History Mr. Wells has made a gallant effort to +visualize "the true proportions of historical to geological time" +[Footnote: 1 Vol. II, p. 605. See also James Harvey Robinson, _The +New History,_ p. 239.] On a scale which represents the time from +Columbus to ourselves by three inches of space, the reader would have +to walk 55 feet to see the date of the painters of the Altamara caves, +550 feet to see the earlier Neanderthalers, a mile or so to the last +of the dinosaurs. More or less precise chronology does not begin until +after 1000 B.C., and at that time "Sargon I of the Akkadian-Sumerian +Empire was a remote memory,... more remote than is Constantine the +Great from the world of the present day.... Hammurabi had been dead a +thousand years... Stonehedge in England was already a thousand years +old." + +Mr. Wells was writing with a purpose. "In the brief period of ten +thousand years these units (into which men have combined) have grown +from the small family tribe of the early neolithic culture to the vast +united realms--vast yet still too small and partial--of the present +time." Mr. Wells hoped by changing the time perspective on our present +problems to change the moral perspective. Yet the astronomical measure +of time, the geological, the biological, any telescopic measure which +minimizes the present is not "more true" than a microscopic. Mr. +Simeon Strunsky is right when he insists that "if Mr. Wells is +thinking of his subtitle, The Probable Future of Mankind, he is +entitled to ask for any number of centuries to work out his solution. +If he is thinking of the salvaging of this western civilization, +reeling under the effects of the Great War, he must think in decades +and scores of years." [Footnote: In a review of _The Salvaging of +Civilization, The Literary Review of the N. Y. Evening Post_, June +18, 1921, p. 5.] It all depends upon the practical purpose for which +you adopt the measure. There are situations when the time perspective +needs to be lengthened, and others when it needs to be shortened. + +The man who says that it does not matter if 15,000,000 Chinese die of +famine, because in two generations the birthrate will make up the +loss, has used a time perspective to excuse his inertia. A person who +pauperizes a healthy young man because he is sentimentally +overimpressed with an immediate difficulty has lost sight of the +duration of the beggar's life. The people who for the sake of an +immediate peace are willing to buy off an aggressive empire by +indulging its appetite have allowed a specious present to interfere +with the peace of their children. The people who will not be patient +with a troublesome neighbor, who want to bring everything to a +"showdown" are no less the victims of a specious present. + +6 + +Into almost every social problem the proper calculation of time +enters. Suppose, for example, it is a question of timber. Some trees +grow faster than others. Then a sound forest policy is one in which +the amount of each species and of each age cut in each season is made +good by replanting. In so far as that calculation is correct the +truest economy has been reached. To cut less is waste, and to cut more +is exploitation. But there may come an emergency, say the need for +aeroplane spruce in a war, when the year's allowance must be exceeded. +An alert government will recognize that and regard the restoration of +the balance as a charge upon the future. + +Coal involves a different theory of time, because coal, unlike a tree, +is produced on the scale of geological time. The supply is limited. +Therefore a correct social policy involves intricate computation of +the available reserves of the world, the indicated possibilities, the +present rate of use, the present economy of use, and the alternative +fuels. But when that computation has been reached it must finally be +squared with an ideal standard involving time. Suppose, for example, +that engineers conclude that the present fuels are being exhausted at +a certain rate; that barring new discoveries industry will have to +enter a phase of contraction at some definite time in the future. We +have then to determine how much thrift and self-denial we will use, +after all feasible economies have been exercised, in order not to rob +posterity. But what shall we consider posterity? Our grandchildren? +Our great grandchildren? Perhaps we shall decide to calculate on a +hundred years, believing that to be ample time for the discovery of +alternative fuels if the necessity is made clear at once. The figures +are, of course, hypothetical. But in calculating that way we shall be +employing what reason we have. We shall be giving social time its +place in public opinion. Let us now imagine a somewhat different case: +a contract between a city and a trolley-car company. The company says +that it will not invest its capital unless it is granted a monopoly of +the main highway for ninety-nine years. In the minds of the men who +make that demand ninety-nine years is so long as to mean "forever." +But suppose there is reason to think that surface cars, run from a +central power plant on tracks, are going out of fashion in twenty +years. Then it is a most unwise contract to make, for you are +virtually condemning a future generation to inferior transportation. +In making such a contract the city officials lack a realizing sense of +ninety-nine years. Far better to give the company a subsidy now in +order to attract capital than to stimulate investment by indulging a +fallacious sense of eternity. No city official and no company official +has a sense of real time when he talks about ninety-nine years. + +Popular history is a happy hunting ground of time confusions. To the +average Englishman, for example, the behavior of Cromwell, the +corruption of the Act of Union, the Famine of 1847 are wrongs suffered +by people long dead and done by actors long dead with whom no living +person, Irish or English, has any real connection. But in the mind of +a patriotic Irishman these same events are almost contemporary. His +memory is like one of those historical paintings, where Virgil and +Dante sit side by side conversing. These perspectives and +foreshortenings are a great barrier between peoples. It is ever so +difficult for a person of one tradition to remember what is +contemporary in the tradition of another. + +Almost nothing that goes by the name of Historic Rights or Historic +Wrongs can be called a truly objective view of the past. Take, for +example, the Franco-German debate about Alsace-Lorraine. It all +depends on the original date you select. If you start with the Rauraci +and Sequani, the lands are historically part of Ancient Gaul. If you +prefer Henry I, they are historically a German territory; if you take +1273 they belong to the House of Austria; if you take 1648 and the +Peace of Westphalia, most of them are French; if you take Louis XIV +and the year 1688 they are almost all French. If you are using the +argument from history you are fairly certain to select those dates in +the past which support your view of what should be done now. + +Arguments about "races" and nationalities often betray the same +arbitrary view of time. During the war, under the influence of +powerful feeling, the difference between "Teutons" on the one hand, +and "Anglo-Saxons" and French on the other, was popularly believed to +be an eternal difference. They had always been opposing races. Yet a +generation ago, historians, like Freeman, were emphasizing the common +Teutonic origin of the West European peoples, and ethnologists would +certainly insist that the Germans, English, and the greater part of +the French are branches of what was once a common stock. The general +rule is: if you like a people to-day you come down the branches to the +trunk; if you dislike them you insist that the separate branches are +separate trunks. In one case you fix your attention on the period +before they were distinguishable; in the other on the period after +which they became distinct. And the view which fits the mood is taken +as the "truth." + +An amiable variation is the family tree. Usually one couple are +appointed the original ancestors, if possible, a couple associated +with an honorific event like the Norman Conquest. That couple have no +ancestors. They are not descendants. Yet they were the descendants of +ancestors, and the expression that So-and-So was the founder of his +house means not that he is the Adam of his family, but that he is the +particular ancestor from whom it is desirable to start, or perhaps the +earliest ancestor of which there is a record. But genealogical tables +exhibit a deeper prejudice. Unless the female line happens to be +especially remarkable descent is traced down through the males. The +tree is male. At various moments females accrue to it as itinerant +bees light upon an ancient apple tree. + +7 + +But the future is the most illusive time of all. Our temptation here +is to jump over necessary steps in the sequence; and as we are +governed by hope or doubt, to exaggerate or to minimize the time +required to complete various parts of a process. The discussion of the +role to be exercised by wage-earners in the management of industry is +riddled with this difficulty. For management is a word that covers +many functions. [Footnote: Cf. Carter L. Goodrich, The Frontier of +Control.] Some of these require no training; some require a little +training; others can be learned only in a lifetime. And the truly +discriminating program of industrial democratization would be one +based on the proper time sequence, so that the assumption of +responsibility would run parallel to a complementary program of +industrial training. The proposal for a sudden dictatorship of the +proletariat is an attempt to do away with the intervening time of +preparation; the resistance to all sharing of responsibility an +attempt to deny the alteration of human capacity in the course of +time. Primitive notions of democracy, such as rotation in office, and +contempt for the expert, are really nothing but the old myth that the +Goddess of Wisdom sprang mature and fully armed from the brow of Jove. +They assume that what it takes years to learn need not be learned at +all. + +Whenever the phrase "backward people" is used as the basis of a +policy, the conception of time is a decisive element. The Covenant of +the League of Nations says, [Footnote: Article XIX.] for example, that +"the character of the mandate must differ according to the stage of +the development of the people," as well as on other grounds. Certain +communities, it asserts, "have reached a stage of development" where +their independence can be provisionally recognized, subject to advice +and assistance "until such time as they are able to stand alone." The +way in which the mandatories and the mandated conceive that time will +influence deeply their relations. Thus in the case of Cuba the +judgment of the American government virtually coincided with that of +the Cuban patriots, and though there has been trouble, there is no +finer page in the history of how strong powers have dealt with the +weak. Oftener in that history the estimates have not coincided. Where +the imperial people, whatever its public expressions, has been deeply +convinced that the backwardness of the backward was so hopeless as not +to be worth remedying, or so profitable that it was not desirable to +remedy it, the tie has festered and poisoned the peace of the world. +There have been a few cases, very few, where backwardness has meant to +the ruling power the need for a program of forwardness, a program with +definite standards and definite estimates of time. Far more +frequently, so frequently in fact as to seem the rule, backwardness +has been conceived as an intrinsic and eternal mark of inferiority. +And then every attempt to be less backward has been frowned upon as +the sedition, which, under these conditions, it undoubtedly is. In our +own race wars we can see some of the results of the failure to realize +that time would gradually obliterate the slave morality of the Negro, +and that social adjustment based on this morality would begin to break +down. + +It is hard not to picture the future as if it obeyed our present +purposes, to annihilate whatever delays our desire, or immortalize +whatever stands between us and our fears. + +8 + +In putting together our public opinions, not only do we have to +picture more space than we can see with our eyes, and more time than +we can feel, but we have to describe and judge more people, more +actions, more things than we can ever count, or vividly imagine. We +have to summarize and generalize. We have to pick out samples, and +treat them as typical. + +To pick fairly a good sample of a large class is not easy. The problem +belongs to the science of statistics, and it is a most difficult +affair for anyone whose mathematics is primitive, and mine remain +azoic in spite of the half dozen manuals which I once devoutly +imagined that I understood. All they have done for me is to make me a +little more conscious of how hard it is to classify and to sample, how +readily we spread a little butter over the whole universe. + +Some time ago a group of social workers in Sheffield, England, started +out to substitute an accurate picture of the mental equipment of the +workers of that city for the impressionistic one they had. [Footnote: +_The Equipment of the Worker_.] They wished to say, with some +decent grounds for saying it, how the workers of Sheffield were +equipped. They found, as we all find the moment we refuse to let our +first notion prevail, that they were beset with complications. Of the +test they employed nothing need be said here except that it was a +large questionnaire. For the sake of the illustration, assume that the +questions were a fair test of mental equipment for English city life. +Theoretically, then, those questions should have been put to every +member of the working class. But it is not so easy to know who are the +working class. However, assume again that the census knows how to +classify them. Then there were roughly 104,000 men and 107,000 women +who ought to have been questioned. They possessed the answers which +would justify or refute the casual phrase about the "ignorant workers" +or the "intelligent workers." But nobody could think of questioning +the whole two hundred thousand. + +So the social workers consulted an eminent statistician, Professor +Bowley. He advised them that not less than 408 men and 408 women would +prove to be a fair sample. According to mathematical calculation this +number would not show a greater deviation from the average than 1 in +22. [Footnote: _Op. cit._, p. 65.] They had, therefore, to +question at least 816 people before they could pretend to talk about +the average workingman. But which 816 people should they approach? "We +might have gathered particulars concerning workers to whom one or +another of us had a pre-inquiry access; we might have worked through +philanthropic gentlemen and ladies who were in contact with certain +sections of workers at a club, a mission, an infirmary, a place of +worship, a settlement. But such a method of selection would produce +entirely worthless results. The workers thus selected would not be in +any sense representative of what is popularly called 'the average run +of workers;' they would represent nothing but the little coteries to +which they belonged. + +"The right way of securing 'victims,' to which at immense cost of time +and labour we rigidly adhered, is to get hold of your workers by some +'neutral' or 'accidental' or 'random' method of approach." This they +did. And after all these precautions they came to no more definite +conclusion than that on their classification and according to their +questionnaire, among 200,000 Sheffield workers "about one quarter" +were "well equipped," "approaching three-quarters" were "inadequately +equipped" and that "about one-fifteenth" were "mal-equipped." + +Compare this conscientious and almost pedantic method of arriving at +an opinion, with our usual judgments about masses of people, about the +volatile Irish, and the logical French, and the disciplined Germans, +and the ignorant Slavs, and the honest Chinese, and the untrustworthy +Japanese, and so on and so on. All these are generalizations drawn +from samples, but the samples are selected by a method that +statistically is wholly unsound. Thus the employer will judge labor by +the most troublesome employee or the most docile that he knows, and +many a radical group has imagined that it was a fair sample of the +working class. How many women's views on the "servant question" are +little more than the reflection of their own treatment of their +servants? The tendency of the casual mind is to pick out or stumble +upon a sample which supports or defies its prejudices, and then to +make it the representative of a whole class. + +A great deal of confusion arises when people decline to classify +themselves as we have classified them. Prophecy would be so much +easier if only they would stay where we put them. But, as a matter of +fact, a phrase like the working class will cover only some of the +truth for a part of the time. When you take all the people, below a +certain level of income, and call them the working class, you cannot +help assuming that the people so classified will behave in accordance +with your stereotype. Just who those people are you are not quite +certain. Factory hands and mine workers fit in more or less, but farm +hands, small farmers, peddlers, little shop keepers, clerks, servants, +soldiers, policemen, firemen slip out of the net. The tendency, when +you are appealing to the "working class," is to fix your attention on +two or three million more or less confirmed trade unionists, and treat +them as Labor; the other seventeen or eighteen million, who might +qualify statistically, are tacitly endowed with the point of view +ascribed to the organized nucleus. How very misleading it was to +impute to the British working class in 1918-1921 the point of view +expressed in the resolutions of the Trades Union Congress or in the +pamphlets written by intellectuals. + +The stereotype of Labor as Emancipator selects the evidence which +supports itself and rejects the other. And so parallel with the real +movements of working men there exists a fiction of the Labor Movement, +in which an idealized mass moves towards an ideal goal. The fiction +deals with the future. In the future possibilities are almost +indistinguishable from probabilities and probabilities from +certainties. If the future is long enough, the human will might turn +what is just conceivable into what is very likely, and what is likely +into what is sure to happen. James called this the faith ladder, and +said that "it is a slope of goodwill on which in the larger questions +of life men habitually live." [Footnote: William James, _Some +Problems of Philosophy_, p. 224.] + +"1. There is nothing absurd in a certain view of the world being true, +nothing contradictory; + +2. It _might_ have been true under certain conditions; + +3. It _may_ be true even now; + +4. It is _fit_ to be true; + +5. It _ought_ to be true; + +6. It _must_ be true; + +7. It _shall_ be true, at any rate true for me." + +And, as he added in another place, [Footnote: _A Pluralistic +Universe_, p. 329.] "your acting thus may in certain special cases +be a means of making it securely true in the end." Yet no one would +have insisted more than he, that, so far as we know how, we must avoid +substituting the goal for the starting point, must avoid reading back +into the present what courage, effort and skill might create in the +future. Yet this truism is inordinately difficult to live by, because +every one of us is so little trained in the selection of our samples. + +If we believe that a certain thing ought to be true, we can almost +always find either an instance where it is true, or someone who +believes it ought to be true. It is ever so hard when a concrete fact +illustrates a hope to weigh that fact properly. When the first six +people we meet agree with us, it is not easy to remember that they may +all have read the same newspaper at breakfast. And yet we cannot send +out a questionnaire to 816 random samples every time we wish to +estimate a probability. In dealing with any large mass of facts, the +presumption is against our having picked true samples, if we are +acting on a casual impression. + +9 + +And when we try to go one step further in order to seek the causes and +effects of unseen and complicated affairs, haphazard opinion is very +tricky. There are few big issues in public life where cause and effect +are obvious at once. They are not obvious to scholars who have devoted +years, let us say, to studying business cycles, or price and wage +movements, or the migration and the assimilation of peoples, or the +diplomatic purposes of foreign powers. Yet somehow we are all supposed +to have opinions on these matters, and it is not surprising that the +commonest form of reasoning is the intuitive, post hoc ergo propter +hoc. + +The more untrained a mind, the more readily it works out a theory that +two things which catch its attention at the same time are causally +connected. We have already dwelt at some length on the way things +reach our attention. We have seen that our access to information is +obstructed and uncertain, and that our apprehension is deeply +controlled by our stereotypes; that the evidence available to our +reason is subject to illusions of defense, prestige, morality, space, +time, and sampling. We must note now that with this initial taint, +public opinions are still further beset, because in a series of events +seen mostly through stereotypes, we readily accept sequence or +parallelism as equivalent to cause and effect. + +This is most likely to happen when two ideas that come together arouse +the same feeling. If they come together they are likely to arouse the +same feeling; and even when they do not arrive together a powerful +feeling attached to one is likely to suck out of all the corners of +memory any idea that feels about the same. Thus everything painful +tends to collect into one system of cause and effect, and likewise +everything pleasant. + +"IId IIm (1675) This day I hear that G[od] has shot an arrow into the +midst of this Town. The small pox is in an ordinary ye sign of the +Swan, the ordinary Keepers name is Windsor. His daughter is sick of +the disease. It is observable that this disease begins at an alehouse, +to testify God's displeasure agt the sin of drunkenness & yt of +multiplying alehouses!" [Footnote: _The Heart of the Puritan_, p. +177, edited by Elizabeth Deering Hanscom.] + +Thus Increase Mather, and thus in the year 1919 a distinguished +Professor of Celestial Mechanics discussing the Einstein theory: + +"It may well be that.... Bolshevist uprisings are in reality the +visible objects of some underlying, deep, mental disturbance, +world-wide in character.... This same spirit of unrest has invaded +science." [Footnote: Cited in _The New Republic_, Dec. 24, 1919, +p. 120.] + +In hating one thing violently, we readily associate with it as cause +or effect most of the other things we hate or fear violently. They may +have no more connection than smallpox and alehouses, or Relativity and +Bolshevism, but they are bound together in the same emotion. In a +superstitious mind, like that of the Professor of Celestial Mechanics, +emotion is a stream of molten lava which catches and imbeds whatever +it touches. When you excavate in it you find, as in a buried city, all +sorts of objects ludicrously entangled in each other. Anything can be +related to anything else, provided it feels like it. Nor has a mind in +such a state any way of knowing how preposterous it is. Ancient fears, +reinforced by more recent fears, coagulate into a snarl of fears where +anything that is dreaded is the cause of anything else that is +dreaded. + +10 + +Generally it all culminates in the fabrication of a system of all +evil, and of another which is the system of all good. Then our love of +the absolute shows itself. For we do not like qualifying +adverbs. [Footnote: _Cf_. Freud's discussion of absolutism in +dreams, _Interpretation of Dreams_, Chapter VI, especially pp. +288, _et seq_.] They clutter up sentences, and interfere with +irresistible feeling. We prefer most to more, least to less, we +dislike the words rather, perhaps, if, or, but, toward, not quite, +almost, temporarily, partly. Yet nearly every opinion about public +affairs needs to be deflated by some word of this sort. But in our +free moments everything tends to behave absolutely,--one hundred +percent, everywhere, forever. + +It is not enough to say that our side is more right than the enemy's, +that our victory will help democracy more than his. One must insist +that our victory will end war forever, and make the world safe for +democracy. And when the war is over, though we have thwarted a greater +evil than those which still afflict us, the relativity of the result +fades out, the absoluteness of the present evil overcomes our spirit, +and we feel that we are helpless because we have not been +irresistible. Between omnipotence and impotence the pendulum swings. + +Real space, real time, real numbers, real connections, real weights +are lost. The perspective and the background and the dimensions of +action are clipped and frozen in the stereotype. + + + + +PART IV + +INTERESTS + +CHAPTER 11. THE ENLISTING OF INTEREST + " 12. SELF-INTEREST RECONSIDERED + + + + +CHAPTER XI + +THE ENLISTING OF INTEREST + +I + +BUT the human mind is not a film which registers once and for all each +impression that comes through its shutters and lenses. The human mind +is endlessly and persistently creative. The pictures fade or combine, +are sharpened here, condensed there, as we make them more completely +our own. They do not lie inert upon the surface of the mind, but are +reworked by the poetic faculty into a personal expression of +ourselves. We distribute the emphasis and participate in the action. + +In order to do this we tend to personalize quantities, and to +dramatize relations. As some sort of allegory, except in acutely +sophisticated minds, the affairs of the world are represented. Social +Movements, Economic Forces, National Interests, Public Opinion are +treated as persons, or persons like the Pope, the President, Lenin, +Morgan or the King become ideas and institutions. The deepest of all +the stereotypes is the human stereotype which imputes human nature to +inanimate or collective things. + +The bewildering variety of our impressions, even after they have been +censored in all kinds of ways, tends to force us to adopt the greater +economy of the allegory. So great is the multitude of things that we +cannot keep them vividly in mind. Usually, then, we name them, and let +the name stand for the whole impression. But a name is porous. Old +meanings slip out and new ones slip in, and the attempt to retain the +full meaning of the name is almost as fatiguing as trying to recall +the original impressions. Yet names are a poor currency for thought. +They are too empty, too abstract, too inhuman. And so we begin to see +the name through some personal stereotype, to read into it, finally to +see in it the incarnation of some human quality. + +Yet human qualities are themselves vague and fluctuating. They are +best remembered by a physical sign. And therefore, the human qualities +we tend to ascribe to the names of our impressions, themselves tend to +be visualized in physical metaphors. The people of England, the +history of England, condense into England, and England becomes John +Bull, who is jovial and fat, not too clever, but well able to take +care of himself. The migration of a people may appear to some as the +meandering of a river, and to others like a devastating flood. The +courage people display may be objectified as a rock; their purpose as +a road, their doubts as forks of the road, their difficulties as ruts +and rocks, their progress as a fertile valley. If they mobilize their +dread-naughts they unsheath a sword. If their army surrenders they are +thrown to earth. If they are oppressed they are on the rack or under +the harrow. + +When public affairs are popularized in speeches, headlines, plays, +moving pictures, cartoons, novels, statues or paintings, their +transformation into a human interest requires first abstraction from +the original, and then animation of what has been abstracted. We +cannot be much interested in, or much moved by, the things we do not +see. Of public affairs each of us sees very little, and therefore, +they remain dull and unappetizing, until somebody, with the makings of +an artist, has translated them into a moving picture. Thus the +abstraction, imposed upon our knowledge of reality by all the +limitations of our access and of our prejudices, is compensated. Not +being omnipresent and omniscient we cannot see much of what we have to +think and talk about. Being flesh and blood we will not feed on words +and names and gray theory. Being artists of a sort we paint pictures, +stage dramas and draw cartoons out of the abstractions. + +Or, if possible, we find gifted men who can visualize for us. For +people are not all endowed to the same degree with the pictorial +faculty. Yet one may, I imagine, assert with Bergson that the +practical intelligence is most closely adapted to spatial +qualities. [Footnote: _Creative Evolution_, Chs. III, IV.] A +"clear" thinker is almost always a good visualizer. But for that same +reason, because he is "cinematographic," he is often by that much +external and insensitive. For the people who have intuition, which is +probably another name for musical or muscular perception, often +appreciate the quality of an event and the inwardness of an act far +better than the visualizer. They have more understanding when the +crucial element is a desire that is never crudely overt, and appears +on the surface only in a veiled gesture, or in a rhythm of speech. +Visualization may catch the stimulus and the result. But the +intermediate and internal is often as badly caricatured by a +visualizer, as is the intention of the composer by an enormous soprano +in the sweet maiden's part. + +Nevertheless, though they have often a peculiar justice, intuitions +remain highly private and largely incommunicable. But social +intercourse depends on communication, and while a person can often +steer his own life with the utmost grace by virtue of his intuitions, +he usually has great difficulty in making them real to others. When he +talks about them they sound like a sheaf of mist. For while intuition +does give a fairer perception of human feeling, the reason with its +spatial and tactile prejudice can do little with that perception. +Therefore, where action depends on whether a number of people are of +one mind, it is probably true that in the first instance no idea is +lucid for practical decision until it has visual or tactile value. But +it is also true, that no visual idea is significant to us until it has +enveloped some stress of our own personality. Until it releases or +resists, depresses or enhances, some craving of our own, it remains +one of the objects which do not matter. + +2 + +Pictures have always been the surest way of conveying an idea, and +next in order, words that call up pictures in memory. But the idea +conveyed is not fully our own until we have identified ourselves with +some aspect of the picture. The identification, or what Vernon Lee has +called empathy, [Footnote: _Beauty and Ugliness_.] may be almost +infinitely subtle and symbolic. The mimicry may be performed without +our being aware of it, and sometimes in a way that would horrify those +sections of our personality which support our self-respect. In +sophisticated people the participation may not be in the fate of the +hero, but in the fate of the whole idea to which both hero and villain +are essential. But these are refinements. + +In popular representation the handles for identification are almost +always marked. You know who the hero is at once. And no work promises +to be easily popular where the marking is not definite and the choice +clear. [Footnote: A fact which bears heavily on the character of news. +_Cf_. Part VII.] But that is not enough. The audience must have +something to do, and the contemplation of the true, the good and the +beautiful is not something to do. In order not to sit inertly in the +presence of the picture, and this applies as much to newspaper stories +as to fiction and the cinema, the audience must be exercised by the +image. Now there are two forms of exercise which far transcend all +others, both as to ease with which they are aroused, and eagerness +with which stimuli for them are sought. They are sexual passion and +fighting, and the two have so many associations with each other, blend +into each other so intimately, that a fight about sex outranks every +other theme in the breadth of its appeal. There is none so engrossing +or so careless of all distinctions of culture and frontiers. + +The sexual motif figures hardly at all in American political imagery. +Except in certain minor ecstasies of war, in an occasional scandal, or +in phases of the racial conflict with Negroes or Asiatics, to speak of +it at all would seem far-fetched. Only in moving pictures, novels, and +some magazine fiction are industrial relations, business competition, +politics, and diplomacy tangled up with the girl and the other woman. +But the fighting motif appears at every turn. Politics is interesting +when there is a fight, or as we say, an issue. And in order to make +politics popular, issues have to be found, even when in truth and +justice, there are none,--none, in the sense that the differences of +judgment, or principle, or fact, do not call for the enlistment of +pugnacity. [Footnote: _Cf_. Frances Taylor Patterson, _Cinema +Craftsmanship_, pp. 31-32. "III. If the plot lacks suspense: 1. Add +an antagonist, 2. Add an obstacle, 3. Add a problem, 4. Emphasize one +of the questions in the minds of the spectator.,.."] + +But where pugnacity is not enlisted, those of us who are not directly +involved find it hard to keep up our interest. For those who are +involved the absorption may be real enough to hold them even when no +issue is involved. They may be exercised by sheer joy in activity, or +by subtle rivalry or invention. But for those to whom the whole +problem is external and distant, these other faculties do not easily +come into play. In order that the faint image of the affair shall mean +something to them, they must be allowed to exercise the love of +struggle, suspense, and victory. + +Miss Patterson [Footnote: _Op. cit._, pp. 6-7.] insists that +"suspense... constitutes the difference between the masterpieces in +the Metropolitan Museum of Art and the pictures at the Rivoli or the +Rialto Theatres." Had she made it clear that the masterpieces lack +either an easy mode of identification or a theme popular for this +generation, she would be wholly right in saying that this "explains +why the people straggle into the Metropolitan by twos and threes and +struggle into the Rialto and Rivoli by hundreds. The twos and threes +look at a picture in the Art Museum for less than ten minutes--unless +they chance to be art students, critics, or connoisseurs. The hundreds +in the Rivoli or the Rialto look at the picture for more than an hour. +As far as beauty is concerned there can be no comparison of the merits +of the two pictures. Yet the motion picture draws more people and +holds them at attention longer than do the masterpieces, not through +any intrinsic merit of its own, but because it depicts unfolding +events, the outcome of which the audience is breathlessly waiting. It +possesses the element of struggle, which never fails to arouse +suspense." + +In order then that the distant situation shall not be a gray flicker +on the edge of attention, it should be capable of translation into +pictures in which the opportunity for identification is recognizable. +Unless that happens it will interest only a few for a little while. It +will belong to the sights seen but not felt, to the sensations that +beat on our sense organs, and are not acknowledged. We have to take +sides. We have to be able to take sides. In the recesses of our being +we must step out of the audience on to the stage, and wrestle as the +hero for the victory of good over evil. We must breathe into the +allegory the breath of our life. + +3 + +And so, in spite of the critics, a verdict is rendered in the old +controversy about realism and romanticism. Our popular taste is to +have the drama originate in a setting realistic enough to make +identification plausible and to have it terminate in a setting +romantic enough to be desirable, but not so romantic as to be +inconceivable. In between the beginning and the end the canons are +liberal, but the true beginning and the happy ending are landmarks. +The moving picture audience rejects fantasy logically developed, +because in pure fantasy there is no familiar foothold in the age of +machines. It rejects realism relentlessly pursued because it does not +enjoy defeat in a struggle that has become its own. + +What will be accepted as true, as realistic, as good, as evil, as +desirable, is not eternally fixed. These are fixed by stereotypes, +acquired from earlier experiences and carried over into judgment of +later ones. And, therefore, if the financial investment in each film +and in popular magazines were not so exorbitant as to require instant +and widespread popularity, men of spirit and imagination would be able +to use the screen and the periodical, as one might dream of their +being used, to enlarge and to refine, to verify and criticize the +repertory of images with which our imaginations work. But, given the +present costs, the men who make moving pictures, like the church and +the court painters of other ages, must adhere to the stereotypes that +they find, or pay the price of frustrating expectation. The +stereotypes can be altered, but not in time to guarantee success when +the film is released six months from now. + +The men who do alter the stereotypes, the pioneering artists and +critics, are naturally depressed and angered at managers and editors +who protect their investments. They are risking everything, then why +not the others? That is not quite fair, for in their righteous fury +they have forgotten their own rewards, which are beyond any that their +employers can hope to feel. They could not, and would not if they +could, change places. And they have forgotten another thing in the +unceasing war with Philistia. They have forgotten that they are +measuring their own success by standards that artists and wise men of +the past would never have dreamed of invoking. They are asking for +circulations and audiences that were never considered by any artist +until the last few generations. And when they do not get them, they +are disappointed. + +Those who catch on, like Sinclair Lewis in "Main Street," are men who +have succeeded in projecting definitely what great numbers of other +people were obscurely trying to say inside their heads. "You have said +it for me." They establish a new form which is then endlessly copied +until it, too, becomes a stereotype of perception. The next pioneer +finds it difficult to make the public see Main Street any other way. +And he, like the forerunners of Sinclair Lewis, has a quarrel with the +public. + +This quarrel is due not only to the conflict of stereotypes, but to +the pioneering artist's reverence for his material. Whatever the plane +he chooses, on that plane he remains. If he is dealing with the +inwardness of an event he follows it to its conclusion regardless of +the pain it causes. He will not tag his fantasy to help anyone, or cry +peace where there is no peace. There is his America. But big audiences +have no stomach for such severity. They are more interested in +themselves than in anything else in the world. The selves in which +they are interested are the selves that have been revealed by schools +and by tradition. They insist that a work of art shall be a vehicle +with a step where they can climb aboard, and that they shall ride, not +according to the contours of the country, but to a land where for an +hour there are no clocks to punch and no dishes to wash. To satisfy +these demands there exists an intermediate class of artists who are +able and willing to confuse the planes, to piece together a +realistic-romantic compound out of the inventions of greater men, and, +as Miss Patterson advises, give "what real life so rarely does-the +triumphant resolution of a set of difficulties; the anguish of virtue +and the triumph of sin... changed to the glorifications of virtue and +the eternal punishment of its enemy." [Footnote: _Op. cit._, p. +46. "The hero and heroine must in general possess youth, beauty, +goodness, exalted self-sacrifice, and unalterable constancy."] + +4 + +The ideologies of politics obey these rules. The foothold of realism +is always there. The picture of some real evil, such as the German +threat or class conflict, is recognizable in the argument. There is a +description of some aspect of the world which is convincing because it +agrees with familiar ideas. But as the ideology deals with an unseen +future, as well as with a tangible present, it soon crosses +imperceptibly the frontier of verification. In describing the present +you are more or less tied down to common experience. In describing +what nobody has experienced you are bound to let go. You stand at +Armageddon, more or less, but you battle for the Lord, perhaps.... A +true beginning, true according to the standards prevailing, and a +happy ending. Every Marxist is hard as nails about the brutalities of +the present, and mostly sunshine about the day after the dictatorship. +So were the war propagandists: there was not a bestial quality in +human nature they did not find everywhere east of the Rhine, or west +of it if they were Germans. The bestiality was there all right. But +after the victory, eternal peace. Plenty of this is quite cynically +deliberate. For the skilful propagandist knows that while you must +start with a plausible analysis, you must not keep on analyzing, +because the tedium of real political accomplishment will soon destroy +interest. So the propagandist exhausts the interest in reality by a +tolerably plausible beginning, and then stokes up energy for a long +voyage by brandishing a passport to heaven. + +The formula works when the public fiction enmeshes itself with a +private urgency. But once enmeshed, in the heat of battle, the +original self and the original stereotype which effected the junction +may be wholly lost to sight. + + + + +CHAPTER XII + +SELF-INTEREST RECONSIDERED + +1 + +THEREFORE, the identical story is not the same story to all who hear +it. Each will enter it at a slightly different point, since no two +experiences are exactly alike; he will reenact it in his own way, and +transfuse it with his own feelings. Sometimes an artist of compelling +skill will force us to enter into lives altogether unlike our own, +lives that seem at first glance dull, repulsive, or eccentric. But +that is rare. In almost every story that catches our attention we +become a character and act out the role with a pantomime of our own. +The pantomime may be subtle or gross, may be sympathetic to the story, +or only crudely analogous; but it will consist of those feelings which +are aroused by our conception of the role. And so, the original theme +as it circulates, is stressed, twisted, and embroidered by all the +minds through which it goes. It is as if a play of Shakespeare's were +rewritten each time it is performed with all the changes of emphasis +and meaning that the actors and audience inspired. + +Something very like that seems to have happened to the stories in the +sagas before they were definitively written down. In our time the +printed record, such as it is, checks the exuberance of each +individual's fancy. But against rumor there is little or no checks and +the original story, true or invented, grows wings and horns, hoofs and +beaks, as the artist in each gossip works upon it. The first +narrator's account does not keep its shape and proportions. It is +edited and revised by all who played with it as they heard it, used it +for day dreams, and passed it on. [Footnote: For an interesting +example, see the case described by C. J. Jung, _Zentralblatt für +Psychoanalyse_, 1911, Vol. I, p. 81. Translated by Constance Long, +in _Analytical Psychology_, Ch. IV.] + +Consequently the more mixed the audience, the greater will be the +variation in the response. For as the audience grows larger, the +number of common words diminishes. Thus the common factors in the +story become more abstract. This story, lacking precise character of +its own, is heard by people of highly varied character. They give it +their own character. + +2 + +The character they give it varies not only with sex and age, race and +religion and social position, but within these cruder classifications, +according to the inherited and acquired constitution of the +individual, his faculties, his career, the progress of his career, an +emphasized aspect of his career, his moods and tenses, or his place on +the board in any of the games of life that he is playing. What reaches +him of public affairs, a few lines of print, some photographs, +anecdotes, and some casual experience of his own, he conceives through +his set patterns and recreates with his own emotions. He does not take +his personal problems as partial samples of the greater environment. +He takes his stories of the greater environment as a mimic enlargement +of his private life. + +But not necessarily of that private life as he would describe it to +himself. For in his private life the choices are narrow, and much of +himself is squeezed down and out of sight where it cannot directly +govern his outward behavior. And thus, beside the more average people +who project the happiness of their own lives into a general good will, +or their unhappiness into suspicion and hate, there are the outwardly +happy people who are brutal everywhere but in their own circle, as +well as the people who, the more they detest their families, their +friends, their jobs, the more they overflow with love for mankind. + +As you descend from generalities to detail, it becomes more apparent +that the character in which men deal with their affairs is not fixed. +Possibly their different selves have a common stem and common +qualities, but the branches and the twigs have many forms. Nobody +confronts every situation with the same character. His character +varies in some degree through the sheer influence of time and +accumulating memory, since he is not an automaton. His character +varies, not only in time, but according to circumstance. The legend of +the solitary Englishman in the South Seas, who invariably shaves and +puts on a black tie for dinner, bears witness to his own intuitive and +civilized fear of losing the character which he has acquired. So do +diaries, and albums, and souvenirs, old letters, and old clothes, and +the love of unchanging routine testify to our sense of how hard it is +to step twice in the Heraclitan river. + +There is no one self always at work. And therefore it is of great +importance in the formation of any public opinion, what self is +engaged. The Japanese ask the right to settle in California. Clearly +it makes a whole lot of difference whether you conceive the demand as +a desire to grow fruit or to marry the white man's daughter. If two +nations are disputing a piece of territory, it matters greatly whether +the people regard the negotiations as a real estate deal, an attempt +to humiliate them, or, in the excited and provocative language which +usually enclouds these arguments, as a rape. For the self which takes +charge of the instincts when we are thinking about lemons or distant +acres is very different from the self which appears when we are +thinking even potentially as the outraged head of a family. In one +case the private feeling which enters into the opinion is tepid, in +the other, red hot. And so while it is so true as to be mere tautology +that "self-interest" determines opinion, the statement is not +illuminating, until we know which self out of many selects and directs +the interest so conceived. + +Religious teaching and popular wisdom have always distinguished +several personalities in each human being. They have been called the +Higher and Lower, the Spiritual and the Material, the Divine and the +Carnal; and although we may not wholly accept this classification, we +cannot fail to observe that distinctions exist. Instead of two +antithetic selves, a modern man would probably note a good many not so +sharply separated. He would say that the distinction drawn by +theologians was arbitrary and external, because many different selves +were grouped together as higher provided they fitted into the +theologian's categories, but he would recognize nevertheless that here +was an authentic clue to the variety of human nature. + +We have learned to note many selves, and to be a little less ready to +issue judgment upon them. We understand that we see the same body, but +often a different man, depending on whether he is dealing with a +social equal, a social inferior, or a social superior; on whether he +is making love to a woman he is eligible to marry, or to one whom he +is not; on whether he is courting a woman, or whether he considers +himself her proprietor; on whether he is dealing with his children, +his partners, his most trusted subordinates, the boss who can make him +or break him; on whether he is struggling for the necessities of life, +or successful; on whether he is dealing with a friendly alien, or a +despised one; on whether he is in great danger, or in perfect +security; on whether he is alone in Paris or among his family in +Peoria. + +People differ widely, of course, in the consistency of their +characters, so widely that they may cover the whole gamut of +differences between a split soul like Dr. Jekyll's and an utterly +singleminded Brand, Parsifal, or Don Quixote. If the selves are too +unrelated, we distrust the man; if they are too inflexibly on one +track we find him arid, stubborn, or eccentric. In the repertory of +characters, meager for the isolated and the self-sufficient, highly +varied for the adaptable, there is a whole range of selves, from that +one at the top which we should wish God to see, to those at the bottom +that we ourselves do not dare to see. There may be octaves for the +family,--father, Jehovah, tyrant,--husband, proprietor, male,--lover, +lecher,--for the occupation,--employer, master, exploiter,--competitor, +intriguer, enemy,--subordinate, courtier, snob. Some never come out +into public view. Others are called out only by exceptional circumstances. +But the characters take their form from a man's conception of the +situation in which he finds himself. If the environment to which he +is sensitive happens to be the smart set, he will imitate the character +he conceives to be appropriate. That character will tend to act as +modulator of his bearing, his speech, his choice of subjects, his +preferences. Much of the comedy of life lies here, in the way people +imagine their characters for situations that are strange to them: the +professor among promoters, the deacon at a poker game, the +cockney in the country, the paste diamond among real diamonds. + +3 + +Into the making of a man's characters there enters a variety of +influences not easily separated. [Footnote: For an interesting sketch +of the more noteworthy early attempts to explain character, see the +chapter called "The Antecedents of the Study of Character and +Temperament," in Joseph Jastrow's _The Psychology of Conviction_.] +The analysis in its fundamentals is perhaps still as doubtful as it +was in the fifth century B. C. when Hippocrates formulated the +doctrine of the humors, distinguished the sanguine, the +melancholic, the choleric, and the phlegmatic dispositions, and +ascribed them to the blood, the black bile, the yellow bile, and the +phlegm. The latest theories, such as one finds them in Cannon, +[Footnote: _Bodily Changes in Pleasure, Pain and Anger_.] Adler, +[Footnote: _The Neurotic Constitution_.] Kempf, [Footnote: _The +Autonomic Functions and the Personality; Psychopathology. Cf_. also +Louis Berman: _The Glands Regulating Personality_.] appear to +follow much the same scent, from the outward behavior and the inner +consciousness to the physiology of the body. But in spite of an +immensely improved technique, no one would be likely to claim that +there are settled conclusions which enable us to set apart nature from +nurture, and abstract the native character from the acquired. It is +only in what Joseph Jastrow has called the slums of psychology that +the explanation of character is regarded as a fixed system to be +applied by phrenologists, palmists, fortune-tellers, mind-readers, and +a few political professors. There you will still find it asserted that +"the Chinese are fond of colors, and have their eyebrows much vaulted" +while "the heads of the Calmucks are depressed from above, but very +large laterally, about the organ which gives the inclination to +acquire; and this nation's propensity to steal, etc., is admitted." +[Footnote: _Jastrow, op. cit._, p. 156.] + +The modern psychologists are disposed to regard the outward behavior +of an adult as an equation between a number of variables, such as the +resistance of the environment, repressed cravings of several +maturities, and the manifest personality. [Footnote: Formulated by +Kempf, _Psychopathology_, p. 74, as follows: + +Manifest wishes } + over } +Later Repressed Wishes } + Over } opposed by the resistance of the +Adolescent Repressed Wishes } environment=Behavior + Over } +Preadolescent Repressed Wishes } +] They permit us to suppose, though I have not seen the notion +formulated, that the repression or control of cravings is fixed not in +relation to the whole person all the time, but more or less in respect +to his various selves. There are things he will not do as a patriot +that he will do when he is not thinking of himself as a patriot. No +doubt there are impulses, more or less incipient in childhood, that +are never exercised again in the whole of a man's life, except as they +enter obscurely and indirectly into combination with other impulses. +But even that is not certain, since repression is not irretrievable. +For just as psychoanalysis can bring to the surface a buried impulse, +so can social situations. [Footnote: _Cf._ the very interesting +book of Everett Dean Martin, _The Behavior of Crowds_. + +Also Hobbes, _Leviathan_, Part II, Ch. 25. "For the passions of +men, which asunder are moderate, as the heat of one brand, in an +assembly are like many brands, that inflame one another, especially +when they blow one another with orations...." + +LeBon, _The Crowd_, elaborates this observation of Hobbes's.] It +is only when our surroundings remain normal and placid, when what is +expected of us by those we meet is consistent, that we live without +knowledge of many of our dispositions. When the unexpected occurs, we +learn much about ourselves that we did not know. + +The selves, which we construct with the help of all who influence us, +prescribe which impulses, how emphasized, how directed, are +appropriate to certain typical situations for which we have learned +prepared attitudes. For a recognizable type of experience, there is a +character which controls the outward manifestations of our whole +being. Murderous hate is, for example, controlled in civil life. +Though you choke with rage, you must not display it as a parent, +child, employer, politician. You would not wish to display a +personality that exudes murderous hate. You frown upon it, and the +people around you also frown. But if a war breaks out, the chances are +that everybody you admire will begin to feel the justification of +killing and hating. At first the vent for these feelings is very +narrow. The selves which come to the front are those which are attuned +to a real love of country, the kind of feeling that you find in Rupert +Brooke, and in Sir Edward Grey's speech on August 3,1914, and in +President Wilson's address to Congress on April 2, 1917. The reality +of war is still abhorred, and what war actually means is learned but +gradually. For previous wars are only transfigured memories. In that +honeymoon phase, the realists of war rightly insist that the nation is +not yet awake, and reassure each other by saying: "Wait for the +casualty lists." Gradually the impulse to kill becomes the main +business, and all those characters which might modify it, +disintegrate. The impulse becomes central, is sanctified, and +gradually turns unmanageable. It seeks a vent not alone on the idea of +the enemy, which is all the enemy most people actually see during the +war, but upon all the persons and objects and ideas that have always +been hateful. Hatred of the enemy is legitimate. These other hatreds +have themselves legitimized by the crudest analogy, and by what, once +having cooled off, we recognize as the most far-fetched analogy. It +takes a long time to subdue so powerful an impulse once it goes loose. +And therefore, when the war is over in fact, it takes time and +struggle to regain self-control, and to deal with the problems of +peace in civilian character. + +Modern war, as Mr. Herbert Croly has said, is inherent in the +political structure of modern society, but outlawed by its ideals. For +the civilian population there exists no ideal code of conduct in war, +such as the soldier still possesses and chivalry once prescribed. The +civilians are without standards, except those that the best of them +manage to improvise. The only standards they possess make war an +accursed thing. Yet though the war may be a necessary one, no moral +training has prepared them for it. Only their higher selves have a +code and patterns, and when they have to act in what the higher +regards as a lower character profound disturbance results. + +The preparation of characters for all the situations in which men may +find themselves is one function of a moral education. Clearly then, it +depends for its success upon the sincerity and knowledge with which +the environment has been explored. For in a world falsely conceived, +our own characters are falsely conceived, and we misbehave. So the +moralist must choose: either he must offer a pattern of conduct for +every phase of life, however distasteful some of its phases may be, or +he must guarantee that his pupils will never be confronted by the +situations he disapproves. Either he must abolish war, or teach people +how to wage it with the greatest psychic economy; either he must +abolish the economic life of man and feed him with stardust and dew, +or he must investigate all the perplexities of economic life and offer +patterns of conduct which are applicable in a world where no man is +self-supporting. But that is just what the prevailing moral culture so +generally refuses to do. In its best aspects it is diffident at the +awful complication of the modern world. In its worst, it is just +cowardly. Now whether the moralists study economics and politics and +psychology, or whether the social scientists educate the moralists is +no great matter. Each generation will go unprepared into the modern +world, unless it has been taught to conceive the kind of personality +it will have to be among the issues it will most likely meet. + +4 + +Most of this the naive view of self-interest leaves out of account. It +forgets that self and interest are both conceived somehow, and that +for the most part they are conventionally conceived. The ordinary +doctrine of self-interest usually omits altogether the cognitive +function. So insistent is it on the fact that human beings finally +refer all things to themselves, that it does not stop to notice that +men's ideas of all things and of themselves are not instinctive. They +are acquired. + +Thus it may be true enough, as James Madison wrote in the tenth paper +of the Federalist, that "a landed interest, a manufacturing interest, +a mercantile interest, a moneyed interest, with many lesser interests, +grow up of necessity in civilized nations, and divide them into +different classes, actuated by different sentiments and views." But if +you examine the context of Madison's paper, you discover something +which I think throws light upon that view of instinctive fatalism, +called sometimes the economic interpretation of history. Madison was +arguing for the federal constitution, and "among the numerous +advantages of the union" he set forth "its tendency to break and +control the violence of faction." Faction was what worried Madison. +And the causes of faction he traced to "the nature of man," where +latent dispositions are "brought into different degrees of activity, +according to the different circumstances of civil society. A zeal for +different opinions concerning religion, concerning government and many +other points, as well of speculation as of practice; an attachment to +different leaders ambitiously contending for preeminence and power, or +to persons of other descriptions whose fortunes have been interesting +to the human passions, have, in turn, divided mankind into parties, +inflamed them with mutual animosity, and rendered them much more +disposed to vex and oppress each other, than to coöperate for their +common good. So strong is this propensity of mankind to fall into +mutual animosities, that where no substantial occasion presents +itself, the most frivolous and fanciful distinctions have been +sufficient to kindle their unfriendly passions and excite their most +violent conflicts. But the _most common_ and _durable_ source +of factions has been the various and unequal distribution of property." + +Madison's theory, therefore, is that the propensity to faction may be +kindled by religious or political opinions, by leaders, but most +commonly by the distribution of property. Yet note that Madison claims +only that men are divided by their relation to property. He does not +say that their property and their opinions are cause and effect, but +that differences of property are the causes of differences of opinion. +The pivotal word in Madison's argument is "different." From the +existence of differing economic situations you can tentatively infer a +probable difference of opinions, but you cannot infer what those +opinions will necessarily be. + +This reservation cuts radically into the claims of the theory as that +theory is usually held. That the reservation is necessary, the +enormous contradiction between dogma and practice among orthodox +socialists bears witness. They argue that the next stage in social +evolution is the inevitable result of the present stage. But in order +to produce that inevitable next stage they organize and agitate to +produce "class consciousness." Why, one asks, does not the economic +situation produce consciousness of class in everybody? It just +doesn't, that is all. And therefore the proud claim will not stand +that the socialist philosophy rests on prophetic insight into destiny. +It rests on an hypothesis about human nature. [Footnote: _Cf._ +Thorstein Veblen, "The Socialist Economics of Karl Marx and His +Followers," in _The Place of Science in Modern Civilization,_ +esp. pp. 413-418.] + +The socialist practice is based on a belief that if men are +economically situated in different ways, they can then be induced to +hold certain views. Undoubtedly they often come to believe, or can be +induced to believe different things, as they are, for example, +landlords or tenants, employees or employers, skilled or unskilled +laborers, wageworkers or salaried men, buyers or sellers, farmers or +middle-men, exporters or importers, creditors or debtors. Differences +of income make a profound difference in contact and opportunity. Men +who work at machines will tend, as Mr. Thorstein Veblen has so +brilliantly demonstrated, [Footnote: _The Theory of Business +Enterprise_.] to interpret experience differently from handicraftsmen +or traders. If this were all that the materialistic conception of politics +asserted, the theory would be an immensely valuable hypothesis that +every interpreter of opinion would have to use. But he would often +have to abandon the theory, and he would always have to be on +guard. For in trying to explain a certain public opinion, it is rarely +obvious which of a man's many social relations is effecting a particular +opinion. Does Smith's opinion arise from his problems as a landlord, +an importer, an owner of railway shares, or an employer? Does +Jones's opinion, Jones being a weaver in a textile mill, come from +the attitude of his boss, the competition of new immigrants, his wife's +grocery bills, or the ever present contract with the firm which is +selling him a Ford car and a house and lot on the instalment plan? +Without special inquiry you cannot tell. The economic determinist +cannot tell. + +A man's various economic contacts limit or enlarge the range of his +opinions. But which of the contacts, in what guise, on what theory, +the materialistic conception of politics cannot predict. It can +predict, with a high degree of probability, that if a man owns a +factory, his ownership will figure in those opinions which seem to +have some bearing on that factory. But how the function of being an +owner will figure, no economic determinist as such, can tell you. +There is no fixed set of opinions on any question that go with being +the owner of a factory, no views on labor, on property, on management, +let alone views on less immediate matters. The determinist can predict +that in ninety-nine cases out of a hundred the owner will resist +attempts to deprive him of ownership, or that he will favor +legislation which he thinks will increase his profits. But since there +is no magic in ownership which enables a business man to know what +laws will make him prosper, there is no chain of cause and effect +described in economic materialism which enables anyone to prophesy +whether the owner will take a long view or a short one, a competitive +or a cooperative. + +Did the theory have the validity which is so often claimed for it, it +would enable us to prophesy. We could analyze the economic interests +of a people, and deduce what the people was bound to do. Marx tried +that, and after a good guess about the trusts, went wholly wrong. The +first socialist experiment came, not as he predicted, out of the +culmination of capitalist development in the West, but out of the +collapse of a pre-capitalist system in the East. Why did he go wrong? +Why did his greatest disciple, Lenin, go wrong? Because the Marxians +thought that men's economic position would irresistibly produce a +clear conception of their economic interests. They thought they +themselves possessed that clear conception, and that what they knew +the rest of mankind would learn. The event has shown, not only that a +clear conception of interest does not arise automatically in everyone, +but that it did not arise even in Marx and Lenin themselves. After all +that Marx and Lenin have written, the social behavior of mankind is +still obscure. It ought not to be, if economic position alone +determined public opinion. Position ought, if their theory were +correct, not only to divide mankind into classes, but to supply each +class with a view of its interest and a coherent policy for obtaining +it. Yet nothing is more certain than that all classes of men are in +constant perplexity as to what their interests are. [Footnote: As a +matter of fact, when it came to the test, Lenin completely abandoned +the materialistic interpretation of politics. Had he held sincerely to +the Marxian formula when he seized power in 1917, he would have said +to himself: according to the teachings of Marx, socialism will develop +out of a mature capitalism... here am I, in control of a nation that +is only entering upon a capitalist development... it is true that I am +a socialist, but I am a scientific socialist... it follows that for +the present all idea of a socialist republic is out of the question... +we must advance capitalism in order that the evolution which Marx +predicted may take place. But Lenin did nothing of the sort. Instead +of waiting for evolution to evolve, he tried by will, force, and +education, to defy the historical process which his philosophy +assumed. + +Since this was written Lenin has abandoned communism on the ground +that Russia does not possess the necessary basis in a mature +capitalism. He now says that Russia must create capitalism, which will +create a proletariat, which will some day create communism. This is at +least consistent with Marxist dogma. But it shows how little +determinism there is in the opinions of a determinist.] + +This dissolves the impact of economic determinism. For if our economic +interests are made up of our variable concepts of those interests, +then as the master key to social processes the theory fails. That +theory assumes that men are capable of adopting only one version of +their interest, and that having adopted it, they move fatally to +realize it. It assumes the existence of a specific class interest. +That assumption is false. A class interest can be conceived largely or +narrowly, selfishly or unselfishly, in the light of no facts, some +facts, many facts, truth and error. And so collapses the Marxian +remedy for class conflicts. That remedy assumes that if all property +could be held in common, class differences would disappear. The +assumption is false. Property might well be held in common, and yet +not be conceived as a whole. The moment any group of people failed to +see communism in a communist manner, they would divide into classes on +the basis of what they saw. + +In respect to the existing social order Marxian socialism emphasizes +property conflict as the maker of opinion, in respect to the loosely +defined working class it ignores property conflict as the basis of +agitation, in respect to the future it imagines a society without +property conflict, and, therefore, without conflict of opinion. Now in +the existing social order there may be more instances where one man +must lose if another is to gain, than there would be under socialism, +but for every case where one must lose for another to gain, there are +endless cases where men simply imagine the conflict because they are +uneducated. And under socialism, though you removed every instance of +absolute conflict, the partial access of each man to the whole range +of facts would nevertheless create conflict. A socialist state will +not be able to dispense with education, morality, or liberal science, +though on strict materialistic grounds the communal ownership of +properties ought to make them superfluous. The communists in Russia +would not propagate their faith with such unflagging zeal if economic +determinism were alone determining the opinion of the Russian people. + +5 + +The socialist theory of human nature is, like the hedonistic calculus, +an example of false determinism. Both assume that the unlearned +dispositions fatally but intelligently produce a certain type of +behavior. The socialist believes that the dispositions pursue the +economic interest of a class; the hedonist believes that they pursue +pleasure and avoid pain. Both theories rest on a naive view of +instinct, a view, defined by James, [Footnote: _Principles of +Psychology_, Vol. II, p. 383.] though radically qualified by him, +as "the faculty of acting in such a way as to produce certain ends, +without foresight of the ends and without previous education in the +performance." + +It is doubtful whether instinctive action of this sort figures at all +in the social life of mankind. For as James pointed out: [Footnote: +_Op. cit._, Vol. II, p. 390.] "every instinctive act in an animal +with memory must cease to be 'blind' after being once repeated." +Whatever the equipment at birth, the innate dispositions are from +earliest infancy immersed in experience which determines what shall +excite them as stimulus. "They become capable," as Mr. McDougall +says, [Footnote: Introduction to _Social Psychology_, Fourth +Edition, pp. 31-32.] "of being initiated, not only by the perception +of objects of the kind which directly excite the innate disposition, +the natural or native excitants of the instinct, but also by ideas of +such objects, and by perceptions and by ideas of objects of other +kinds." [Footnote: "Most definitions of instincts and instinctive +actions take account only of their conative aspects... and it is a +common mistake to ignore the cognitive and affective aspects of the +instinctive mental process." Footnote _op. cit._, p. 29.] + +It is only the "central part of the disposition" [Footnote: p. 34.] +says Mr. McDougall further, "that retains its specific character and +remains common to all individuals and all situations in which the +instinct is excited." The cognitive processes, and the actual bodily +movements by which the instinct achieves its end may be indefinitely +complicated. In other words, man has an instinct of fear, but what he +will fear and how he will try to escape, is determined not from birth, +but by experience. + +If it were not for this variability, it would be difficult to conceive +the inordinate variety of human nature. But when you consider that all +the important tendencies of the creature, his appetites, his loves, +his hates, his curiosity, his sexual cravings, his fears, and +pugnacity, are freely attachable to all sorts of objects as stimulus, +and to all kinds of objects as gratification, the complexity of human +nature is not so inconceivable. And when you think that each new +generation is the casual victim of the way a previous generation was +conditioned, as well as the inheritor of the environment that +resulted, the possible combinations and permutations are enormous. + +There is no prima facie case then for supposing that because persons +crave some particular thing, or behave in some particular way, human +nature is fatally constituted to crave that and act thus. The craving +and the action are both learned, and in another generation might be +learned differently. Analytic psychology and social history unite in +supporting this conclusion. Psychology indicates how essentially +casual is the nexus between the particular stimulus and the particular +response. Anthropology in the widest sense reinforces the view by +demonstrating that the things which have excited men's passions, and +the means which they have used to realize them, differ endlessly from +age to age and from place to place. + +Men pursue their interest. But how they shall pursue it is not fatally +determined, and, therefore, within whatever limits of time this planet +will continue to support human life, man can set no term upon the +creative energies of men. He can issue no doom of automatism. He can +say, if he must, that for his life there will be no changes which he +can recognize as good. But in saying that he will be confining his +life to what he can see with his eye, rejecting what he might see with +his mind; he will be taking as the measure of good a measure which is +only the one he happens to possess. He can find no ground for +abandoning his highest hopes and relaxing his conscious effort unless +he chooses to regard the unknown as the unknowable, unless he elects +to believe that what no one knows no one will know, and that what +someone has not yet learned no one will ever be able to teach. + + + + +PART V + +THE MAKING OF A COMMON WILL + +CHAPTER 13. THE TRANSFER OF INTEREST + " 14. YES OR NO + " 15. LEADERS AND THE RANK AND FILE + + + + +CHAPTER XIII + +THE TRANSFER OF INTEREST + +This goes to show that there are many variables in each man's +impressions of the invisible world. The points of contact vary, the +stereotyped expectations vary, the interest enlisted varies most +subtly of all. The living impressions of a large number of people are +to an immeasurable degree personal in each of them, and unmanageably +complex in the mass. How, then, is any practical relationship +established between what is in people's heads and what is out there +beyond their ken in the environment? How in the language of democratic +theory, do great numbers of people feeling each so privately about so +abstract a picture, develop any common will? How does a simple and +constant idea emerge from this complex of variables? How are those +things known as the Will of the People, or the National Purpose, or +Public Opinion crystallized out of such fleeting and casual imagery? + +That there is a real difficulty here was shown by an angry tilt in the +spring of 1921 between the American Ambassador to England and a very +large number of other Americans. Mr. Harvey, speaking at a British +dinner table, had assured the world without the least sign of +hesitancy what were the motives of Americans in 1917. [Footnote: _New +York Times_, May 20, 1921.] As he described them, they were not the +motives which President Wilson had insisted upon when _he_ +enunciated the American mind. Now, of course, neither Mr. Harvey nor +Mr. Wilson, nor the critics and friends of either, nor any one else, +can know quantitatively and qualitatively what went on in thirty or +forty million adult minds. But what everybody knows is that a war was +fought and won by a multitude of efforts, stimulated, no one knows in +what proportion, by the motives of Wilson and the motives of Harvey +and all kinds of hybrids of the two. People enlisted and fought, +worked, paid taxes, sacrificed to a common end, and yet no one can +begin to say exactly what moved each person to do each thing that he +did. It is no use, then, Mr. Harvey telling a soldier who thought this +was a war to end war that the soldier did not think any such thing. +The soldier who thought that _thought that_. And Mr. Harvey, who +thought something else, thought _something else_. + +In the same speech Mr. Harvey formulated with equal clarity what the +voters of 1920 had in their minds. That is a rash thing to do, and, if +you simply assume that all who voted your ticket voted as you did, +then it is a disingenuous thing to do. The count shows that sixteen +millions voted Republican, and nine millions Democratic. They voted, +says Mr. Harvey, for and against the League of Nations, and in support +of this claim, he can point to Mr. Wilson's request for a referendum, +and to the undeniable fact that the Democratic party and Mr. Cox +insisted that the League was the issue. But then, saying that the +League was the issue did not make the League the issue, and by +counting the votes on election day you do not know the real division +of opinion about the League. There were, for example, nine million +Democrats. Are you entitled to believe that all of them are staunch +supporters of the League? Certainly you are not. For your knowledge of +American politics tells you that many of the millions voted, as they +always do, to maintain the existing social system in the South, and +that whatever their views on the League, they did not vote to express +their views. Those who wanted the League were no doubt pleased that +the Democratic party wanted it too. Those who disliked the League may +have held their noses as they voted. But both groups of Southerners +voted the same ticket. + +Were the Republicans more unanimous? Anybody can pick Republican +voters enough out of his circle of friends to cover the whole gamut of +opinion from the irreconcilability of Senators Johnson and Knox to the +advocacy of Secretary Hoover and Chief Justice Taft. No one can say +definitely how many people felt in any particular way about the +League, nor how many people let their feelings on that subject +determine their vote. When there are only two ways of expressing a +hundred varieties of feeling, there is no certain way of knowing what +the decisive combination was. Senator Borah found in the Republican +ticket a reason for voting Republican, but so did President Lowell. +The Republican majority was composed of men and women who thought a +Republican victory would kill the League, plus those who thought it +the most practical way to secure the League, plus those who thought it +the surest way offered to obtain an amended League. All these voters +were inextricably entangled with their own desire, or the desire of +other voters to improve business, or put labor in its place, or to +punish the Democrats for going to war, or to punish them for not +having gone sooner, or to get rid of Mr. Burleson, or to improve the +price of wheat, or to lower taxes, or to stop Mr. Daniels from +outbuilding the world, or to help Mr. Harding do the same thing. + +And yet a sort of decision emerged; Mr. Harding moved into the White +House. For the least common denominator of all the votes was that the +Democrats should go and the Republicans come in. That was the only +factor remaining after all the contradictions had cancelled each other +out. But that factor was enough to alter policy for four years. The +precise reasons why change was desired on that November day in 1920 +are not recorded, not even in the memories of the individual voters. +The reasons are not fixed. They grow and change and melt into other +reasons, so that the public opinions Mr. Harding has to deal with are +not the opinions that elected him. That there is no inevitable +connection between an assortment of opinions and a particular line of +action everyone saw in 1916. Elected apparently on the cry that he +kept us out of war, Mr. Wilson within five months led the country into +war. + +The working of the popular will, therefore, has always called for +explanation. Those who have been most impressed by its erratic working +have found a prophet in M. LeBon, and have welcomed generalizations +about what Sir Robert Peel called "that great compound of folly, +weakness, prejudice, wrong feeling, right feeling, obstinacy and +newspaper paragraphs which is called public opinion." Others have +concluded that since out of drift and incoherence, settled aims do +appear, there must be a mysterious contrivance at work somewhere over +and above the inhabitants of a nation. They invoke a collective soul, +a national mind, a spirit of the age which imposes order upon random +opinion. An oversoul seems to be needed, for the emotions and ideas in +the members of a group do not disclose anything so simple and so +crystalline as the formula which those same individuals will accept as +a true statement of their Public Opinion. + +2 + +But the facts can, I think, be explained more convincingly without the +help of the oversoul in any of its disguises. After all, the art of +inducing all sorts of people who think differently to vote alike is +practiced in every political campaign. In 1916, for example, the +Republican candidate had to produce Republican votes out of many +different kinds of Republicans. Let us look at Mr. Hughes' first +speech after accepting the nomination. [Footnote: Delivered at Carnegie +Hall, New York City, July 31, 1916.] The context is still clear enough +in our minds to obviate much explanation; yet the issues are no longer +contentious. The candidate was a man of unusually plain speech, who +had been out of politics for several years and was not personally +committed on the issues of the recent past. He had, moreover, none of +that wizardry which popular leaders like Roosevelt, Wilson, or Lloyd +George possess, none of that histrionic gift by which such men +impersonate the feelings of their followers. From that aspect of +politics he was by temperament and by training remote. But yet he knew +by calculation what the politician's technic is. He was one of those +people who know just how to do a thing, but who can not quite do it +themselves. They are often better teachers than the virtuoso to whom +the art is so much second nature that he himself does not know how he +does it. The statement that those who can, do; those who cannot, +teach, is not nearly so much of a reflection on the teacher as it +sounds. + +Mr. Hughes knew the occasion was momentous, and he had prepared his +manuscript carefully. In a box sat Theodore Roosevelt just back from +Missouri. All over the house sat the veterans of Armageddon in various +stages of doubt and dismay. On the platform and in the other boxes the +ex-whited sepulchres and ex-second-story men of 1912 were to be seen, +obviously in the best of health and in a melting mood. Out beyond the +hall there were powerful pro-Germans and powerful pro-Allies; a war +party in the East and in the big cities; a peace party in the middle +and far West. There was strong feeling about Mexico. Mr. Hughes had to +form a majority against the Democrats out of people divided into all +sorts of combinations on Taft vs. Roosevelt, pro-Germans vs. +pro-Allies, war vs. neutrality, Mexican intervention vs. +non-intervention. + +About the morality or the wisdom of the affair we are, of course, not +concerned here. Our only interest is in the method by which a leader +of heterogeneous opinion goes about the business of securing a +homogeneous vote. + +"This _representative_ gathering is a happy augury. It means the +strength of _reunion._ It means that the party of _Lincoln_ +is restored...." + +The italicized words are binders: _Lincoln_ in such a speech has +of course, no relation to Abraham Lincoln. It is merely a stereotype +by which the piety which surrounds that name can be transferred to the +Republican candidate who now stands in his shoes. Lincoln reminds the +Republicans, Bull Moose and Old Guard, that before the schism they had +a common history. About the schism no one can afford to speak. But it +is there, as yet unhealed. + +The speaker must heal it. Now the schism of 1912 had arisen over +domestic questions; the reunion of 1916 was, as Mr. Roosevelt had +declared, to be based on a common indignation against Mr. Wilson's +conduct of international affairs. But international affairs were also +a dangerous source of conflict. It was necessary to find an opening +subject which would not only ignore 1912 but would avoid also the +explosive conflicts of 1916. The speaker skilfully selected the spoils +system in diplomatic appointments. "Deserving Democrats" was a +discrediting phrase, and Mr. Hughes at once evokes it. The record +being indefensible, there is no hesitation in the vigor of the attack. +Logically it was an ideal introduction to a common mood. + +Mr. Hughes then turns to Mexico, beginning with an historical review. +He had to consider the general sentiment that affairs were going badly +in Mexico; also, a no less general sentiment that war should be +avoided; and two powerful currents of opinion, one of which said +President Wilson was right in not recognizing Huerta, the other which +preferred Huerta to Carranza, and intervention to both. Huerta was the +first sore spot in the record... + +"He was certainly in fact the head of the Government in Mexico." + +But the moralists who regarded Huerta as a drunken murderer had to be +placated. + +"Whether or not he should be recognized was a question to be +determined in the exercise of a sound discretion, but according to +correct principles." + +So instead of saying that Huerta should have been recognized, the +candidate says that correct principles ought to be applied. Everybody +believes in correct principles, and everybody, of course, believes he +possesses them. To blur the issue still further President Wilson's +policy is described as "intervention." It was that in law, perhaps, +but not in the sense then currently meant by the word. By stretching +the word to cover what Mr. Wilson had done, as well as what the real +interventionists wanted, the issue between the two factions was to be +repressed. + +Having got by the two explosive points "_Huerta_" and +"_intervention_" by letting the words mean all things to all men, +the speech passes for a while to safer ground. The candidate tells the +story of Tampico, Vera Cruz, Villa, Santa Ysabel, Columbus and +Carrizal. Mr. Hughes is specific, either because the facts as known +from the newspapers are irritating, or because the true explanation +is, as for example in regard to Tampico, too complicated. No contrary +passions could be aroused by such a record. But at the end the +candidate had to take a position. His audience expected it. The +indictment was Mr. Roosevelt's. Would Mr. Hughes adopt his remedy, +intervention? + +"The nation has no policy of aggression toward Mexico. We have no +desire for any part of her territory. We wish her to have peace, +stability and prosperity. We should be ready to aid her in binding up +her wounds, in relieving her from starvation and distress, in giving +her in every practicable way the benefits of our disinterested +friendship. The conduct of this administration has created +difficulties which we shall have to surmount.... _We shall have to +adopt a new policy,_ a policy of _firmness_ and consistency +through which alone we can promote an enduring _friendship._" + +The theme friendship is for the non-interventionists, the theme "new +policy" and "firmness" is for the interventionists. On the +non-contentious record, the detail is overwhelming; on the issue +everything is cloudy. + +Concerning the European war Mr. Hughes employed an ingenious formula: + +"I stand for the unflinching maintenance of _all_ American rights +on land and sea." + +In order to understand the force of that statement at the time it was +spoken, we must remember how each faction during the period of +neutrality believed that the nations it opposed in Europe were alone +violating American rights. Mr. Hughes seemed to say to the pro-Allies: +I would have coerced Germany. But the pro-Germans had been insisting +that British sea power was violating most of our rights. The formula +covers two diametrically opposed purposes by the symbolic phrase +"American rights." + +But there was the Lusitania. Like the 1912 schism, it was an +invincible obstacle to harmony. + +"... I am confident that there would have been no destruction of +American lives by the sinking of the Lusitania." + +Thus, what cannot be compromised must be obliterated, when there is a +question on which we cannot all hope to get together, let us pretend +that it does not exist. About the future of American relations with +Europe Mr. Hughes was silent. Nothing he could say would possibly +please the two irreconcilable factions for whose support he was +bidding. + +It is hardly necessary to say that Mr. Hughes did not invent this +technic and did not employ it with the utmost success. But he +illustrated how a public opinion constituted out of divergent opinions +is clouded; how its meaning approaches the neutral tint formed out of +the blending of many colors. Where superficial harmony is the aim and +conflict the fact, obscurantism in a public appeal is the usual +result. Almost always vagueness at a crucial point in public debate is +a symptom of cross-purposes. + +3 + +But how is it that a vague idea so often has the power to unite deeply +felt opinions? These opinions, we recall, however deeply they may be +felt, are not in continual and pungent contact with the facts they +profess to treat. On the unseen environment, Mexico, the European war, +our grip is slight though our feeling may be intense. The original +pictures and words which aroused it have not anything like the force +of the feeling itself. The account of what has happened out of sight +and hearing in a place where we have never been, has not and never can +have, except briefly as in a dream or fantasy, all the dimensions of +reality. But it can arouse all, and sometimes even more emotion than +the reality. For the trigger can be pulled by more than one stimulus. + +The stimulus which originally pulled the trigger may have been a +series of pictures in the mind aroused by printed or spoken words. +These pictures fade and are hard to keep steady; their contours and +their pulse fluctuate. Gradually the process sets in of knowing what +you feel without being entirely certain why you feel it. The fading +pictures are displaced by other pictures, and then by names or +symbols. But the emotion goes on, capable now of being aroused by the +substituted images and names. Even in severe thinking these +substitutions take place, for if a man is trying to compare two +complicated situations, he soon finds exhausting the attempt to hold +both fully in mind in all their detail. He employs a shorthand of +names and signs and samples. He has to do this if he is to advance at +all, because he cannot carry the whole baggage in every phrase through +every step he takes. But if he forgets that he has substituted and +simplified, he soon lapses into verbalism, and begins to talk about +names regardless of objects. And then he has no way of knowing when +the name divorced from its first thing is carrying on a misalliance +with some other thing. It is more difficult still to guard against +changelings in casual politics. + +For by what is known to psychologists as conditioned response, an +emotion is not attached merely to one idea. There are no end of things +which can arouse the emotion, and no end of things which can satisfy +it. This is particularly true where the stimulus is only dimly and +indirectly perceived, and where the objective is likewise indirect. +For you can associate an emotion, say fear, first with something +immediately dangerous, then with the idea of that thing, then with +something similar to that idea, and so on and on. The whole structure +of human culture is in one respect an elaboration of the stimuli and +responses of which the original emotional capacities remain a fairly +fixed center. No doubt the quality of emotion has changed in the +course of history, but with nothing like the speed, or elaboration, +that has characterized the conditioning of it. + +People differ widely in their susceptibility to ideas. There are some +in whom the idea of a starving child in Russia is practically as vivid +as a starving child within sight. There are others who are almost +incapable of being excited by a distant idea. There are many +gradations between. And there are people who are insensitive to facts, +and aroused only by ideas. But though the emotion is aroused by the +idea, we are unable to satisfy the emotion by acting ourselves upon +the scene itself. The idea of the starving Russian child evokes a +desire to feed the child. But the person so aroused cannot feed it. He +can only give money to an impersonal organization, or to a +personification which he calls Mr. Hoover. His money does not reach +that child. It goes to a general pool from which a mass of children +are fed. And so just as the idea is second hand, so are the effects of +the action second hand. The cognition is indirect, the conation is +indirect, only the effect is immediate. Of the three parts of the +process, the stimulus comes from somewhere out of sight, the response +reaches somewhere out of sight, only the emotion exists entirely +within the person. Of the child's hunger he has only an idea, of the +child's relief he has only an idea, but of his own desire to help he +has a real experience. It is the central fact of the business, the +emotion within himself, which is first hand. + +Within limits that vary, the emotion is transferable both as regards +stimulus and response. Therefore, if among a number of people, +possessing various tendencies to respond, you can find a stimulus +which will arouse the same emotion in many of them, you can substitute +it for the original stimuli. If, for example, one man dislikes the +League, another hates Mr. Wilson, and a third fears labor, you may be +able to unite them if you can find some symbol which is the antithesis +of what they all hate. Suppose that symbol is Americanism. The first +man may read it as meaning the preservation of American isolation, or +as he may call it, independence; the second as the rejection of a +politician who clashes with his idea of what an American president +should be, the third as a call to resist revolution. The symbol in +itself signifies literally no one thing in particular, but it can be +associated with almost anything. And because of that it can become the +common bond of common feelings, even though those feelings were +originally attached to disparate ideas. + +When political parties or newspapers declare for Americanism, +Progressivism, Law and Order, Justice, Humanity, they hope to +amalgamate the emotion of conflicting factions which would surely +divide, if, instead of these symbols, they were invited to discuss a +specific program. For when a coalition around the symbol has been +effected, feeling flows toward conformity under the symbol rather than +toward critical scrutiny of the measures. It is, I think, convenient +and technically correct to call multiple phrases like these symbolic. +They do not stand for specific ideas, but for a sort of truce or +junction between ideas. They are like a strategic railroad center +where many roads converge regardless of their ultimate origin or their +ultimate destination. But he who captures the symbols by which public +feeling is for the moment contained, controls by that much the +approaches of public policy. And as long as a particular symbol has +the power of coalition, ambitious factions will fight for possession. +Think, for example, of Lincoln's name or of Roosevelt's. A leader or +an interest that can make itself master of current symbols is master +of the current situation. There are limits, of course. Too violent +abuse of the actualities which groups of people think the symbol +represents, or too great resistance in the name of that symbol to new +purposes, will, so to speak, burst the symbol. In this manner, during +the year 1917, the imposing symbol of Holy Russia and the Little +Father burst under the impact of suffering and defeat. + +4 + +The tremendous consequences of Russia's collapse were felt on all the +fronts and among all the peoples. They led directly to a striking +experiment in the crystallization of a common opinion out of the +varieties of opinion churned up by the war. The Fourteen Points were +addressed to all the governments, allied, enemy, neutral, and to all +the peoples. They were an attempt to knit together the chief +imponderables of a world war. Necessarily this was a new departure, +because this was the first great war in which all the deciding +elements of mankind could be brought to think about the same ideas, or +at least about the same names for ideas, simultaneously. Without +cable, radio, telegraph, and daily press, the experiment of the +Fourteen Points would have been impossible. It was an attempt to +exploit the modern machinery of communication to start the return to a +"common consciousness" throughout the world. + +But first we must examine some of the circumstances as they presented +themselves at the end of 1917. For in the form which the document +finally assumed, all these considerations are somehow represented. +During the summer and autumn a series of events had occurred which +profoundly affected the temper of the people and the course of the +war. In July the Russians had made a last offensive, had been +disastrously beaten, and the process of demoralization which led to +the Bolshevik revolution of November had begun. Somewhat earlier the +French had suffered a severe and almost disastrous defeat in Champagne +which produced mutinies in the army and a defeatist agitation among +the civilians. England was suffering from the effects of the submarine +raids, from the terrible losses of the Flanders battles, and in +November at Cambrai the British armies met a reverse that appalled the +troops at the front and the leaders at home. Extreme war weariness +pervaded the whole of western Europe. + +In effect, the agony and disappointment had jarred loose men's +concentration on the accepted version of the war. Their interests were +no longer held by the ordinary official pronouncements, and their +attention began to wander, fixing now upon their own suffering, now +upon their party and class purposes, now upon general resentments +against the governments. That more or less perfect organization of +perception by official propaganda, of interest and attention by the +stimuli of hope, fear, and hatred, which is called morale, was by way +of breaking down. The minds of men everywhere began to search for new +attachments that promised relief. + +Suddenly they beheld a tremendous drama. On the Eastern front there +was a Christmas truce, an end of slaughter, an end of noise, a promise +of peace. At Brest-Litovsk the dream of all simple people had come to +life: it was possible to negotiate, there was some other way to end +the ordeal than by matching lives with the enemy. Timidly, but with +rapt attention, people began to turn to the East. Why not, they asked? +What is it all for? Do the politicians know what they are doing? Are +we really fighting for what they say? Is it possible, perhaps, to +secure it without fighting? Under the ban of the censorship, little of +this was allowed to show itself in print, but, when Lord Lansdowne +spoke, there was a response from the heart. The earlier symbols of the +war had become hackneyed, and had lost their power to unify. Beneath +the surface a wide schism was opening up in each Allied country. + +Something similar was happening in Central Europe. There too the +original impulse of the war was weakened; the union sacrée was broken. +The vertical cleavages along the battle front were cut across by +horizontal divisions running in all kinds of unforeseeable ways. The +moral crisis of the war had arrived before the military decision was +in sight. All this President Wilson and his advisers realized. They +had not, of course, a perfect knowledge of the situation, but what I +have sketched they knew. + +They knew also that the Allied Governments were bound by a series of +engagements that in letter and in spirit ran counter to the popular +conception of what the war was about. The resolutions of the Paris +Economic Conference were, of course, public property, and the network +of secret treaties had been published by the Bolsheviks in November of +1917. [Footnote: President Wilson stated at his conference with the +Senators that he had never heard of these treaties until he reached +Paris. That statement is perplexing. The Fourteen Points, as the text +shows, could not have been formulated without a knowledge of the +secret treaties. The substance of those treaties was before the +President when he and Colonel House prepared the final published text +of the Fourteen Points.] Their terms were only vaguely known to the +peoples, but it was definitely believed that they did not comport with +the idealistic slogan of self-determination, no annexations and no +indemnities. Popular questioning took the form of asking how many +thousand English lives Alsace-Lorraine or Dalmatia were worth, how +many French lives Poland or Mesopotamia were worth. Nor was such +questioning entirely unknown in America. The whole Allied cause had +been put on the defensive by the refusal to participate at +Brest-Litovsk. + +Here was a highly sensitive state of mind which no competent leader +could fail to consider. The ideal response would have been joint +action by the Allies. That was found to be impossible when it was +considered at the Interallied Conference of October. But by December +the pressure had become so great that Mr. George and Mr. Wilson were +moved independently to make some response. The form selected by the +President was a statement of peace terms under fourteen heads. The +numbering of them was an artifice to secure precision, and to create +at once the impression that here was a business-like document. The +idea of stating "peace terms" instead of "war aims" arose from the +necessity of establishing a genuine alternative to the Brest-Litovsk +negotiations. They were intended to compete for attention by +substituting for the spectacle of Russo-German parleys the much +grander spectacle of a public world-wide debate. + +Having enlisted the interest of the world, it was necessary to hold +that interest unified and flexible for all the different possibilities +which the situation contained. The terms had to be such that the +majority among the Allies would regard them as worth while. They had +to meet the national aspirations of each people, and yet to limit +those aspirations so that no one nation would regard itself as a +catspaw for another. The terms had to satisfy official interests so as +not to provoke official disunion, and yet they had to meet popular +conceptions so as to prevent the spread of demoralization. They had, +in short, to preserve and confirm Allied unity in case the war was to +go on. + +But they had also to be the terms of a possible peace, so that in case +the German center and left were ripe for agitation, they would have a +text with which to smite the governing class. The terms had, +therefore, to push the Allied governors nearer to their people, drive +the German governors away from their people, and establish a line of +common understanding between the Allies, the non-official Germans, and +the subject peoples of Austria-Hungary. The Fourteen Points were a +daring attempt to raise a standard to which almost everyone might +repair. If a sufficient number of the enemy people were ready there +would be peace; if not, then the Allies would be better prepared to +sustain the shock of war. + +All these considerations entered into the making of the Fourteen +Points. No one man may have had them all in mind, but all the men +concerned had some of them in mind. Against this background let us +examine certain aspects of the document. The first five points and the +fourteenth deal with "open diplomacy," "freedom of the seas," "equal +trade opportunities," "reduction of armaments," no imperialist +annexation of colonies, and the League of Nations. They might be +described as a statement of the popular generalizations in which +everyone at that time professed to believe. But number three is more +specific. It was aimed consciously and directly at the resolutions of +the Paris Economic Conference, and was meant to relieve the German +people of their fear of suffocation. + +Number six is the first point dealing with a particular nation. It was +intended as a reply to Russian suspicion of the Allies, and the +eloquence of its promises was attuned to the drama of Brest-Litovsk. +Number seven deals with Belgium, and is as unqualified in form and +purpose as was the conviction of practically the whole world, +including very large sections of Central Europe. Over number eight we +must pause. It begins with an absolute demand for evacuation and +restoration of French territory, and then passes on to the question of +Alsace-Lorraine. The phrasing of this clause most perfectly +illustrates the character of a public statement which must condense a +vast complex of interests in a few words. "And the wrong done to +France by Prussia in 1871 in the matter of Alsace-Lorraine, which has +unsettled the peace of the world for nearly fifty years, should be +righted. ..." Every word here was chosen with meticulous care. The +wrong done should be righted; why not say that Alsace-Lorraine should +be restored? It was not said, because it was not certain that all of +the French _at that time_ would fight on indefinitely for +reannexation if they were offered a plebiscite; and because it was +even less certain whether the English and Italians would fight on. The +formula had, therefore, to cover both contingencies. The word +"righted" guaranteed satisfaction to France, but did not read as a +commitment to simple annexation. But why speak of the wrong done by +_Prussia_ in _1871_? The word Prussia was, of course, intended +to remind the South Germans that Alsace-Lorraine belonged not to +them but to Prussia. Why speak of peace unsettled for "fifty years," +and why the use of "1871"? In the first place, what the French and +the rest of the world remembered was 1871. That was the nodal +point of their grievance. But the formulators of the Fourteen Points +knew that French officialdom planned for more than the Alsace-Lorraine +of 1871. The secret memoranda that had passed between the Czar's +ministers and French officials in 1916 covered the annexation of the +Saar Valley and some sort of dismemberment of the Rhineland. It was +planned to include the Saar Valley under the term "Alsace-Lorraine" +because it had been part of Alsace-Lorraine in 1814, though it had +been detached in 1815, and was no part of the territory at the close +of the Franco-Prussian war. The official French formula for annexing +the Saar was to subsume it under "Alsace-Lorraine" meaning the +Alsace-Lorraine of 1814-1815. By insistence on "1871" the President +was really defining the ultimate boundary between Germany and France, +was adverting to the secret treaty, and was casting it aside. + +Number nine, a little less subtly, does the same thing in respect to +Italy. "Clearly recognizable lines of nationality" are exactly what +the lines of the Treaty of London were not. Those lines were partly +strategic, partly economic, partly imperialistic, partly ethnic. The +only part of them that could possibly procure allied sympathy was that +which would recover the genuine Italia Irredenta. All the rest, as +everyone who was informed knew, merely delayed the impending Jugoslav +revolt. + +5 + +It would be a mistake to suppose that the apparently unanimous +enthusiasm which greeted the Fourteen Points represented agreement on +a program. Everyone seemed to find something that he liked and +stressed this aspect and that detail. But no one risked a discussion. +The phrases, so pregnant with the underlying conflicts of the +civilized world, were accepted. They stood for opposing ideas, but +they evoked a common emotion. And to that extent they played a part in +rallying the western peoples for the desperate ten months of war which +they had still to endure. + +As long as the Fourteen Points dealt with that hazy and happy future +when the agony was to be over, the real conflicts of interpretation +were not made manifest. They were plans for the settlement of a wholly +invisible environment, and because these plans inspired all groups +each with its own private hope, all hopes ran together as a public +hope. For harmonization, as we saw in Mr. Hughes's speech, is a +hierarchy of symbols. As you ascend the hierarchy in order to include +more and more factions you may for a time preserve the emotional +connection though you lose the intellectual. But even the emotion +becomes thinner. As you go further away from experience, you go higher +into generalization or subtlety. As you go up in the balloon you throw +more and more concrete objects overboard, and when you have reached +the top with some phrase like the Rights of Humanity or the World Made +Safe for Democracy, you see far and wide, but you see very little. Yet +the people whose emotions are entrained do not remain passive. As the +public appeal becomes more and more all things to all men, as the +emotion is stirred while the meaning is dispersed, their very private +meanings are given a universal application. Whatever you want badly is +the Rights of Humanity. For the phrase, ever more vacant, capable of +meaning almost anything, soon comes to mean pretty nearly everything. +Mr. Wilson's phrases were understood in endlessly different ways in +every corner of the earth. No document negotiated and made of public +record existed to correct the confusion. [Footnote: The American +interpretation of the fourteen points was explained to the allied +statesmen just before the armistice.] And so, when the day of +settlement came, everybody expected everything. The European authors +of the treaty had a large choice, and they chose to realize those +expectations which were held by those of their countrymen who wielded +the most power at home. + +They came down the hierarchy from the Rights of Humanity to the Rights +of France, Britain and Italy. They did not abandon the use of symbols. +They abandoned only those which after the war had no permanent roots +in the imagination of their constituents. They preserved the unity of +France by the use of symbolism, but they would not risk anything for +the unity of Europe. The symbol France was deeply attached, the symbol +Europe had only a recent history. Nevertheless the distinction between +an omnibus like Europe and a symbol like France is not sharp. The +history of states and empires reveals times when the scope of the +unifying idea increases and also times when it shrinks. One cannot say +that men have moved consistently from smaller loyalties to larger +ones, because the facts will not bear out the claim. The Roman Empire +and the Holy Roman Empire bellied out further than those national +unifications in the Nineteenth Century from which believers in a World +State argue by analogy. Nevertheless, it is probably true that the +real integration has increased regardless of the temporary inflation +and deflation of empires. + +6 + +Such a real integration has undoubtedly occurred in American history. +In the decade before 1789 most men, it seems, felt that their state +and their community were real, but that the confederation of states +was unreal. The idea of their state, its flag, its most conspicuous +leaders, or whatever it was that represented Massachusetts, or +Virginia, were genuine symbols. That is to say, they were fed by +actual experiences from childhood, occupation, residence, and the +like. The span of men's experience had rarely traversed the imaginary +boundaries of their states. The word Virginian was related to pretty +nearly everything that most Virginians had ever known or felt. It was +the most extensive political idea which had genuine contact with their +experience. + +Their experience, not their needs. For their needs arose out of their +real environment, which in those days was at least as large as the +thirteen colonies. They needed a common defense. They needed a +financial and economic regime as extensive as the Confederation. But +as long as the pseudo-environment of the state encompassed them, the +state symbols exhausted their political interest. An interstate idea, +like the Confederation, represented a powerless abstraction. It was an +omnibus, rather than a symbol, and the harmony among divergent groups, +which the omnibus creates, is transient. + +I have said that the idea of confederation was a powerless +abstraction. Yet the need of unity existed in the decade before the +Constitution was adopted. The need existed, in the sense that affairs +were askew unless the need of unity was taken into account. Gradually +certain classes in each colony began to break through the state +experience. Their personal interests led across the state lines to +interstate experiences, and gradually there was constructed in their +minds a picture of the American environment which was truly national +in scope. For them the idea of federation became a true symbol, and +ceased to be an omnibus. The most imaginative of these men was +Alexander Hamilton. It happened that he had no primitive attachment to +any one state, for he was born in the West Indies, and had, from the +very beginning of his active life, been associated with the common +interests of all the states. Thus to most men of the time the question +of whether the capital should be in Virginia or in Philadelphia was of +enormous importance, because they were locally minded. To Hamilton +this question was of no emotional consequence; what he wanted was the +assumption of the state debts because they would further nationalize +the proposed union. So he gladly traded the site of the capitol for +two necessary votes from men who represented the Potomac district. To +Hamilton the Union was a symbol that represented all his interests and +his whole experience; to White and Lee from the Potomac, the symbol of +their province was the highest political entity they served, and they +served it though they hated to pay the price. They agreed, says +Jefferson, to change their votes, "White with a revulsion of stomach +almost convulsive." [Footnote: _Works,_ Vol. IX, p. 87. Cited by +Beard, _Economic Origins of Jeffersonian Democracy,_ p. 172.] + +In the crystallizing of a common will, there is always an Alexander +Hamilton at work. + + + + +CHAPTER XIV + +YES OR NO + +1 + +Symbols are often so useful and so mysteriously powerful that the word +itself exhales a magical glamor. In thinking about symbols it is +tempting to treat them as if they possessed independent energy. Yet no +end of symbols which once provoked ecstasy have quite ceased to affect +anybody. The museums and the books of folklore are full of dead +emblems and incantations, since there is no power in the symbol, +except that which it acquires by association in the human mind. The +symbols that have lost their power, and the symbols incessantly +suggested which fail to take root, remind us that if we were patient +enough to study in detail the circulation of a symbol, we should +behold an entirely secular history. + +In the Hughes campaign speech, in the Fourteen Points, in Hamilton's +project, symbols are employed. But they are employed by somebody at a +particular moment. The words themselves do not crystallize random +feeling. The words must be spoken by people who are strategically +placed, and they must be spoken at the opportune moment. Otherwise +they are mere wind. The symbols must be earmarked. For in themselves +they mean nothing, and the choice of possible symbols is always so +great that we should, like the donkey who stood equidistant between +two bales of hay, perish from sheer indecision among the symbols that +compete for our attention. + +Here, for example, are the reasons for their vote as stated by certain +private citizens to a newspaper just before the election of 1920. + +For Harding: + +"The patriotic men and women of to-day, who cast their ballots for +Harding and Coolidge will be held by posterity to have signed our +Second Declaration of Independence." + +Mr. Wilmot--, inventor. + +"He will see to it that the United States does not enter into +'entangling alliances,' Washington as a city will benefit by changing +the control of the government from the Democrats to the Republicans." + +Mr. Clarence--, salesman. + +For Cox: + +"The people of the United States realize that it is our duty pledged +on the fields of France, to join the League of Nations. We must +shoulder our share of the burden of enforcing peace throughout the +world." + +Miss Marie--, stenographer. + +"We should lose our own respect and the respect of other nations were +we to refuse to enter the League of Nations in obtaining international +peace." + +Mr. Spencer--, statistician. + +The two sets of phrases are equally noble, equally true, and almost +reversible. Would Clarence and Wilmot have admitted for an instant +that they intended to default in our duty pledged on the fields of +France; or that they did not desire international peace? Certainly +not. Would Marie and Spencer have admitted that they were in favor of +entangling alliances or the surrender of American independence? They +would have argued with you that the League was, as President Wilson +called it, a disentangling alliance, as well as a Declaration of +Independence for all the world, plus a Monroe Doctrine for the planet. + +2 + +Since the offering of symbols is so generous, and the meaning that can +be imputed is so elastic, how does any particular symbol take root in +any particular person's mind? It is planted there by another human +being whom we recognize as authoritative. If it is planted deeply +enough, it may be that later we shall call the person authoritative +who waves that symbol at us. But in the first instance symbols are +made congenial and important because they are introduced to us by +congenial and important people. + +For we are not born out of an egg at the age of eighteen with a +realistic imagination; we are still, as Mr. Shaw recalls, in the era +of Burge and Lubin, where in infancy we are dependent upon older +beings for our contacts. And so we make our connections with the outer +world through certain beloved and authoritative persons. They are the +first bridge to the invisible world. And though we may gradually +master for ourselves many phases of that larger environment, there +always remains a vaster one that is unknown. To that we still relate +ourselves through authorities. Where all the facts are out of sight a +true report and a plausible error read alike, sound alike, feel alike. + +Except on a few subjects where our own knowledge is great, we cannot +choose between true and false accounts. So we choose between +trustworthy and untrustworthy reporters. [Footnote: See an +interesting, rather quaint old book: George Cornewall Lewis, _An +Essay on the Influence of Authority in Matters of Opinion_.] + +Theoretically we ought to choose the most expert on each subject. But +the choice of the expert, though a good deal easier than the choice of +truth, is still too difficult and often impracticable. The experts +themselves are not in the least certain who among them is the most +expert. And at that, the expert, even when we can identify him, is, +likely as not, too busy to be consulted, or impossible to get at. But +there are people whom we can identify easily enough because they are +the people who are at the head of affairs. Parents, teachers, and +masterful friends are the first people of this sort we encounter. Into +the difficult question of why children trust one parent rather than +another, the history teacher rather than the Sunday school teacher, we +need not try to enter. Nor how trust gradually spreads through a +newspaper or an acquaintance who is interested in public affairs to +public personages. The literature of psychoanalysis is rich in +suggestive hypothesis. + +At any rate we do find ourselves trusting certain people, who +constitute our means of junction with pretty nearly the whole realm of +unknown things. Strangely enough, this fact is sometimes regarded as +inherently undignified, as evidence of our sheep-like, ape-like +nature. But complete independence in the universe is simply +unthinkable. If we could not take practically everything for granted, +we should spend our lives in utter triviality. The nearest thing to a +wholly independent adult is a hermit, and the range of a hermit's +action is very short. Acting entirely for himself, he can act only +within a tiny radius and for simple ends. If he has time to think +great thoughts we can be certain that he has accepted without +question, before he went in for being a hermit, a whole repertory of +painfully acquired information about how to keep warm and how to keep +from being hungry, and also about what the great questions are. + +On all but a very few matters for short stretches in our lives, the +utmost independence that we can exercise is to multiply the +authorities to whom we give a friendly hearing. As congenital amateurs +our quest for truth consists in stirring up the experts, and forcing +them to answer any heresy that has the accent of conviction. In such a +debate we can often judge who has won the dialectical victory, but we +are virtually defenseless against a false premise that none of the +debaters has challenged, or a neglected aspect that none of them has +brought into the argument. We shall see later how the democratic +theory proceeds on the opposite assumption and assumes for the +purposes of government an unlimited supply of self-sufficient +individuals. + +The people on whom we depend for contact with the outer world are +those who seem to be running it. [Footnote: _Cf._ Bryce, _Modern +Democracies_ Vol. II, pp. 544-545.] They may be running only a +very small part of the world. The nurse feeds the child, bathes it, and +puts it to bed. That does not constitute the nurse an authority on +physics, zoology, and the Higher Criticism. Mr. Smith runs, or at least +hires, the man who runs the factory. That does not make him an +authority on the Constitution of the United States, nor on the effects +\of the Fordney tariff. Mr. Smoot runs the Republican party in the State +of Utah. That in itself does not prove he is the best man to consult +about taxation. But the nurse may nevertheless determine for a while +what zoology the child shall learn, Mr. Smith will have much to say on +what the Constitution shall mean to his wife, his secretary, and perhaps +even to his parson, and who shall define the limits of Senator Smoot's +authority? + +The priest, the lord of the manor, the captains and the kings, the +party leaders, the merchant, the boss, however these men are chosen, +whether by birth, inheritance, conquest or election, they and their +organized following administer human affairs. They are the officers, +and although the same man may be field marshal at home, second +lieutenant at the office, and scrub private in politics, although in many +institutions the hierarchy of rank is vague or concealed, yet in every +institution that requires the cooperation of many persons, some such +hierarchy exists. [Footnote: _Cf._ M. Ostrogorski, _Democracy and the +Organization of Political Parties, passim;_ R. Michels, _Political Parties, +passim;_ and Bryce, _Modern Democracies,_ particularly Chap. +LXXV; also Ross, _Principles of Sociology,_ Chaps. XXII-XXIV. ] +In American politics we call it a machine, or "the organization." + +3 + +There are a number of important distinctions between the members of +the machine and the rank and file. The leaders, the steering committee +and the inner circle, are in direct contact with their environment. +They may, to be sure, have a very limited notion of what they ought to +define as the environment, but they are not dealing almost wholly with +abstractions. There are particular men they hope to see elected, +particular balance sheets they wish to see improved, concrete +objectives that must be attained. I do not mean that they escape the +human propensity to stereotyped vision. Their stereotypes often make +them absurd routineers. But whatever their limitations, the chiefs are +in actual contact with some crucial part of that larger environment. +They decide. They give orders. They bargain. And something definite, +perhaps not at all what they imagined, actually happens. + +Their subordinates are not tied to them by a common conviction. That +is to say the lesser members of a machine do not dispose their loyalty +according to independent judgment about the wisdom of the leaders. In +the hierarchy each is dependent upon a superior and is in turn +superior to some class of his dependents. What holds the machine +together is a system of privileges. These may vary according to the +opportunities and the tastes of those who seek them, from nepotism and +patronage in all their aspects to clannishness, hero-worship or a +fixed idea. They vary from military rank in armies, through land and +services in a feudal system, to jobs and publicity in a modern +democracy. That is why you can breakup a particular machine by +abolishing its privileges. But the machine in every coherent group is, +I believe, certain to reappear. For privilege is entirely relative, +and uniformity is impossible. Imagine the most absolute communism of +which your mind is capable, where no one possessed any object that +everyone else did not possess, and still, if the communist group had +to take any action whatever, the mere pleasure of being the friend of +the man who was going to make the speech that secured the most votes, +would, I am convinced, be enough to crystallize an organization of +insiders around him. + +It is not necessary, then, to invent a collective intelligence in +order to explain why the judgments of a group are usually more +coherent, and often more true to form than the remarks of the man in +the street. One mind, or a few can pursue a train of thought, but a +group trying to think in concert can as a group do little more than +assent or dissent. The members of a hierarchy can have a corporate +tradition. As apprentices they learn the trade from the masters, who +in turn learned it when they were apprentices, and in any enduring +society, the change of personnel within the governing hierarchies is +slow enough to permit the transmission of certain great stereotypes +and patterns of behavior. From father to son, from prelate to novice, +from veteran to cadet, certain ways of seeing and doing are taught. +These ways become familiar, and are recognized as such by the mass of +outsiders. + +4 + +Distance alone lends enchantment to the view that masses of human +beings ever coöperate in any complex affair without a central machine +managed by a very few people. "No one," says Bryce, [Footnote: _Op. +cit._, Vol. II, p. 542.] "can have had some years' experience of +the conduct of affairs in a legislature or an administration without +observing how extremely small is the number of persons by whom the +world is governed." He is referring, of course, to affairs of state. +To be sure if you consider all the affairs of mankind the number of +people who govern is considerable, but if you take any particular +institution, be it a legislature, a party, a trade union, a +nationalist movement, a factory, or a club, the number of those who +govern is a very small percentage of those who are theoretically +supposed to govern. + +Landslides can turn one machine out and put another in; revolutions +sometimes abolish a particular machine altogether. The democratic +revolution set up two alternating machines, each of which in the +course of a few years reaps the advantage from the mistakes of the +other. But nowhere does the machine disappear. Nowhere is the idyllic +theory of democracy realized. Certainly not in trades unions, nor in +socialist parties, nor in communist governments. There is an inner +circle, surrounded by concentric circles which fade out gradually into +the disinterested or uninterested rank and file. + +Democrats have never come to terms with this commonplace of group +life. They have invariably regarded it as perverse. For there are two +visions of democracy: one presupposes the self-sufficient individual; +the other an Oversoul regulating everything. + +Of the two the Oversoul has some advantage because it does at least +recognize that the mass makes decisions that are not spontaneously +born in the breast of every member. But the Oversoul as presiding +genius in corporate behavior is a superfluous mystery if we fix our +attention upon the machine. The machine is a quite prosaic reality. It +consists of human beings who wear clothes and live in houses, who can +be named and described. They perform all the duties usually assigned +to the Oversoul. + +5 + +The reason for the machine is not the perversity of human nature. It +is that out of the private notions of any group no common idea emerges +by itself. For the number of ways is limited in which a multitude of +people can act directly upon a situation beyond their reach. Some of +them can migrate, in one form or another, they can strike or boycott, +they can applaud or hiss. They can by these means occasionally resist +what they do not like, or coerce those who obstruct what they desire. +But by mass action nothing can be constructed, devised, negotiated, or +administered. A public as such, without an organized hierarchy around +which it can gather, may refuse to buy if the prices are too high, or +refuse to work if wages are too low. A trade union can by mass action +in a strike break an opposition so that the union officials can +negotiate an agreement. It may win, for example, the _right_ to +joint control. But it cannot exercise the right except through an +organization. A nation can clamor for war, but when it goes to war it +must put itself under orders from a general staff. + +The limit of direct action is for all practical purposes the power to +say Yes or No on an issue presented to the mass. [Footnote: _Cf_. +James, _Some Problems of Philosophy_, p. 227. "But for most of +our emergencies, fractional solutions are impossible. Seldom can we +act fractionally." _Cf_. Lowell, _Public Opinion and Popular +Government_, pp. 91, 92.] For only in the very simplest cases does +an issue present itself in the same form spontaneously and +approximately at the same time to all the members of a public. There +are unorganized strikes and boycotts, not merely industrial ones, +where the grievance is so plain that virtually without leadership the +same reaction takes place in many people. But even in these +rudimentary cases there are persons who know what they want to do more +quickly than the rest, and who become impromptu ringleaders. Where +they do not appear a crowd will mill about aimlessly beset by all its +private aims, or stand by fatalistically, as did a crowd of fifty +persons the other day, and watch a man commit suicide. + +For what we make out of most of the impressions that come to us from +the invisible world is a kind of pantomime played out in revery. The +number of times is small that we consciously decide anything about +events beyond our sight, and each man's opinion of what he could +accomplish if he tried, is slight. There is rarely a practical issue, +and therefore no great habit of decision. This would be more evident +were it not that most information when it reaches us carries with it +an aura of suggestion as to how we ought to feel about the news. That +suggestion we need, and if we do not find it in the news we turn to +the editorials or to a trusted adviser. The revery, if we feel +ourselves implicated, is uncomfortable until we know where we stand, +that is, until the facts have been formulated so that we can feel Yes +or No in regard to them. + +When a number of people all say Yes they may have all kinds of reasons +for saying it. They generally do. For the pictures in their minds are, +as we have already noted, varied in subtle and intimate ways. But this +subtlety remains within their minds; it becomes represented publicly +by a number of symbolic phrases which carry the individual emotion +after evacuating most of the intention. The hierarchy, or, if it is a +contest, then the two hierarchies, associate the symbols with a +definite action, a vote of Yes or No, an attitude pro or con. Then +Smith who was against the League and Jones who was against Article X, +and Brown who was against Mr. Wilson and all his works, each for his +own reason, all in the name of more or less the same symbolic phrase, +register a vote _against_ the Democrats by voting for the +Republicans. A common will has been expressed. + +A concrete choice had to be presented, the choice had to be connected, +by the transfer of interest through the symbols, with individual +opinion. The professional politicians learned this long before the +democratic philosophers. And so they organized the caucus, the +nominating convention, and the steering committee, as the means of +formulating a definite choice. Everyone who wishes to accomplish +anything that requires the cooperation of a large number of people +follows their example. Sometimes it is done rather brutally as when +the Peace Conference reduced itself to the Council of Ten, and the +Council of Ten to the Big Three or Four; and wrote a treaty which the +minor allies, their own constituents, and the enemy were permitted to +take or leave. More consultation than that is generally possible and +desirable. But the essential fact remains that a small number of heads +present a choice to a large group. + +6 + +The abuses of the steering committee have led to various proposals +such as the initiative, referendum and direct primary. But these +merely postponed or obscured the need for a machine by complicating +the elections, or as H. G. Wells once said with scrupulous accuracy, +the selections. For no amount of balloting can obviate the need of +creating an issue, be it a measure or a candidate, on which the voters +can say Yes, or No. There is, in fact, no such thing as "direct +legislation." For what happens where it is supposed to exist? The +citizen goes to the polls, receives a ballot on which a number of +measures are printed, almost always in abbreviated form, and, if he +says anything at all, he says Yes or No. The most brilliant amendment +in the world may occur to him. He votes Yes or No on that bill and no +other. You have to commit violence against the English language to +call that legislation. I do not argue, of course, that there are no +benefits, whatever you call the process. I think that for certain +kinds of issues there are distinct benefits. But the necessary +simplicity of any mass decision is a very important fact in view of +the inevitable complexity of the world in which those decisions +operate. The most complicated form of voting that anyone proposes is, +I suppose, the preferential ballot. Among a number of candidates +presented the voter under that system, instead of saying yes to one +candidate and no to all the others, states the order of his choice. +But even here, immensely more flexible though it is, the action of the +mass depends upon the quality of the choices presented. [Footnote: +_Cf._ H. J. Laski, _Foundations of Sovereignty,_ p. 224. "... +proportional representation... by leading, as it seems to lead, to the +group system... may deprive the electors of their choice of leaders." +The group system undoubtedly tends, as Mr. Laski says, to make the +selection of the executive more indirect, but there is no doubt also +that it tends to produce legislative assemblies in which currents of +opinion are more fully represented. Whether that is good or bad +cannot be determined a priori. But one can say that successful +cooperation and responsibility in a more accurately representative +assembly require a higher organization of political intelligence and +political habit, than in a rigid two-party house. It is a more complex +political form and may therefore work less well.] And those choices +are presented by the energetic coteries who hustle about with +petitions and round up the delegates. The Many can elect after the Few +have nominated. + + + + +CHAPTER XV + +LEADERS AND THE RANK AND FILE + +I + +BECAUSE of their transcendent practical importance, no successful +leader has ever been too busy to cultivate the symbols which organize +his following. What privileges do within the hierarchy, symbols do for +the rank and file. They conserve unity. From the totem pole to the +national flag, from the wooden idol to God the Invisible King, from +the magic word to some diluted version of Adam Smith or Bentham, +symbols have been cherished by leaders, many of whom were themselves +unbelievers, because they were focal points where differences merged. +The detached observer may scorn the "star-spangled" ritual which +hedges the symbol, perhaps as much as the king who told himself that +Paris was worth a few masses. But the leader knows by experience that +only when symbols have done their work is there a handle he can use to +move a crowd. In the symbol emotion is discharged at a common target, +and the idiosyncrasy of real ideas blotted out. No wonder he hates +what he calls destructive criticism, sometimes called by free spirits +the elimination of buncombe. "Above all things," says Bagehot, "our +royalty is to be reverenced, and if you begin to poke about it you +cannot reverence it." [Footnote: _The English Constitution,_ p. +127. D. Appleton & Company, 1914.] For poking about with clear +definitions and candid statements serves all high purposes known to +man, except the easy conservation of a common will. Poking about, as +every responsible leader suspects, tends to break the transference of +emotion from the individual mind to the institutional symbol. And the +first result of that is, as he rightly says, a chaos of individualism +and warring sects. The disintegration of a symbol, like Holy Russia, +or the Iron Diaz, is always the beginning of a long upheaval. + +These great symbols possess by transference all the minute and +detailed loyalties of an ancient and stereotyped society. They evoke +the feeling that each individual has for the landscape, the furniture, +the faces, the memories that are his first, and in a static society, +his only reality. That core of images and devotions without which he +is unthinkable to himself, is nationality. The great symbols take up +these devotions, and can arouse them without calling forth the +primitive images. The lesser symbols of public debate, the more casual +chatter of politics, are always referred back to these proto-symbols, +and if possible associated with them. The question of a proper fare on +a municipal subway is symbolized as an issue between the People and +the Interests, and then the People is inserted in the symbol American, +so that finally in the heat of a campaign, an eight cent fare becomes +unAmerican. The Revolutionary fathers died to prevent it. Lincoln +suffered that it might not come to pass, resistance to it was implied +in the death of those who sleep in France. + +Because of its power to siphon emotion out of distinct ideas, the +symbol is both a mechanism of solidarity, and a mechanism of +exploitation. It enables people to work for a common end, but just +because the few who are strategically placed must choose the concrete +objectives, the symbol is also an instrument by which a few can fatten +on many, deflect criticism, and seduce men into facing agony for +objects they do not understand. + +Many aspects of our subjection to symbols are not flattering if we +choose to think of ourselves as realistic, self-sufficient, and +self-governing personalities. Yet it is impossible to conclude that +symbols are altogether instruments of the devil. In the realm of +science and contemplation they are undoubtedly the tempter himself. +But in the world of action they may be beneficent, and are sometimes a +necessity. The necessity is often imagined, the peril manufactured. +But when quick results are imperative, the manipulation of masses +through symbols may be the only quick way of having a critical thing +done. It is often more important to act than to understand. It is +sometimes true that the action would fail if everyone understood it. +There are many affairs which cannot wait for a referendum or endure +publicity, and there are times, during war for example, when a nation, +an army, and even its commanders must trust strategy to a very few +minds; when two conflicting opinions, though one happens to be right, +are more perilous than one opinion which is wrong. The wrong opinion +may have bad results, but the two opinions may entail disaster by +dissolving unity. [Footnote: Captain Peter S. Wright, Assistant +Secretary of the Supreme War Council, _At the Supreme War +Council,_ is well worth careful reading on secrecy and unity of +command, even though in respect to the allied leaders he wages a +passionate polemic.] + +Thus Foch and Sir Henry Wilson, who foresaw the impending disaster to +Cough's army, as a consequence of the divided and scattered reserves, +nevertheless kept their opinions well within a small circle, knowing +that even the risk of a smashing defeat was less certainly +destructive, than would have been an excited debate in the newspapers. +For what matters most under the kind of tension which prevailed in +March, 1918, is less the rightness of a particular move than the +unbroken expectation as to the source of command. Had Foch "gone to +the people" he might have won the debate, but long before he could +have won it, the armies which he was to command would have dissolved. +For the spectacle of a row on Olympus is diverting and destructive. + +But so also is a conspiracy of silence. Says Captain Wright: "It is in +the High Command and not in the line, that the art of camouflage is +most practiced, and reaches to highest flights. All chiefs everywhere +are now kept painted, by the busy work of numberless publicists, so as +to be mistaken for Napoleons--at a distance....It becomes almost +impossible to displace these Napoleons, whatever their incompetence, +because of the enormous public support created by hiding or glossing +failure, and exaggerating or inventing success.... But the most +insidious and worst effect of this so highly organized falsity is on +the generals themselves: modest and patriotic as they mostly are, and +as most men must be to take up and follow the noble profession of +arms, they themselves are ultimately affected by these universal +illusions, and reading it every morning in the paper, they also grow +persuaded they are thunderbolts of war and infallible, however much +they fail, and that their maintenance in command is an end so sacred +that it justifies the use of any means.... These various conditions, +of which this great deceit is the greatest, at last emancipate all +General Staffs from all control. They no longer live for the nation: +the nation lives, or rather dies, for them. Victory or defeat ceases +to be the prime interest. What matters to these semi-sovereign +corporations is whether dear old Willie or poor old Harry is going to +be at their head, or the Chantilly party prevail over the Boulevard +des Invalides party." [Footnote: _Op. cit._, pp. 98, 101-105.] + +Yet Captain Wright who can be so eloquent and so discerning about the +dangers of silence is forced nevertheless to approve the silence of +Foch in not publicly destroying the illusions. There is here a +complicated paradox, arising as we shall see more fully later on, +because the traditional democratic view of life is conceived, not for +emergencies and dangers, but for tranquillity and harmony. And so +where masses of people must coöperate in an uncertain and eruptive +environment, it is usually necessary to secure unity and flexibility +without real consent. The symbol does that. It obscures personal +intention, neutralizes discrimination, and obfuscates individual +purpose. It immobilizes personality, yet at the same time it +enormously sharpens the intention of the group and welds that group, +as nothing else in a crisis can weld it, to purposeful action. It +renders the mass mobile though it immobilizes personality. The symbol +is the instrument by which in the short run the mass escapes from its +own inertia, the inertia of indecision, or the inertia of headlong +movement, and is rendered capable of being led along the zigzag of a +complex situation. + +2 + +But in the longer run, the give and take increases between the leaders +and the led. The word most often used to describe the state of mind in +the rank and file about its leaders is morale. That is said to be good +when the individuals do the part allotted to them with all their +energy; when each man's whole strength is evoked by the command from +above. It follows that every leader must plan his policy with this in +mind. He must consider his decision not only on "the merits," but also +in its effect on any part of his following whose continued support he +requires. If he is a general planning an attack, he knows that his +organized military units will scatter into mobs if the percentage of +casualties rises too high. + +In the Great War previous calculations were upset to an extraordinary +degree, for "out of every nine men who went to France five became +casualties." [Footnote: _Op. cit_., p. 37. Figures taken by +Captain Wright from the statistical abstract of the war in the +Archives of the War Office. The figures refer apparently to the +English losses alone, possibly to the English and French.] The limit +of endurance was far greater than anyone had supposed. But there was a +limit somewhere. And so, partly because of its effect on the enemy, +but also in great measure because of its effect on the troops and +their families, no command in this war dared to publish a candid +statement of its losses. In France the casualty lists were never +published. In England, America, and Germany publication of the losses +of a big battle were spread out over long periods so as to destroy a +unified impression of the total. Only the insiders knew until long +afterwards what the Somme had cost, or the Flanders battles; +[Footnote: _Op cit._, p. 34, the Somme cost nearly 500,000 +casualties; the Arras and Flanders offensives of 1917 cost 650,000 +British casualties.] and Ludendorff undoubtedly had a very much more +accurate idea of these casualties than any private person in London, +Paris or Chicago. All the leaders in every camp did their best to +limit the amount of actual war which any one soldier or civilian could +vividly conceive. But, of course, among old veterans like the French +troops of 1917, a great deal more is known about war than ever reaches +the public. Such an army begins to judge its commanders in terms of +its own suffering. And then, when another extravagant promise of +victory turns out to be the customary bloody defeat, you may find that +a mutiny breaks out over some comparatively minor blunder, [Footnote: +The Allies suffered many bloodier defeats than that on the Chemin des +Dames.] like Nivelle's offensive of 1917, because it is a cumulative +blunder. Revolutions and mutinies generally follow a small sample of a +big series of evils. [Footnote: _Cf._ Pierrefeu's account, _op. +cit._, on the causes of the Soissons mutinies, and the method +adopted by Pétain to deal with them. Vol. I, Part III, _et seq._] + +The incidence of policy determines the relation between leader and +following. If those whom he needs in his plan are remote from the +place where the action takes place, if the results are hidden or +postponed, if the individual obligations are indirect or not yet due, +above all if assent is an exercise of some pleasurable emotion, the +leader is likely to have a free hand. Those programs are immediately +most popular, like prohibition among teetotalers, which do not at once +impinge upon the private habits of the followers. That is one great +reason why governments have such a free hand in foreign affairs. Most +of the frictions between two states involve a series of obscure and +long-winded contentions, occasionally on the frontier, but far more +often in regions about which school geographies have supplied no +precise ideas. In Czechoslovakia America is regarded as the Liberator; +in American newspaper paragraphs and musical comedy, in American +conversation by and large, it has never been finally settled whether +the country we liberated is Czechoslavia or Jugoslovakia. + +In foreign affairs the incidence of policy is for a very long time +confined to an unseen environment. Nothing that happens out there is +felt to be wholly real. And so, because in the ante-bellum period, +nobody has to fight and nobody has to pay, governments go along +according to their lights without much reference to their people. In +local affairs the cost of a policy is more easily visible. And +therefore, all but the most exceptional leaders prefer policies in +which the costs are as far as possible indirect. + +They do not like direct taxation. They do not like to pay as they go. +They like long term debts. They like to have the voters believe that +the foreigner will pay. They have always been compelled to calculate +prosperity in terms of the producer rather than in terms of the +consumer, because the incidence on the consumer is distributed over so +many trivial items. Labor leaders have always preferred an increase of +money wages to a decrease in prices. There has always been more +popular interest in the profits of millionaires, which are visible but +comparatively unimportant, than in the wastes of the industrial +system, which are huge but elusive. A legislature dealing with a +shortage of houses, such as exists when this is written, illustrates +this rule, first by doing nothing to increase the number of houses, +second by smiting the greedy landlord on the hip, third by +investigating the profiteering builders and working men. For a +constructive policy deals with remote and uninteresting factors, while +a greedy landlord, or a profiteering plumber is visible and immediate. + +But while people will readily believe that in an unimagined future and +in unseen places a certain policy will benefit them, the actual +working out of policy follows a different logic from their opinions. A +nation may be induced to believe that jacking up the freight rates +will make the railroads prosperous. But that belief will not make the +roads prosperous, if the impact of those rates on farmers and shippers +is such as to produce a commodity price beyond what the consumer can +pay. Whether the consumer will pay the price depends not upon whether +he nodded his head nine months previously at the proposal to raise +rates and save business, but on whether he now wants a new hat or a +new automobile enough to pay for them. + +3 + +Leaders often pretend that they have merely uncovered a program which +existed in the minds of their public. When they believe it, they are +usually deceiving themselves. Programs do not invent themselves +synchronously in a multitude of minds. That is not because a multitude +of minds is necessarily inferior to that of the leaders, but because +thought is the function of an organism, and a mass is not an organism. + +This fact is obscured because the mass is constantly exposed to +suggestion. It reads not the news, but the news with an aura of +suggestion about it, indicating the line of action to be taken. It +hears reports, not objective as the facts are, but already stereotyped +to a certain pattern of behavior. Thus the ostensible leader often +finds that the real leader is a powerful newspaper proprietor. But if, +as in a laboratory, one could remove all suggestion and leading from +the experience of a multitude, one would, I think, find something like +this: A mass exposed to the same stimuli would develop responses that +could theoretically be charted in a polygon of error. There would be a +certain group that felt sufficiently alike to be classified together. +There would be variants of feeling at both ends. These classifications +would tend to harden as individuals in each of the classifications +made their reactions vocal. That is to say, when the vague feelings of +those who felt vaguely had been put into words, they would know more +definitely what they felt, and would then feel it more definitely. + +Leaders in touch with popular feeling are quickly conscious of these +reactions. They know that high prices are pressing upon the mass, or +that certain classes of individuals are becoming unpopular, or that +feeling towards another nation is friendly or hostile. But, always +barring the effect of suggestion which is merely the assumption of +leadership by the reporter, there would be nothing in the feeling of +the mass that fatally determined the choice of any particular policy. +All that the feeling of the mass demands is that policy as it is +developed and exposed shall be, if not logically, then by analogy and +association, connected with the original feeling. + +So when a new policy is to be launched, there is a preliminary bid for +community of feeling, as in Mark Antony's speech to the followers of +Brutus. [Footnote: Excellently analyzed in Martin, _The Behavior of +Crowds,_ pp. 130-132,] In the first phase, the leader vocalizes the +prevalent opinion of the mass. He identifies himself with the familiar +attitudes of his audience, sometimes by telling a good story, +sometimes by brandishing his patriotism, often by pinching a +grievance. Finding that he is trustworthy, the multitude milling +hither and thither may turn in towards him. He will then be expected +to set forth a plan of campaign. But he will not find that plan in the +slogans which convey the feelings of the mass. It will not even always +be indicated by them. Where the incidence of policy is remote, all +that is essential is that the program shall be verbally and +emotionally connected at the start with what has become vocal in the +multitude. Trusted men in a familiar role subscribing to the accepted +symbols can go a very long way on their own initiative without +explaining the substance of their programs. + +But wise leaders are not content to do that. Provided they think +publicity will not strengthen opposition too much, and that debate +will not delay action too long, they seek a certain measure of +consent. They take, if not the whole mass, then the subordinates of +the hierarchy sufficiently into their confidence to prepare them for +what might happen, and to make them feel that they have freely willed +the result. But however sincere the leader may be, there is always, +when the facts are very complicated, a certain amount of illusion in +these consultations. For it is impossible that all the contingencies +shall be as vivid to the whole public as they are to the more +experienced and the more imaginative. A fairly large percentage are +bound to agree without having taken the time, or without possessing +the background, for appreciating the choices which the leader presents +to them. No one, however, can ask for more. And only theorists do. If +we have had our day in court, if what we had to say was heard, and +then if what is done comes out well, most of us do not stop to +consider how much our opinion affected the business in hand. + +And therefore, if the established powers are sensitive and +well-informed, if they are visibly trying to meet popular feeling, and +actually removing some of the causes of dissatisfaction, no matter how +slowly they proceed, provided they are seen to be proceeding, they +have little to fear. It takes stupendous and persistent blundering, +plus almost infinite tactlessness, to start a revolution from below. +Palace revolutions, interdepartmental revolutions, are a different +matter. So, too, is demagogy. That stops at relieving the tension by +expressing the feeling. But the statesman knows that such relief is +temporary, and if indulged too often, unsanitary. He, therefore, sees +to it that he arouses no feeling which he cannot sluice into a program +that deals with the facts to which the feelings refer. + +But all leaders are not statesmen, all leaders hate to resign, and +most leaders find it hard to believe that bad as things are, the other +fellow would not make them worse. They do not passively wait for the +public to feel the incidence of policy, because the incidence of that +discovery is generally upon their own heads. They are, therefore, +intermittently engaged in mending their fences and consolidating their +position. + +The mending of fences consists in offering an occasional scapegoat, in +redressing a minor grievance affecting a powerful individual or +faction, rearranging certain jobs, placating a group of people who +want an arsenal in their home town, or a law to stop somebody's vices. +Study the daily activity of any public official who depends on +election and you can enlarge this list. There are Congressmen elected +year after year who never think of dissipating their energy on public +affairs. They prefer to do a little service for a lot of people on a +lot of little subjects, rather than to engage in trying to do a big +service out there in the void. But the number of people to whom any +organization can be a successful valet is limited, and shrewd +politicians take care to attend either the influential, or somebody so +blatantly uninfluential that to pay any attention to him is a mark of +sensational magnanimity. The far greater number who cannot be held by +favors, the anonymous multitude, receive propaganda. + +The established leaders of any organization have great natural +advantages. They are believed to have better sources of information. +The books and papers are in their offices. They took part in the +important conferences. They met the important people. They have +responsibility. It is, therefore, easier for them to secure attention +and to speak in a convincing tone. But also they have a very great +deal of control over the access to the facts. Every official is in +some degree a censor. And since no one can suppress information, +either by concealing it or forgetting to mention it, without some +notion of what he wishes the public to know, every leader is in some +degree a propagandist. Strategically placed, and compelled often to +choose even at the best between the equally cogent though conflicting +ideals of safety for the institution, and candor to his public, the +official finds himself deciding more and more consciously what facts, +in what setting, in what guise he shall permit the public to know. + +4 + +That the manufacture of consent is capable of great refinements no +one, I think, denies. The process by which public opinions arise is +certainly no less intricate than it has appeared in these pages, and +the opportunities for manipulation open to anyone who understands the +process are plain enough. + +The creation of consent is not a new art. It is a very old one which +was supposed to have died out with the appearance of democracy. But it +has not died out. It has, in fact, improved enormously in technic, +because it is now based on analysis rather than on rule of thumb. And +so, as a result of psychological research, coupled with the modern +means of communication, the practice of democracy has turned a corner. +A revolution is taking place, infinitely more significant than any +shifting of economic power. + +Within the life of the generation now in control of affairs, +persuasion has become a self-conscious art and a regular organ of +popular government. None of us begins to understand the consequences, +but it is no daring prophecy to say that the knowledge of how to +create consent will alter every political calculation and modify every +political premise. Under the impact of propaganda, not necessarily in +the sinister meaning of the word alone, the old constants of our +thinking have become variables. It is no longer possible, for example, +to believe in the original dogma of democracy; that the knowledge +needed for the management of human affairs comes up spontaneously from +the human heart. Where we act on that theory we expose ourselves to +self-deception, and to forms of persuasion that we cannot verify. It +has been demonstrated that we cannot rely upon intuition, conscience, +or the accidents of casual opinion if we are to deal with the world +beyond our reach. + + + + +PART VI + +THE IMAGE OF DEMOCRACY + +"I confess that in America I saw more than America; +I sought the image of democracy itself." + +Alexis de Tocqueville. + +CHAPTER 16. THE SELF-CENTERED MAN + " 17. THE SELF-CONTAINED COMMUNITY + " 18. THE ROLE OF FORCE, PATRONAGE AND PRIVILEGE + " 19. THE OLD IMAGE IN A NEW FORM: GUILD SOCIALISM + " 20. A NEW IMAGE + + + + +CHAPTER XVI + +THE SELF-CENTERED MAN + +I + +SINCE Public Opinion is supposed to be the prime mover in democracies, +one might reasonably expect to find a vast literature. One does not +find it. There are excellent books on government and parties, that is, +on the machinery which in theory registers public opinions after they +are formed. But on the sources from which these public opinions arise, +on the processes by which they are derived, there is relatively +little. The existence of a force called Public Opinion is in the main +taken for granted, and American political writers have been most +interested either in finding out how to make government express the +common will, or in how to prevent the common will from subverting the +purposes for which they believe the government exists. According to +their traditions they have wished either to tame opinion or to obey +it. Thus the editor of a notable series of text-books writes that "the +most difficult and the most momentous question of government (is) how +to transmit the force of individual opinion into public +action." [Footnote: Albert Bushnell Hart in the Introductory note to A. +Lawrence Lowell's _Public Opinion and Popular Government. _] + +But surely there is a still more momentous question, the question of +how to validate our private versions of the political scene. There is, +as I shall try to indicate further on, the prospect of radical +improvement by the development of principles already in operation. But +this development will depend on how well we learn to use knowledge of +the way opinions are put together to watch over our own opinions when +they are being put together. For casual opinion, being the product of +partial contact, of tradition, and personal interests, cannot in the +nature of things take kindly to a method of political thought which is +based on exact record, measurement, analysis and comparison. Just +those qualities of the mind which determine what shall seem +interesting, important, familiar, personal, and dramatic, are the +qualities which in the first instance realistic opinion frustrates. +Therefore, unless there is in the community at large a growing +conviction that prejudice and intuition are not enough, the working +out of realistic opinion, which takes time, money, labor, conscious +effort, patience, and equanimity, will not find enough support. That +conviction grows as self-criticism increases, and makes us conscious +of buncombe, contemptuous of ourselves when we employ it, and on guard +to detect it. Without an ingrained habit of analyzing opinion when we +read, talk, and decide, most of us would hardly suspect the need of +better ideas, nor be interested in them when they appear, nor be able +to prevent the new technic of political intelligence from being +manipulated. + +Yet democracies, if we are to judge by the oldest and most powerful of +them, have made a mystery out of public opinion. There have been +skilled organizers of opinion who understood the mystery well enough +to create majorities on election day. But these organizers have been +regarded by political science as low fellows or as "problems," not as +possessors of the most effective knowledge there was on how to create +and operate public opinion. The tendency of the people who have voiced +the ideas of democracy, even when they have not managed its action, +the tendency of students, orators, editors, has been to look upon +Public Opinion as men in other societies looked upon the uncanny +forces to which they ascribed the last word in the direction of +events. + +For in almost every political theory there is an inscrutable element +which in the heyday of that theory goes unexamined. Behind the +appearances there is a Fate, there are Guardian Spirits, or Mandates +to a Chosen People, a Divine Monarchy, a Vice-Regent of Heaven, or a +Class of the Better Born. The more obvious angels, demons, and kings +are gone out of democratic thinking, but the need for believing that +there are reserve powers of guidance persists. It persisted for those +thinkers of the Eighteenth Century who designed the matrix of +democracy. They had a pale god, but warm hearts, and in the doctrine +of popular sovereignty they found the answer to their need of an +infallible origin for the new social order. There was the mystery, and +only enemies of the people touched it with profane and curious hands. + +2 + +They did not remove the veil because they were practical politicians +in a bitter and uncertain struggle. They had themselves felt the +aspiration of democracy, which is ever so much deeper, more intimate +and more important than any theory of government. They were engaged, +as against the prejudice of ages, in the assertion of human dignity. +What possessed them was not whether John Smith had sound views on any +public question, but that John Smith, scion of a stock that had always +been considered inferior, would now bend his knee to no other man. It +was this spectacle that made it bliss "in that dawn to be alive." But +every analyst seems to degrade that dignity, to deny that all men are +reasonable all the time, or educated, or informed, to note that people +are fooled, that they do not always know their own interests, and that +all men are not equally fitted to govern. + +The critics were about as welcome as a small boy with a drum. Every +one of these observations on the fallibility of man was being +exploited ad nauseam. Had democrats admitted there was truth in any of +the aristocratic arguments they would have opened a breach in the +defenses. And so just as Aristotle had to insist that the slave was a +slave by nature, the democrats had to insist that the free man was a +legislator and administrator by nature. They could not stop to explain +that a human soul might not yet have, or indeed might never have, this +technical equipment, and that nevertheless it had an inalienable right +not to be used as the unwilling instrument of other men. The superior +people were still too strong and too unscrupulous to have refrained +from capitalizing so candid a statement. + +So the early democrats insisted that a reasoned righteousness welled +up spontaneously out of the mass of men. All of them hoped that it +would, many of them believed that it did, although the cleverest, like +Thomas Jefferson, had all sorts of private reservations. But one thing +was certain: if public opinion did not come forth spontaneously, +nobody in that age believed it would come forth at all. For in one +fundamental respect the political science on which democracy was based +was the same science that Aristotle formulated. It was the same +science for democrat and aristocrat, royalist and republican, in that +its major premise assumed the art of government to be a natural +endowment. Men differed radically when they tried to name the men so +endowed; but they agreed in thinking that the greatest question of all +was to find those in whom political wisdom was innate. Royalists were +sure that kings were born to govern. Alexander Hamilton thought that +while "there are strong minds in every walk of life... the +representative body, with too few exceptions to have any influence on +the spirit of the government, will be composed of landholders, +merchants, and men of the learned professions." [Footnote: _The +Federalist_, Nos. 35, 36. _Cf_. comment by Henry Jones Ford in +his _Rise and Growth of American Politics_. Ch. V.] Jefferson +thought the political faculties were deposited by God in farmers and +planters, and sometimes spoke as if they were found in all the people. +[Footnote: See below p. 268.] The main premise was the same: to govern +was an instinct that appeared, according to your social preferences, +in one man or a chosen few, in all males, or only in males who were +white and twenty-one, perhaps even in all men and all women. + +In deciding who was most fit to govern, knowledge of the world was +taken for granted. The aristocrat believed that those who dealt with +large affairs possessed the instinct, the democrats asserted that all +men possessed the instinct and could therefore deal with large +affairs. It was no part of political science in either case to think +out how knowledge of the world could be brought to the ruler. If you +were for the people you did not try to work out the question of how to +keep the voter informed. By the age of twenty-one he had his political +faculties. What counted was a good heart, a reasoning mind, a balanced +judgment. These would ripen with age, but it was not necessary to +consider how to inform the heart and feed the reason. Men took in +their facts as they took in their breath. + +3 + +But the facts men could come to possess in this effortless way were +limited. They could know the customs and more obvious character of the +place where they lived and worked. But the outer world they had to +conceive, and they did not conceive it instinctively, nor absorb +trustworthy knowledge of it just by living. Therefore, the only +environment in which spontaneous politics were possible was one +confined within the range of the ruler's direct and certain knowledge. +There is no escaping this conclusion, wherever you found government on +the natural range of men's faculties. "If," as Aristotle said, +[Footnote: _Politics_, Bk. VII, Ch. 4.] "the citizens of a state +are to judge and distribute offices according to merit, then they must +know each other's characters; where they do not possess this +knowledge, both the election to offices and the decision of law suits +will go wrong." + +Obviously this maxim was binding upon every school of political +thought. But it presented peculiar difficulties to the democrats. +Those who believed in class government could fairly claim that in the +court of the king, or in the country houses of the gentry, men did +know each other's characters, and as long as the rest of mankind was +passive, the only characters one needed to know were the characters of +men in the ruling class. But the democrats, who wanted to raise the +dignity of all men, were immediately involved by the immense size and +confusion of their ruling class--the male electorate. Their science +told them that politics was an instinct, and that the instinct worked +in a limited environment. Their hopes bade them insist that all men in +a very large environment could govern. In this deadly conflict between +their ideals and their science, the only way out was to assume without +much discussion that the voice of the people was the voice of God. + +The paradox was too great, the stakes too big, their ideal too +precious for critical examination. They could not show how a citizen +of Boston was to stay in Boston and conceive the views of a Virginian, +how a Virginian in Virginia could have real opinions about the +government at Washington, how Congressmen in Washington could have +opinions about China or Mexico. For in that day it was not possible +for many men to have an unseen environment brought into the field of +their judgment. There had been some advances, to be sure, since +Aristotle. There were a few newspapers, and there were books, better +roads perhaps, and better ships. But there was no great advance, and +the political assumptions of the Eighteenth Century had essentially to +be those that had prevailed in political science for two thousand +years. The pioneer democrats did not possess the material for +resolving the conflict between the known range of man's attention and +their illimitable faith in his dignity. + +Their assumptions antedated not only the modern newspaper, the +world-wide press services, photography and moving pictures, but, what +is really more significant, they antedated measurement and record, +quantitative and comparative analysis, the canons of evidence, and the +ability of psychological analysis to correct and discount the +prejudices of the witness. I do not mean to say that our records are +satisfactory, our analysis unbiased, our measurements sound. I do mean +to say that the key inventions have been made for bringing the unseen +world into the field of judgment. They had not been made in the time +of Aristotle, and they were not yet important enough to be visible for +political theory in the age of Rousseau, Montesquieu, or Thomas +Jefferson. In a later chapter I think we shall see that even in the +latest theory of human reconstruction, that of the English Guild +Socialists, all the deeper premises have been taken over from this +older system of political thought. + +That system, whenever it was competent and honest, had to assume that +no man could have more than a very partial experience of public +affairs. In the sense that he can give only a little time to them, +that assumption is still true, and of the utmost consequence. But +ancient theory was compelled to assume, not only that men could give +little attention to public questions, but that the attention available +would have to be confined to matters close at hand. It would have been +visionary to suppose that a time would come when distant and +complicated events could conceivably be reported, analyzed, and +presented in such a form that a really valuable choice could be made +by an amateur. That time is now in sight. There is no longer any doubt +that the continuous reporting of an unseen environment is feasible. It +is often done badly, but the fact that it is done at all shows that it +can be done, and the fact that we begin to know how badly it is often +done, shows that it can be done better. With varying degrees of skill +and honesty distant complexities are reported every day by engineers +and accountants for business men, by secretaries and civil servants +for officials, by intelligence officers for the General Staff, by some +journalists for some readers. These are crude beginnings but radical, +far more radical in the literal meaning of that word than the +repetition of wars, revolutions, abdications and restorations; as +radical as the change in the scale of human life which has made it +possible for Mr. Lloyd George to discuss Welsh coal mining after +breakfast in London, and the fate of the Arabs before dinner in Paris. + +For the possibility of bringing any aspect of human affairs within the +range of judgment breaks the spell which has lain upon political +ideas. There have, of course, been plenty of men who did not realize +that the range of attention was the main premise of political science. +They have built on sand. They have demonstrated in their own persons +the effects of a very limited and self-centered knowledge of the +world. But for the political thinkers who have counted, from Plato and +Aristotle through Machiavelli and Hobbes to the democratic theorists, +speculation has revolved around the self-centered man who had to see +the whole world by means of a few pictures in his head. + + + + +CHAPTER XVII + +THE SELF-CONTAINED COMMUNITY + +1 + +THAT groups of self-centered people would engage in a struggle for +existence if they rubbed against each other has always been evident. +This much truth there is at any rate in that famous passage in the +Leviathan where Hobbes says that "though there had never been any time +wherein particular men were in a condition of war one against another, +yet at all times kings and _persons_ of _sovereign authority +because_ of their _independency_, are in continual jealousies +and in the state and posture of gladiators, having their weapons +pointing, and their eyes fixed on one another..." [Footnote: +_Leviathan_, Ch. XIII. Of the Natural Condition of Mankind as +concerning their Felicity and Misery.] + +2 + +To circumvent this conclusion one great branch of human thought, which +had and has many schools, proceeded in this fashion: it conceived an +ideally just pattern of human relations in which each person had well +defined functions and rights. If he conscientiously filled the role +allotted to him, it did not matter whether his opinions were right or +wrong. He did his duty, the next man did his, and all the dutiful +people together made a harmonious world. Every caste system +illustrates this principle; you find it in Plato's Republic and in +Aristotle, in the feudal ideal, in the circles of Dante's Paradise, in +the bureaucratic type of socialism, and in laissez-faire, to an +amazing degree in syndicalism, guild socialism, anarchism, and in the +system of international law idealized by Mr. Robert Lansing. All of +them assume a pre-established harmony, inspired, imposed, or innate, +by which the self-opinionated person, class, or community is +orchestrated with the rest of mankind. The more authoritarian imagine +a conductor for the symphony who sees to it that each man plays his +part; the anarchistic are inclined to think that a more divine concord +would be heard if each player improvised as he went along. + +But there have also been philosophers who were bored by these schemes +of rights and duties, took conflict for granted, and tried to see how +their side might come out on top. They have always seemed more +realistic, even when they seemed alarming, because all they had to do +was to generalize the experience that nobody could escape. Machiavelli +is the classic of this school, a man most mercilessly maligned, +because he happened to be the first naturalist who used plain language +in a field hitherto preempted by supernaturalists. [Footnote: F. S. +Oliver in his _Alexander Hamilton_, says of Machiavelli (p. 174): +"Assuming the conditions which exist--the nature of man and of +things--to be unchangeable, he proceeds in a calm, unmoral way, like a +lecturer on frogs, to show how a valiant and sagacious ruler can best +turn events to his own advantage and the security of his dynasty."] He +has a worse name and more disciples than any political thinker who +ever lived. He truly described the technic of existence for the +self-contained state. That is why he has the disciples. He has the bad +name chiefly because he cocked his eye at the Medici family, dreamed +in his study at night where he wore his "noble court dress" that +Machiavelli was himself the Prince, and turned a pungent description +of the way things are done into an eulogy on that way of doing them. + +In his most infamous chapter [Footnote: _The Prince_, Ch. XVIII. +"Concerning the way in which Princes should keep faith." Translation +by W. K. Marriott.] he wrote that "a prince ought to take care that he +never lets anything slip from his lips that is not replete with the +above-named five qualities, that he may appear to him who hears and +sees him altogether merciful, faithful, humane, upright, and +religious. There is nothing more necessary to appear to have than this +last quality, inasmuch as men judge generally more by the eye than by +the hand, because it belongs to everybody to see you, to few to come +in touch with you. Everyone sees what you appear to be, few really +know what you are, and those few dare not oppose themselves to the +opinion of the many, who have the majesty of the state to defend them; +and in the actions of all men, and especially of princes, which it is +not prudent to challenge, one judges by the result.... One prince of +the present time, whom it is not well to name, never preaches anything +else but peace and good faith, and to both he is most hostile, and +either, if he had kept it, would have deprived him of reputation and +kingdom many a time." + +That is cynical. But it is the cynicism of a man who saw truly without +knowing quite why he saw what he saw. Machiavelli is thinking of the +run of men and princes "who judge generally more by the eye than by +the hand," which is his way of saying that their judgments are +subjective. He was too close to earth to pretend that the Italians of +his day saw the world steadily and saw it whole. He would not indulge +in fantasies, and he had not the materials for imagining a race of men +that had learned how to correct their vision. + +The world, as he found it, was composed of people whose vision could +rarely be corrected, and Machiavelli knew that such people, since they +see all public relations in a private way, are involved in perpetual +strife. What they see is their own personal, class, dynastic, or +municipal version of affairs that in reality extend far beyond the +boundaries of their vision. They see their aspect. They see it as +right. But they cross other people who are similarly self-centered. +Then their very existence is endangered, or at least what they, for +unsuspected private reasons, regard as their existence and take to be +a danger. The end, which is impregnably based on a real though private +experience justifies the means. They will sacrifice any one of these +ideals to save all of them,... "one judges by the result..." + +3 + +These elemental truths confronted the democratic philosophers. +Consciously or otherwise, they knew that the range of political +knowledge was limited, that the area of self-government would have to +be limited, and that self-contained states when they rubbed against +each other were in the posture of gladiators. But they knew just as +certainly, that there was in men a will to decide their own fate, and +to find a peace that was not imposed by force. How could they +reconcile the wish and the fact? + +They looked about them. In the city states of Greece and Italy they +found a chronicle of corruption, intrigue and war. [Footnote: +"Democracies have ever been spectacles of turbulence and contention... +and have in general been as short in their lives as they have been +violent in their deaths." Madison, _Federalist_, No. 10.] In +their own cities they saw faction, artificiality, fever. This was no +environment in which the democratic ideal could prosper, no place +where a group of independent and equally competent people managed +their own affairs spontaneously. They looked further, guided somewhat +perhaps by Jean Jacques Rousseau, to remote, unspoiled country +villages. They saw enough to convince themselves that there the ideal +was at home. Jefferson in particular felt this, and Jefferson more +than any other man formulated the American image of democracy. From +the townships had come the power that had carried the American +Revolution to victory. From the townships were to come the votes that +carried Jefferson's party to power. Out there in the farming +communities of Massachusetts and Virginia, if you wore glasses that +obliterated the slaves, you could see with your mind's eye the image +of what democracy was to be. + +"The American Revolution broke out," says de Tocqueville, [Footnote: +_Democracy in America,_ Vol. I, p. 51. Third Edition] "and the +doctrine of the sovereignty of the people, which had been nurtured in +the townships, took possession of the state." It certainly took +possession of the minds of those men who formulated and popularized +the stereotypes of democracy. "The cherishment of the people was our +principle," wrote Jefferson. [Footnote: Cited in Charles Beard, +_Economic Origins of Jeffersonian Democracy._ Ch. XIV. ] But the +people he cherished almost exclusively were the small landowning +farmers: "Those who labor in the earth are the chosen people of God, +if ever He had a chosen people, whose breasts He has made his peculiar +deposit for substantial and genuine virtue. It is the focus in which +He keeps alive that sacred fire, which otherwise might escape from the +face of the earth. Corruption of morals in the mass of cultivators is +a phenomenon of which no age nor nation has furnished an example." + +However much of the romantic return to nature may have entered into +this exclamation, there was also an element of solid sense. Jefferson +was right in thinking that a group of independent farmers comes nearer +to fulfilling the requirements of spontaneous democracy than any other +human society. But if you are to preserve the ideal, you must fence +off these ideal communities from the abominations of the world. If the +farmers are to manage their own affairs, they must confine affairs to +those they are accustomed to managing. Jefferson drew all these +logical conclusions. He disapproved of manufacture, of foreign +commerce, and a navy, of intangible forms of property, and in theory +of any form of government that was not centered in the small +self-governing group. He had critics in his day: one of them remarked +that "wrapt up in the fullness of self-consequence and strong enough, +in reality, to defend ourselves against every invader, we might enjoy +an eternal rusticity and live, forever, thus apathized and vulgar +under the shelter of a selfish, satisfied indifference." [Footnote: +_Op. cit_., p. 426.] + +4 + +The democratic ideal, as Jefferson moulded it, consisting of an ideal +environment and a selected class, did not conflict with the political +science of his time. It did conflict with the realities. And when the +ideal was stated in absolute terms, partly through exuberance and +partly for campaign purposes, it was soon forgotten that the theory +was originally devised for very special conditions. It became the +political gospel, and supplied the stereotypes through which Americans +of all parties have looked at politics. + +That gospel was fixed by the necessity that in Jefferson's time no one +could have conceived public opinions that were not spontaneous and +subjective. The democratic tradition is therefore always trying to see +a world where people are exclusively concerned with affairs of which +the causes and effects all operate within the region they inhabit. +Never has democratic theory been able to conceive itself in the +context of a wide and unpredictable environment. The mirror is +concave. And although democrats recognize that they are in contact +with external affairs, they see quite surely that every contact +outside that self-contained group is a threat to democracy as +originally conceived. That is a wise fear. If democracy is to be +spontaneous, the interests of democracy must remain simple, +intelligible, and easily managed. Conditions must approximate those of +the isolated rural township if the supply of information is to be left +to casual experience. The environment must be confined within the +range of every man's direct and certain knowledge. + +The democrat has understood what an analysis of public opinion seems +to demonstrate: that in dealing with an unseen environment decisions +"are manifestly settled at haphazard, which clearly they ought not to +be." [Footnote: Aristotle, _Politics_, Bk. VII, Ch. IV.] So he +has always tried in one way or another to minimize the importance of +that unseen environment. He feared foreign trade because trade +involves foreign connections; he distrusted manufactures because they +produced big cities and collected crowds; if he had nevertheless to +have manufactures, he wanted protection in the interest of +self-sufficiency. When he could not find these conditions in the real +world, he went passionately into the wilderness, and founded Utopian +communities far from foreign contacts. His slogans reveal his +prejudice. He is for Self-Government, Self-Determination, +Independence. Not one of these ideas carries with it any notion of +consent or community beyond the frontiers of the self-governing +groups. The field of democratic action is a circumscribed area. Within +protected boundaries the aim has been to achieve self-sufficiency and +avoid entanglement. This rule is not confined to foreign policy, but +it is plainly evident there, because life outside the national +boundaries is more distinctly alien than any life within. And as +history shows, democracies in their foreign policy have had generally +to choose between splendid isolation and a diplomacy that violated +their ideals. The most successful democracies, in fact, Switzerland, +Denmark, Australia, New Zealand, and America until recently, have had +no foreign policy in the European sense of that phrase. Even a rule +like the Monroe Doctrine arose from the desire to supplement the two +oceans by a glacis of states that were sufficiently republican to have +no foreign policy. + +Whereas danger is a great, perhaps an indispensable condition of +autocracy, [Footnote: Fisher Ames, frightened by the democratic +revolution of 1800, wrote to Rufus King in 1802: "We need, as all +nations do, the compression on the outside of our circle of a +formidable neighbor, whose presence shall at all times excite stronger +fears than demagogues can inspire the people with towards their +government." Cited by Ford, _Rise and Growth of American +Politics,_ p. 69.] security was seen to be a necessity if democracy +was to work. There must be as little disturbance as possible of the +premise of a self-contained community. Insecurity involves surprises. +It means that there are people acting upon your life, over whom you +have no control, with whom you cannot consult. It means that forces +are at large which disturb the familiar routine, and present novel +problems about which quick and unusual decisions are required. Every +democrat feels in his bones that dangerous crises are incompatible +with democracy, because he knows that the inertia of masses is such +that to act quickly a very few must decide and the rest follow rather +blindly. This has not made non-resistants out of democrats, but it has +resulted in all democratic wars being fought for pacifist aims. Even +when the wars are in fact wars of conquest, they are sincerely +believed to be wars in defense of civilization. + +These various attempts to enclose a part of the earth's surface were +not inspired by cowardice, apathy, or, what one of Jefferson's critics +called a willingness to live under monkish discipline. The democrats +had caught sight of a dazzling possibility, that every human being +should rise to his full stature, freed from man-made limitations. With +what they knew of the art of government, they could, no more than +Aristotle before them, conceive a society of autonomous individuals, +except an enclosed and simple one. They could, then, select no other +premise if they were to reach the conclusion that all the people could +spontaneously manage their public affairs. + +5 + +Having adopted the premise because it was necessary to their keenest +hope, they drew other conclusions as well. Since in order to have +spontaneous self-government, you had to have a simple self-contained +community, they took it for granted that one man was as competent as +the next to manage these simple and self-contained affairs. Where the +wish is father to the thought such logic is convincing. Moreover, the +doctrine of the omnicompetent citizen is for most practical purposes +true in the rural township. Everybody in a village sooner or later +tries his hand at everything the village does. There is rotation in +office by men who are jacks of all trades. There was no serious +trouble with the doctrine of the omnicompetent citizen until the +democratic stereotype was universally applied, so that men looked at a +complicated civilization and saw an enclosed village. + +Not only was the individual citizen fitted to deal with all public +affairs, but he was consistently public-spirited and endowed with +unflagging interest. He was public-spirited enough in the township, +where he knew everybody and was interested in everybody's business. +The idea of enough for the township turned easily into the idea of +enough for any purpose, for as we have noted, quantitative thinking +does not suit a stereotype. But there was another turn to the circle. +Since everybody was assumed to be interested enough in important +affairs, only those affairs came to seem important in which everybody +was interested. + +This meant that men formed their picture of the world outside from the +unchallenged pictures in their heads. These pictures came to them well +stereotyped by their parents and teachers, and were little corrected +by their own experience. Only a few men had affairs that took them +across state lines. Even fewer had reason to go abroad. Most voters +lived their whole lives in one environment, and with nothing but a few +feeble newspapers, some pamphlets, political speeches, their religious +training, and rumor to go on, they had to conceive that larger +environment of commerce and finance, of war and peace. The number of +public opinions based on any objective report was very small in +proportion to those based on casual fancy. + +And so for many different reasons, self-sufficiency was a spiritual +ideal in the formative period. The physical isolation of the township, +the loneliness of the pioneer, the theory of democracy, the Protestant +tradition, and the limitations of political science all converged to +make men believe that out of their own consciences they must extricate +political wisdom. It is not strange that the deduction of laws from +absolute principles should have usurped so much of their free energy. +The American political mind had to live on its capital. In legalism it +found a tested body of rules from which new rules could be spun +without the labor of earning new truths from experience. The formulae +became so curiously sacred that every good foreign observer has been +amazed at the contrast between the dynamic practical energy of the +American people and the static theorism of their public life. That +steadfast love of fixed principles was simply the only way known of +achieving self-sufficiency. But it meant that the public opinions of +any one community about the outer world consisted chiefly of a few +stereotyped images arranged in a pattern deduced from their legal and +their moral codes, and animated by the feeling aroused by local +experiences. + +Thus democratic theory, starting from its fine vision of ultimate +human dignity, was forced by lack of the instruments of knowledge for +reporting its environment, to fall back upon the wisdom and experience +which happened to have accumulated in the voter. God had, in the words +of Jefferson, made men's breasts "His peculiar deposit for substantial +and genuine virtue." These chosen people in their self-contained +environment had all the facts before them. The environment was so +familiar that one could take it for granted that men were talking +about substantially the same things. The only real disagreements, +therefore, would be in judgments about the same facts. There was no +need to guarantee the sources of information. They were obvious, and +equally accessible to all men. Nor was there need to trouble about the +ultimate criteria. In the self-contained community one could assume, +or at least did assume, a homogeneous code of morals. The only place, +therefore, for differences of opinion was in the logical application +of accepted standards to accepted facts. And since the reasoning +faculty was also well standardized, an error in reasoning would be +quickly exposed in a free discussion. It followed that truth could be +obtained by liberty within these limits. The community could take its +supply of information for granted; its codes it passed on through +school, church, and family, and the power to draw deductions from a +premise, rather than the ability to find the premise, was regarded as +the chief end of intellectual training. + + + + +CHAPTER XVIII + +THE ROLE OF FORCE, PATRONAGE AND PRIVILEGE + +1 + +"IT has happened as was to have been foreseen," wrote Hamilton, +[Footnote: _Federalist,_ No. 15] "the measures of the Union have +not been executed; the delinquencies of the States have, step by step, +matured themselves to an extreme which has at length arrested all the +wheels of the national government and brought them to an awful +stand."... For "in our case the concurrence of thirteen distinct +sovereign wills is requisite, under the confederation, to the complete +execution of every important measure that proceeds from the Union." +How could it be otherwise, he asked: "The rulers of the respective +members... will undertake to judge of the propriety of the measures +themselves. They will consider the conformity of the thing proposed or +required to their immediate interests or aims; the momentary +conveniences or inconveniences that would attend its adoption. All +this will be done, and in a spirit of interested and suspicious +scrutiny, without that knowledge of national circumstances and reasons +of state which is essential to right judgment, and with that strong +predilection in favor of local objects which can hardly fail to +mislead the decision. The same process must be repeated in every +member of which the body is constituted; and the execution of the +plans framed by the councils of the whole, will always fluctuate on +the discretion of the ill-informed and prejudiced opinion of every +part. Those who have been conversant in the proceedings of popular +assemblies, who have seen how difficult it often is, when there is no +exterior pressure of circumstances, to bring them to harmonious +resolutions on important points, will readily conceive how impossible +it must be to induce a number of such assemblies, deliberating at a +distance from each other, at different times, and under different +impressions, long to coöperate in the same views and pursuits." + +Over ten years of storm and stress with a congress that was, as John +Adams said, [Footnote: Ford, _op. cit._, p. 36.] "only a diplomatic +assembly," had furnished the leaders of the revolution "with an +instructive but afflicting lesson" [Footnote: _Federalist_, No. 15.] +in what happens when a number of self-centered communities +are entangled in the same environment. And so, when they went +to Philadelphia in May of 1787, ostensibly to revise the Articles of +Confederation, they were really in full reaction against the +fundamental premise of Eighteenth Century democracy. Not only +were the leaders consciously opposed to the democratic spirit of +the time, feeling, as Madison said, that "democracies have ever +been spectacles of turbulence and contention," but within the +national frontiers they were determined to offset as far as they could +the ideal of self-governing communities in self-contained environments. +The collisions and failures of concave democracy, where men +spontaneously managed all their own affairs, were before their eyes. +The problem as they saw it, was to restore government as against +democracy. They understood government to be the power to make +national decisions and enforce them throughout the nation; +democracy they believed was the insistence of localities and classes +upon self-determination in accordance with their immediate interests +and aims. + +They could not consider in their calculations the possibility of such +an organization of knowledge that separate communities would act +simultaneously on the same version of the facts. We just begin to +conceive this possibility for certain parts of the world where there +is free circulation of news and a common language, and then only for +certain aspects of life. The whole idea of a voluntary federalism in +industry and world politics is still so rudimentary, that, as we see +in our own experience, it enters only a little, and only very +modestly, into practical politics. What we, more than a century later, +can only conceive as an incentive to generations of intellectual +effort, the authors of the Constitution had no reason to conceive at +all. In order to set up national government, Hamilton and his +colleagues had to make plans, not on the theory that men would +coöperate because they had a sense of common interest, but on the +theory that men could be governed, if special interests were kept in +equilibrium by a balance of power. "Ambition," Madison said, +[Footnote: _Federalist_, No. 51, cited by Ford, _op. cit._, +p. 60.] "must be made to counteract ambition." + +They did not, as some writers have supposed, intend to balance every +interest so that the government would be in a perpetual deadlock. They +intended to deadlock local and class interest to prevent these from +obstructing government. "In framing a government which is to be +administered by men over men," wrote Madison, [Footnote: _Id_.] +"the great difficulty lies in this: _you must first enable the +government to control the governed_, and in the next place, oblige +it to control itself." In one very important sense, then, the doctrine +of checks and balances was the remedy of the federalist leaders for +the problem of public opinion. They saw no other way to substitute +"the mild influence of the magistracy" for the "sanguinary agency of +the sword" [Footnote: _Federalist, No. 15.] except by devising an +ingenious machine to neutralize local opinion. They did not understand +how to manipulate a large electorate, any more than they saw the +possibility of common consent upon the basis of common information. It +is true that Aaron Burr taught Hamilton a lesson which impressed him a +good deal when he seized control of New York City in 1800 by the aid +of Tammany Hall. But Hamilton was killed before he was able to take +account of this new discovery, and, as Mr. Ford says, [Footnote: Ford, +_op. cit._, p. 119.] Burr's pistol blew the brains out of the +Federal party. + +2 + +When the constitution was written, "politics could still be managed by +conference and agreement among gentlemen" [Footnote: _Op. cit._, +p. 144] and it was to the gentry that Hamilton turned for a +government. It was intended that they should manage national affairs +when local prejudice had been brought into equilibrium by the +constitutional checks and balances. No doubt Hamilton, who belonged to +this class by adoption, had a human prejudice in their favor. But that +by itself is a thin explanation of his statecraft. Certainly there can +be no question of his consuming passion for union, and it is, I think, +an inversion of the truth to argue that he made the Union to protect +class privileges, instead of saying that he used class privileges to +make the Union. "We must take man as we find him," Hamilton said, "and +if we expect him to serve the public we must interest his passions in +doing so." [Footnote: _Op. cit._, p. 47] He needed men to govern, +whose passions could be most quickly attached to a national interest. +These were the gentry, the public creditors, manufacturers, shippers, +and traders, [Footnote: Beard, _Economic Interpretation of the +Constitution, passim._] and there is probably no better instance in +history of the adaptation of shrewd means to clear ends, than in the +series of fiscal measures, by which Hamilton attached the provincial +notables to the new government. + +Although the constitutional convention worked behind closed doors, and +although ratification was engineered by "a vote of probably not more +than one-sixth of the adult males," [Footnote: Beard, _op. cit._, +p. 325.] there was little or no pretence. The Federalists argued for +union, not for democracy, and even the word republic had an unpleasant +sound to George Washington when he had been for more than two years a +republican president. The constitution was a candid attempt to limit +the sphere of popular rule; the only democratic organ it was intended +the government should possess was the House, based on a suffrage +highly limited by property qualifications. And even at that, the +House, it was believed, would be so licentious a part of the +government, that it was carefully checked and balanced by the Senate, +the electoral college, the Presidential veto, and by judicial +interpretation. + +Thus at the moment when the French Revolution was kindling popular +feeling the world over, the American revolutionists of 1776 came under +a constitution which went back, as far as it was expedient, to the +British Monarchy for a model. This conservative reaction could not +endure. The men who had made it were a minority, their motives were +under suspicion, and when Washington went into retirement, the +position of the gentry was not strong enough to survive the inevitable +struggle for the succession. The anomaly between the original plan of +the Fathers and the moral feeling of the age was too wide not to be +capitalized by a good politician. + +3 + +Jefferson referred to his election as "the great revolution of 1800," +but more than anything else it was a revolution in the mind. No great +policy was altered, but a new tradition was established. For it was +Jefferson who first taught the American people to regard the +Constitution as an instrument of democracy, and he stereotyped the +images, the ideas, and even many of the phrases, in which Americans +ever since have described politics to each other. So complete was the +mental victory, that twenty-five years later de Tocqueville, who was +received in Federalist homes, noted that even those who were "galled +by its continuance"--were not uncommonly heard to "laud the delights +of a republican government, and the advantages of democratic +institutions when they are in public." [Footnote: _Democracy in +America_, Vol. I, Ch. X (Third Edition, 1838), p. 216.] + +The Constitutional Fathers with all their sagacity had failed to see +that a frankly undemocratic constitution would not long be tolerated. +The bold denial of popular rule was bound to offer an easy point of +attack to a man, like Jefferson, who so far as his constitutional +opinions ran, was not a bit more ready than Hamilton to turn over +government to the "unrefined" will of the people. [Footnote: +_Cf._ his plan for the Constitution of Virginia, his ideas for a +senate of property holders, and his views on the judicial veto. Beard, +_Economic Origins of Jeffersonian Democracy_, pp. 450 _et +seq._] The Federalist leaders had been men of definite convictions +who stated them bluntly. There was little real discrepancy between +their public and their private views. But Jefferson's mind was a mass +of ambiguities, not solely because of its defects, as Hamilton and his +biographers have thought, but because he believed in a union and he +believed in spontaneous democracies, and in the political science of +his age there was no satisfactory way to reconcile the two. Jefferson +was confused in thought and action because he had a vision of a new +and tremendous idea that no one had thought out in all its bearings. +But though popular sovereignty was not clearly understood by anybody, +it seemed to imply so great an enhancement of human life, that no +constitution could stand which frankly denied it. The frank denials +were therefore expunged from consciousness, and the document, which is +on its face an honest example of limited constitutional democracy, was +talked and thought about as an instrument for direct popular rule. +Jefferson actually reached the point of believing that the Federalists +had perverted the Constitution, of which in his fancy they were no +longer the authors. And so the Constitution was, in spirit, rewritten. +Partly by actual amendment, partly by practice, as in the case of the +electoral college, but chiefly by looking at it through another set of +stereotypes, the facade was no longer permitted to look oligarchic. + +The American people came to believe that their Constitution was a +democratic instrument, and treated it as such. They owe that fiction +to the victory of Thomas Jefferson, and a great conservative fiction +it has been. It is a fair guess that if everyone had always regarded +the Constitution as did the authors of it, the Constitution would have +been violently overthrown, because loyalty to the Constitution and +loyalty to democracy would have seemed incompatible. Jefferson +resolved that paradox by teaching the American people to read the +Constitution as an expression of democracy. He himself stopped there. +But in the course of twenty-five years or so social conditions had +changed so radically, that Andrew Jackson carried out the political +revolution for which Jefferson had prepared the tradition. [Footnote: +The reader who has any doubts as to the extent of the revolution that +separated Hamilton's opinions from Jackson's practice should turn to +Mr. Henry Jones Ford's _Rise and Growth of American Politics_.] + +4 + +The political center of that revolution was the question of patronage. +By the men who founded the government public office was regarded as a +species of property, not lightly to be disturbed, and it was +undoubtedly their hope that the offices would remain in the hands of +their social class. But the democratic theory had as one of its main +principles the doctrine of the omnicompetent citizen. Therefore, when +people began to look at the Constitution as a democratic instrument, +it was certain that permanence in office would seem undemocratic. The +natural ambitions of men coincided here with the great moral impulse +of their age. Jefferson had popularized the idea without carrying it +ruthlessly into practice, and removals on party grounds were +comparatively few under the Virginian Presidents. It was Jackson who +founded the practice of turning public office into patronage. + +Curious as it sounds to us, the principle of rotation in office with +short terms was regarded as a great reform. Not only did it +acknowledge the new dignity of the average man by treating him as fit +for any office, not only did it destroy the monopoly of a small social +class and appear to open careers to talent, but "it had been advocated +for centuries as a sovereign remedy for political corruption," and as +the one way to prevent the creation of a bureaucracy. [Footnote: Ford, +_op. cit._, p. 169.] The practice of rapid change in public +office was the application to a great territory of the image of +democracy derived from the self-contained village. + +Naturally it did not have the same results in the nation that it had +in the ideal community on which the democratic theory was based. It +produced quite unexpected results, for it founded a new governing +class to take the place of the submerged federalists. Unintentionally, +patronage did for a large electorate what Hamilton's fiscal measures +had done for the upper classes. We often fail to realize how much of +the stability of our government we owe to patronage. For it was +patronage that weaned natural leaders from too much attachment to the +self-centered community, it was patronage that weakened the local +spirit and brought together in some kind of peaceful cooperation, the +very men who, as provincial celebrities, would, in the absence of a +sense of common interest, have torn the union apart. + +But of course, the democratic theory was not supposed to produce a new +governing class, and it has never accommodated itself to the fact. +When the democrat wanted to abolish monopoly of offices, to have +rotation and short terms, he was thinking of the township where anyone +could do a public service, and return humbly to his own farm. The idea +of a special class of politicians was just what the democrat did not +like. But he could not have what he did like, because his theory was +derived from an ideal environment, and he was living in a real one. +The more deeply he felt the moral impulse of democracy, the less ready +he was to see the profound truth of Hamilton's statement that +communities deliberating at a distance and under different impressions +could not long coöperate in the same views and pursuits. For that +truth postpones anything like the full realization of democracy in +public affairs until the art of obtaining common consent has been +radically improved. And so while the revolution under Jefferson and +Jackson produced the patronage which made the two party system, which +created a substitute for the rule of the gentry, and a discipline for +governing the deadlock of the checks and balances, all that happened, +as it were, invisibly. + +Thus, rotation in office might be the ostensible theory, in practice +the offices oscillated between the henchmen. Tenure might not be a +permanent monopoly, but the professional politician was permanent. +Government might be, as President Harding once said, a simple thing, +but winning elections was a sophisticated performance. The salaries in +office might be as ostentatiously frugal as Jefferson's home-spun, but +the expenses of party organization and the fruits of victory were in +the grand manner. The stereotype of democracy controlled the visible +government; the corrections, the exceptions and adaptations of the +American people to the real facts of their environment have had to be +invisible, even when everybody knew all about them. It was only the +words of the law, the speeches of politicians, the platforms, and the +formal machinery of administration that have had to conform to the +pristine image of democracy. + +5 + +If one had asked a philosophical democrat how these self-contained +communities were to coöperate, when their public opinions were so +self-centered, he would have pointed to representative government +embodied in the Congress. And nothing would surprise him more than the +discovery of how steadily the prestige of representative government +has declined, while the power of the Presidency has grown. + +Some critics have traced this to the custom of sending only local +celebrities to Washington. They have thought that if Congress could +consist of the nationally eminent men, the life of the capital would +be more brilliant. It would be, of course, and it would be a very good +thing if retiring Presidents and Cabinet officers followed the example +of John Quincy Adams. But the absence of these men does not explain +the plight of Congress, for its decline began when it was relatively +the most eminent branch of the government. Indeed it is more probable +that the reverse is true, and that Congress ceased to attract the +eminent as it lost direct influence on the shaping of national policy. + +The main reason for the discredit, which is world wide, is, I think, +to be found in the fact that a congress of representatives is +essentially a group of blind men in a vast, unknown world. With some +exceptions, the only method recognized in the Constitution or in the +theory of representative government, by which Congress can inform +itself, is to exchange opinions from the districts. There is no +systematic, adequate, and authorized way for Congress to know what is +going on in the world. The theory is that the best man of each +district brings the best wisdom of his constituents to a central +place, and that all these wisdoms combined are all the wisdom that +Congress needs. Now there is no need to question the value of +expressing local opinions and exchanging them. Congress has great +value as the market-place of a continental nation. In the coatrooms, +the hotel lobbies, the boarding houses of Capitol Hill, at the +tea-parties of the Congressional matrons, and from occasional entries +into the drawing rooms of cosmopolitan Washington, new vistas are +opened, and wider horizons. But even if the theory were applied, and +the districts always sent their wisest men, the sum or a combination +of local impressions is not a wide enough base for national policy, +and no base at all for the control of foreign policy. Since the real +effects of most laws are subtle and hidden, they cannot be understood +by filtering local experiences through local states of mind. They can +be known only by controlled reporting and objective analysis. And just +as the head of a large factory cannot know how efficient it is by +talking to the foreman, but must examine cost sheets and data that +only an accountant can dig out for him, so the lawmaker does not +arrive at a true picture of the state of the union by putting together +a mosaic of local pictures. He needs to know the local pictures, but +unless he possesses instruments for calibrating them, one picture is +as good as the next, and a great deal better. + +The President does come to the assistance of Congress by delivering +messages on the state of the Union. He is in a position to do that +because he presides over a vast collection of bureaus and their +agents, which report as well as act. But he tells Congress what he +chooses to tell it. He cannot be heckled, and the censorship as to +what is compatible with the public interest is in his hands. It is a +wholly one-sided and tricky relationship, which sometimes reaches such +heights of absurdity, that Congress, in order to secure an important +document has to thank the enterprise of a Chicago newspaper, or the +calculated indiscretion of a subordinate official. So bad is the +contact of legislators with necessary facts that they are forced to +rely either on private tips or on that legalized atrocity, the +Congressional investigation, where Congressmen, starved of their +legitimate food for thought, go on a wild and feverish man-hunt, and +do not stop at cannibalism. + +Except for the little that these investigations yield, the occasional +communications from the executive departments, interested and +disinterested data collected by private persons, such newspapers, +periodicals, and books as Congressmen read, and a new and excellent +practice of calling for help from expert bodies like the Interstate +Commerce Commission, the Federal Trade Commission, and the Tariff +Commission, the creation of Congressional opinion is incestuous. From +this it follows either that legislation of a national character is +prepared by a few informed insiders, and put through by partisan +force; or that the legislation is broken up into a collection of local +items, each of which is enacted for a local reason. Tariff schedules, +navy yards, army posts, rivers and harbors, post offices and federal +buildings, pensions and patronage: these are fed out to concave +communities as tangible evidence of the benefits of national life. +Being concave, they can see the white marble building which rises out +of federal funds to raise local realty values and employ local +contractors more readily than they can judge the cumulative cost of +the pork barrel. It is fair to say that in a large assembly of men, +each of whom has practical knowledge only of his own district, laws +dealing with translocal affairs are rejected or accepted by the mass +of Congressmen without creative participation of any kind. They +participate only in making those laws that can be treated as a bundle +of local issues. For a legislature without effective means of +information and analysis must oscillate between blind regularity, +tempered by occasional insurgency, and logrolling. And it is the +logrolling which makes the regularity palatable, because it is by +logrolling that a Congressman proves to his more active constituents +that he is watching their interests as they conceive them. + +This is no fault of the individual Congressman's, except when he is +complacent about it. The cleverest and most industrious representative +cannot hope to understand a fraction of the bills on which he votes. +The best he can do is to specialize on a few bills, and take +somebody's word about the rest. I have known Congressmen, when they +were boning up on a subject, to study as they had not studied since +they passed their final examinations, many large cups of black coffee, +wet towels and all. They had to dig for information, sweat over +arranging and verifying facts, which, in any consciously organized +government, should have been easily available in a form suitable for +decision. And even when they really knew a subject, their anxieties +had only begun. For back home the editors, the board of trade, the +central federated union, and the women's clubs had spared themselves +these labors, and were prepared to view the Congressman's performance +through local spectacles. + +6 + +What patronage did to attach political chieftains to the national +government, the infinite variety of local subsidies and privileges do +for self-centered communities. Patronage and pork amalgamate and +stabilize thousands of special opinions, local discontents, private +ambitions. There are but two other alternatives. One is government by +terror and obedience, the other is government based on such a highly +developed system of information, analysis, and self-consciousness that +"the knowledge of national circumstances and reasons of state" is +evident to all men. The autocratic system is in decay, the voluntary +system is in its very earliest development; and so, in calculating the +prospects of association among large groups of people, a League of +Nations, industrial government, or a federal union of states, the +degree to which the material for a common consciousness exists, +determines how far cooperation will depend upon force, or upon the +milder alternative to force, which is patronage and privilege. The +secret of great state-builders, like Alexander Hamilton, is that they +know how to calculate these principles. + + + + +CHAPTER XIX + +THE OLD IMAGE IN A NEW FORM: GUILD SOCIALISM. + +Whenever the quarrels of self-centered groups become unbearable, +reformers in the past found themselves forced to choose between two +great alternatives. They could take the path to Rome and impose a +Roman peace upon the warring tribes. They could take the path to +isolation, to autonomy and self-sufficiency. Almost always they chose +that path which they had least recently travelled. If they had tried +out the deadening monotony of empire, they cherished above all other +things the simple freedom of their own community. But if they had seen +this simple freedom squandered in parochial jealousies they longed for +the spacious order of a great and powerful state. + +Whichever choice they made, the essential difficulty was the same. If +decisions were decentralized they soon floundered in a chaos of local +opinions. If they were centralized, the policy of the state was based +on the opinions of a small social set at the capital. In any case +force was necessary to defend one local right against another, or to +impose law and order on the localities, or to resist class government +at the center, or to defend the whole society, centralized or +decentralized, against the outer barbarian. + +Modern democracy and the industrial system were both born in a time of +reaction against kings, crown government, and a regime of detailed +economic regulation. In the industrial sphere this reaction took the +form of extreme devolution, known as laissez-faire individualism. Each +economic decision was to be made by the man who had title to the +property involved. Since almost everything was owned by somebody, +there would be somebody to manage everything. This was plural +sovereignty with a vengeance. + +It was economic government by anybody's economic philosophy, though it +was supposed to be controlled by immutable laws of political economy +that must in the end produce harmony. It produced many splendid +things, but enough sordid and terrible ones to start counter-currents. +One of these was the trust, which established a kind of Roman peace +within industry, and a Roman predatory imperialism outside. People +turned to the legislature for relief. They invoked representative +government, founded on the image of the township farmer, to regulate +the semi-sovereign corporations. The working class turned to labor +organization. There followed a period of increasing centralization and +a sort of race of armaments. The trusts interlocked, the craft unions +federated and combined into a labor movement, the political system +grew stronger at Washington and weaker in the states, as the reformers +tried to match its strength against big business. + +In this period practically all the schools of socialist thought from +the Marxian left to the New Nationalists around Theodore Roosevelt, +looked upon centralization as the first stage of an evolution which +would end in the absorption of all the semi-sovereign powers of +business by the political state. The evolution never took place, +except for a few months during the war. That was enough, and there was +a turn of the wheel against the omnivorous state in favor of several +new forms of pluralism. But this time society was to swing back not to +the atomic individualism of Adam Smith's economic man and Thomas +Jefferson's farmer, but to a sort of molecular individualism of +voluntary groups. + +One of the interesting things about all these oscillations of theory +is that each in turn promises a world in which no one will have to +follow Machiavelli in order to survive. They are all established by +some form of coercion, they all exercise coercion in order to maintain +themselves, and they are all discarded as a result of coercion. Yet +they do not accept coercion, either physical power or special +position, patronage, or privilege, as part of their ideal. The +individualist said that self-enlightened self-interest would bring +internal and external peace. The socialist is sure that the motives to +aggression will disappear. The new pluralist hopes they +will. [Footnote: See G. D. H. Cole, _Social Theory,_ p. 142.] +Coercion is the surd in almost all social theory, except the +Machiavellian. The temptation to ignore it, because it is absurd, +inexpressible, and unmanageable, becomes overwhelming in any man who +is trying to rationalize human life. + +2 + +The lengths to which a clever man will sometimes go in order to escape +a full recognition of the role of force is shown by Mr. G. D. H. +Cole's book on Guild Socialism. The present state, he says, "is +primarily an instrument of coercion;" [Footnote: Cole, _Guild +Socialism_, p. 107.] in a guild socialist society there will be no +sovereign power, though there will be a coordinating body. He calls +this body the Commune. + +He then begins to enumerate the powers of the Commune, which, we +recall, is to be primarily not an instrument of coercion. [Footnote: +_Op. cit._ Ch. VIII.] It settles price disputes. Sometimes it +fixes prices, allocates the surplus or distributes the loss. It +allocates natural resources, and controls the issue of credit. It also +"allocates communal labor-power." It ratifies the budgets of the +guilds and the civil services. It levies taxes. "All questions of +income" fall within its jurisdiction. It "allocates" income to the +non-productive members of the community. It is the final arbiter in +all questions of policy and jurisdiction between the guilds. It passes +constitutional laws fixing the functions of the functional bodies. It +appoints the judges. It confers coercive powers upon the guilds, and +ratifies their by-laws wherever these involve coercion. It declares +war and makes peace. It controls the armed forces. It is the supreme +representative of the nation abroad. It settles boundary questions +within the national state. It calls into existence new functional +bodies, or distributes new functions to old ones. It runs the police. +It makes whatever laws are necessary to regulate personal conduct and +personal property. + +These powers are exercised not by one commune, but by a federal +structure of local and provincial communes with a National commune at +the top. Mr. Cole is, of course, welcome to insist that this is not a +sovereign state, but if there is a coercive power now enjoyed by any +modern government for which he has forgotten to make room, I cannot +think of it. + +He tells us, however, that Guild society will be non-coercive: "we +want to build a new society which will be conceived in the spirit, not +of coercion, but of free service." [Footnote: _Op. cit._, p. +141.] Everyone who shares that hope, as most men and women do, will +therefore look closely to see what there is in the Guild Socialist +plan which promises to reduce coercion to its lowest limits, even +though the Guildsmen of to-day have already reserved for their +communes the widest kind of coercive power. It is acknowledged at once +that the new society cannot be brought into existence by universal +consent. Mr. Cole is too honest to shirk the element of force required +to make the transition. [Footnote: _Cf. op. cit._, Ch. X. ] And +while obviously he cannot predict how much civil war there might be, +he is quite clear that there would have to be a period of direct +action by the trade unions. + +3 + +But leaving aside the problems of transition, and any consideration of +what the effect is on their future action, when men have hacked their +way through to the promised land, let us imagine the Guild Society in +being. What keeps it running as a non-coercive society? + +Mr. Cole has two answers to this question. One is the orthodox Marxian +answer that the abolition of capitalist property will remove the +motive to aggression. Yet he does not really believe that, because if +he did, he would care as little as does the average Marxian how the +working class is to run the government, once it is in control. If his +diagnosis were correct, the Marxian would be quite right: if the +disease were the capitalist class and only the capitalist class, +salvation would automatically follow its extinction. But Mr. Cole is +enormously concerned about whether the society which follows the +revolution is to be run by state collectivism, by guilds or +cooperative societies, by a democratic parliament or by functional +representation. In fact, it is as a new theory of representative +government that guild socialism challenges attention. + +The guildsmen do not expect a miracle to result from the disappearance +of capitalist property rights. They do expect, and of course quite +rightly, that if equality of income were the rule, social relations +would be profoundly altered. But they differ, as far as I can make +out, from the orthodox Russian communist in this respect: The +communist proposes to establish equality by force of the dictatorship +of the proletariat, believing that if once people were equalized both +in income and in service, they would then lose the incentives to +aggression. The guildsmen also propose to establish equality by force, +but are shrewd enough to see that if an equilibrium is to be +maintained they have to provide institutions for maintaining it. +Guildsmen, therefore, put their faith in what they believe to be a new +theory of democracy. + +Their object, says Mr. Cole, is "to get the mechanism right, and to +adjust it as far as possible to the expression of men's social wills." +[Reference: _Op. cit._, p. 16.] These wills need to be given +opportunity for self-expression in self-government "in any and every +form of social action." Behind these words is the true democratic +impulse, the desire to enhance human dignity, as well as the +traditional assumption that this human dignity is impugned, unless +each person's will enters into the management of everything that +affects him. The guildsman, like the earlier democrat therefore, looks +about him for an environment in which this ideal of self-government +can be realized. A hundred years and more have passed since Rousseau +and Jefferson, and the center of interest has shifted from the country +to the city. The new democrat can no longer turn to the idealized +rural township for the image of democracy. He turns now to the +workshop. "The spirit of association must be given free play in the +sphere in which it is best able to find expression. This is manifestly +the factory, in which men have the habit and tradition of working +together. The factory is the natural and fundamental unit of +industrial democracy. This involves, not only that the factory must be +free, as far as possible, to manage its own affairs, but also that the +democratic unit of the factory must be made the basis of the larger +democracy of the Guild, and that the larger organs of Guild +administration and government must be based largely on the principle +of factory representation." [Footnote: _Op. cit._, p. 40.] + +Factory is, of course, a very loose word, and Mr. Cole asks us to take +it as meaning mines, shipyards, docks, stations, and every place which +is "a natural center of production." [Footnote: _Op. cit._, p. +41] But a factory in this sense is quite a different thing from an +industry. The factory, as Mr. Cole conceives it, is a work place where +men are really in personal contact, an environment small enough to be +known directly to all the workers. "This democracy if it is to be +real, must come home to, and be exercisable directly by, every +individual member of the Guild." [Footnote: _Op. cit._, p. 40.] +This is important, because Mr. Cole, like Jefferson, is seeking a +natural unit of government. The only natural unit is a perfectly +familiar environment. Now a large plant, a railway system, a great +coal field, is not a natural unit in this sense. Unless it is a very +small factory indeed, what Mr. Cole is really thinking about is the +shop. That is where men can be supposed to have "the habit and +tradition of working together." The rest of the plant, the rest of the +industry, is an inferred environment. + +4 + +Anybody can see, and almost everybody will admit, that self-government +in the purely internal affairs of the shop is government of affairs +that "can be taken in at a single view." [Footnote: Aristotle, +_Politics_, Bk. VII, Ch. IV.] But dispute would arise as to what +constitute the internal affairs of a shop. Obviously the biggest +interests, like wages, standards of production, the purchase of +supplies, the marketing of the product, the larger planning of work, +are by no means purely internal. The shop democracy has freedom, +subject to enormous limiting conditions from the outside. It can deal +to a certain extent with the arrangement of work laid out for the +shop, it can deal with the temper and temperament of individuals, it +can administer petty industrial justice, and act as a court of first +instance in somewhat larger individual disputes. Above all it can act +as a unit in dealing with other shops, and perhaps with the plant as a +whole. But isolation is impossible. The unit of industrial democracy +is thoroughly entangled in foreign affairs. And it is the management +of these external relations that constitutes the test of the guild +socialist theory. + +They have to be managed by representative government arranged in a +federal order from the shop to the plant, the plant to the industry, +the industry to the nation, with intervening regional grouping of +representatives. But all this structure derives from the shop, and all +its peculiar virtues are ascribed to this source. The representatives +who choose the representatives who choose the representatives who +finally "coordinate" and "regulate" the shops are elected, Mr. Cole +asserts, by a true democracy. Because they come originally from a +self-governing unit, the whole federal organism will be inspired by +the spirit and the reality of self-government. Representatives will +aim to carry out the workers' "actual will as understood by themselves," +[Footnote: _Op. cit._, p. 42.] that is, as understood by the +individual in the shops. + +A government run literally on this principle would, if history is any +guide, be either a perpetual logroll, or a chaos of warring shops. For +while the worker in the shop can have a real opinion about matters +entirely within the shop, his "will" about the relation of that shop +to the plant, the industry, and the nation is subject to all the +limitations of access, stereotype, and self-interest that surround any +other self-centered opinion. His experience in the shop at best brings +only aspects of the whole to his attention. His opinion of what is +right within the shop he can reach by direct knowledge of the +essential facts. His opinion of what is right in the great complicated +environment out of sight is more likely to be wrong than right if it +is a generalization from the experience of the individual shop. As a +matter of experience, the representatives of a guild society would +find, just as the higher trade union officials find today, that on a +great number of questions which they have to decide there is no +"actual will as understood" by the shops. + +5 + +The guildsmen insist, however, that such criticism is blind because it +ignores a great political discovery. You may be quite right, they +would say, in thinking that the representatives of the shops would +have to make up their own minds on many questions about which the +shops have no opinion. But you are simply entangled in an ancient +fallacy: you are looking for somebody to represent a group of people. +He cannot be found. The only representative possible is one who acts +for "some particular function," [Footnote: _Op. cit._, pp. 23-24.] +and therefore each person must help choose as many representatives "as +there are distinct essential groups of functions to be performed." + +Assume then that the representatives speak, not for the men in the +shops, but for certain functions in which the men are interested. They +are, mind you, disloyal if they do not carry out the will of the group +about the function, as understood by the group. [Footnote: _Cf._ +Part V, "The Making of a Common Will."] These functional +representatives meet. Their business is to coordinate and regulate. By +what standard does each judge the proposals of the other, assuming, as +we must, that there is conflict of opinion between the shops, since if +there were not, there would be no need to coordinate and regulate? + +Now the peculiar virtue of functional democracy is supposed to be that +men vote candidly according to their own interests, which it is +assumed they know by daily experience. They can do that within the +self-contained group. But in its external relations the group as a +whole, or its representative, is dealing with matters that transcend +immediate experience. The shop does not arrive spontaneously at a view +of the whole situation. Therefore, the public opinions of a shop about +its rights and duties in the industry and in society, are matters of +education or propaganda, not the automatic product of shop-consciousness. +Whether the guildsmen elect a delegate, or a representative, they do +not escape the problem of the orthodox democrat. Either the group +as a whole, or the elected spokesman, must stretch his mind beyond +the limits of direct experience. He must vote on questions coming up +from other shops, and on matters coming from beyond the frontiers of +the whole industry. The primary interest of the shop does not even +cover the function of a whole industrial vocation. The function of a +vocation, a great industry, a district, a nation is a concept, not an +experience, and has to be imagined, invented, taught and believed. +And even though you define function as carefully as possible, once +you admit that the view of each shop on that function will not +necessarily coincide with the view of other shops, you are saying +that the representative of one interest is concerned in the proposals +made by other interests. You are saying that he must conceive a +common interest. And in voting for him you are choosing a man who +will not simply represent your view of your function, which is all that +you know at first hand, but a man who will represent your views +about other people's views of that function. You are voting as +indefinitely as the orthodox democrat. + +6 + +The guildsmen in their own minds have solved the question of how to +conceive a common interest by playing with the word function. They +imagine a society in which all the main work of the world has been +analysed into functions, and these functions in turn synthesized +harmoniously. [Footnote: _Cf. op. cit._, Ch. XIX.] They suppose +essential agreement about the purposes of society as a whole, and +essential agreement about the role of every organized group in +carrying out those purposes. It was a nice sentiment, therefore, which +led them to take the name of their theory from an institution that +arose in a Catholic feudal society. But they should remember that the +scheme of function which the wise men of that age assumed was not +worked out by mortal man. It is unclear how the guildsmen think the +scheme is going to be worked out and made acceptable in the modern +world. Sometimes they seem to argue that the scheme will develop from +trade union organization, at other times that the communes will define +the constitutional function of the groups. But it makes a considerable +practical difference whether they believe that the groups define their +own functions or not. + +In either case, Mr. Cole assumes that society can be carried on by a +social contract based on an accepted idea of "distinct essential +groups of functions." How does one recognize these distinct essential +groups? So far as I can make out, Mr. Cole thinks that a function is +what a group of people are interested in. "The essence of functional +democracy is that a man should count as many times over as there are +functions in which he is interested." [Footnote: _Social Theory,_ +p. 102 _et seq._] Now there are at least two meanings to the word +interested. You can use it to mean that a man is involved, or that his +mind is occupied. John Smith, for example, may have been tremendously +interested in the Stillman divorce case. He may have read every word +of the news in every lobster edition. On the other hand, young Guy +Stillman, whose legitimacy was at stake, probably did not trouble +himself at all. John Smith was interested in a suit that did not +affect his "interests," and Guy was uninterested in one that would +determine the whole course of his life. Mr. Cole, I am afraid, leans +towards John Smith. He is answering the "very foolish objection" that +to vote by functions is to be voting very often: "If a man is not +interested enough to vote, and cannot be aroused to interest enough to +make him vote, on, say, a dozen distinct subjects, he waives his right +to vote and the result is no less democratic than if he voted blindly +and without interest." + +Mr. Cole thinks that the uninstructed voter "waives his right to +vote." From this it follows that the votes of the instructed reveal +their interest, and their interest defines the function. [Footnote: +_Cf._ Ch. XVIII of this book. "Since everybody was assumed to be +interested enough in important affairs, only those affairs came to +seem important in which everybody was interested."] "Brown, Jones, and +Robinson must therefore have, not one vote each, but as many different +functional votes as there are different questions calling for +associative action in which they are interested." [Footnote: _Guild +Socialism,_ p. 24. ] I am considerably in doubt whether Mr. Cole +thinks that Brown, Jones and Robinson should qualify in any election +where they assert that they are interested, or that somebody else, not +named, picks the functions in which they are entitled to be +interested. If I were asked to say what I believe Mr. Cole thinks, it +would be that he has smoothed over the difficulty by the enormously +strange assumption that it is the uninstructed voter who waives his +right to vote; and has concluded that whether functional voting is +arranged by a higher power, or "from below" on the principle that a +man may vote when it interests him to vote, only the instructed will +be voting anyway, and therefore the institution will work. + +But there are two kinds of uninstructed voter. There is the man who +does not know and knows that he does not know. He is generally an +enlightened person. He is the man who waives his right to vote. But +there is also the man who is uninstructed and does not know that he +is, or care. He can always be gotten to the polls, if the party +machinery is working. His vote is the basis of the machine. And since +the communes of the guild society have large powers over taxation, +wages, prices, credit, and natural resources, it would be preposterous +to assume that elections will not be fought at least as passionately +as our own. + +The way people exhibit their interest will not then delimit the +functions of a functional society. There are two other ways that +function might be defined. One would be by the trade unions which +fought the battle that brought guild socialism into being. Such a +struggle would harden groups of men together in some sort of +functional relation, and these groups would then become the vested +interests of the guild socialist society. Some of them, like the +miners and railroad men, would be very strong, and probably deeply +attached to the view of their function which they learned from the +battle with capitalism. It is not at all unlikely that certain +favorably placed trade unions would under a socialist state become the +center of coherence and government. But a guild society would +inevitably find them a tough problem to deal with, for direct action +would have revealed their strategic power, and some of their leaders +at least would not offer up this power readily on the altar of +freedom. In order to "coordinate" them, guild society would have to +gather together its strength, and fairly soon one would find, I think, +that the radicals under guild socialism would be asking for communes +strong enough to define the functions of the guilds. + +But if you are going to have the government (commune) define +functions, the premise of the theory disappears. It had to suppose +that a scheme of functions was obvious in order that the concave shops +would voluntarily relate themselves to society. If there is no settled +scheme of functions in every voter's head, he has no better way under +guild socialism than under orthodox democracy of turning a +self-centered opinion into a social judgment. And, of course, there +can be no such settled scheme, because, even if Mr. Cole and his +friends devised a good one, the shop democracies from which all power +derives, would judge the scheme in operation by what they learn of it +and by what they can imagine. The guilds would see the same scheme +differently. And so instead of the scheme being the skeleton that +keeps guild society together, the attempt to define what the scheme +ought to be, would be under guild socialism as elsewhere, the main +business of politics. If we could allow Mr. Cole his scheme of +functions we could allow him almost everything. Unfortunately he has +inserted in his premise what he wishes a guild society to +deduce. [Footnote: I have dealt with Mr. Cole's theory rather than with +the experience of Soviet Russia because, while the testimony is +fragmentary, all competent observers seem to agree that Russia in 1921 +does not illustrate a communist state in working order. Russia is in +revolution, and what you can learn from Russia is what a revolution is +like. You can learn very little about what a communist society would +be like. It is, however, immensely significant that, first as +practical revolutionists and then as public officials, the Russian +communists have relied not upon the spontaneous democracy of the +Russian people, but on the discipline, special interest and the +noblesse oblige of a specialized class-the loyal and indoctrinated +members of the Communist party. In the "transition," on which no time +limit has been set, I believe, the cure for class government and the +coercive state is strictly homeopathic. + +There is also the question of why I selected Mr. Cole's books rather +than the much more closely reasoned "Constitution for the Socialist +Commonwealth of Great Britain" by Sidney and Beatrice Webb. I admire +that book very much; but I have not been able to convince myself that +it is not an intellectual tour de force. Mr. Cole seems to me far more +authentically in the spirit of the socialist movement, and therefore, +a better witness.] + + + + +CHAPTER XX + +A NEW IMAGE + +1 + +THE lesson is, I think, a fairly clear one. In the absence of +institutions and education by which the environment is so successfully +reported that the realities of public life stand out sharply against +self-centered opinion, the common interests very largely elude public +opinion entirely, and can be managed only by a specialized class whose +personal interests reach beyond the locality. This class is +irresponsible, for it acts upon information that is not common +property, in situations that the public at large does not conceive, +and it can be held to account only on the accomplished fact. + +The democratic theory by failing to admit that self-centered opinions +are not sufficient to procure good government, is involved in +perpetual conflict between theory and practice. According to the +theory, the full dignity of man requires that his will should be, as +Mr. Cole says, expressed "in any and every form of social action." It +is supposed that the expression of their will is the consuming passion +of men, for they are assumed to possess by instinct the art of +government. But as a matter of plain experience, self-determination is +only one of the many interests of a human personality. The desire to +be the master of one's own destiny is a strong desire, but it has to +adjust itself to other equally strong desires, such as the desire for +a good life, for peace, for relief from burdens. In the original +assumptions of democracy it was held that the expression of each man's +will would spontaneously satisfy not only his desire for +self-expression, but his desire for a good life, because the instinct +to express one's self in a good life was innate. + +The emphasis, therefore, has always been on the mechanism for +expressing the will. The democratic El Dorado has always been some +perfect environment, and some perfect system of voting and +representation, where the innate good will and instinctive +statesmanship of every man could be translated into action. In limited +areas and for brief periods the environment has been so favorable, +that is to say so isolated, and so rich in opportunity, that the +theory worked well enough to confirm men in thinking that it was sound +for all time and everywhere. Then when the isolation ended, and +society became complex, and men had to adjust themselves closely to +one another, the democrat spent his time trying to devise more perfect +units of voting, in the hope that somehow he would, as Mr. Cole says, +"get the mechanism right, and adjust it as far as possible to men's +social wills." But while the democratic theorist was busy at this, he +was far away from the actual interests of human nature. He was +absorbed by one interest: self-government. Mankind was interested in +all kinds of other things, in order, in its rights, in prosperity, in +sights and sounds and in not being bored. In so far as spontaneous +democracy does not satisfy their other interests, it seems to most men +most of the time to be an empty thing. Because the art of successful +self-government is not instinctive, men do not long desire +self-government for its own sake. They desire it for the sake of the +results. That is why the impulse to self-government is always +strongest as a protest against bad conditions. + +The democratic fallacy has been its preoccupation with the origin of +government rather than with the processes and results. The democrat +has always assumed that if political power could be derived in the +right way, it would be beneficent. His whole attention has been on the +source of power, since he is hypnotized by the belief that the great +thing is to express the will of the people, first because expression +is the highest interest of man, and second because the will is +instinctively good. But no amount of regulation at the source of a +river will completely control its behavior, and while democrats have +been absorbed in trying to find a good mechanism for originating +social power, that is to say a good mechanism of voting and +representation, they neglected almost every other interest of men. For +no matter how power originates, the crucial interest is in how power +is exercised. What determines the quality of civilization is the use +made of power. And that use cannot be controlled at the source. + +If you try to control government wholly at the source, you inevitably +make all the vital decisions invisible. For since there is no instinct +which automatically makes political decisions that produce a good +life, the men who actually exercise power not only fail to express the +will of the people, because on most questions no will exists, but they +exercise power according to opinions which are hidden from the +electorate. + +If, then, you root out of the democratic philosophy the whole +assumption in all its ramifications that government is instinctive, +and that therefore it can be managed by self-centered opinions, what +becomes of the democratic faith in the dignity of man? It takes a +fresh lease of life by associating itself with the whole personality +instead of with a meager aspect of it. For the traditional democrat +risked the dignity of man on one very precarious assumption, that he +would exhibit that dignity instinctively in wise laws and good +government. Voters did not do that, and so the democrat was forever +being made to look a little silly by tough-minded men. But if, instead +of hanging human dignity on the one assumption about self-government, +you insist that man's dignity requires a standard of living, in which +his capacities are properly exercised, the whole problem changes. The +criteria which you then apply to government are whether it is +producing a certain minimum of health, of decent housing, of material +necessities, of education, of freedom, of pleasures, of beauty, not +simply whether at the sacrifice of all these things, it vibrates to +the self-centered opinions that happen to be floating around in men's +minds. In the degree to which these criteria can be made exact and +objective, political decision, which is inevitably the concern of +comparatively few people, is actually brought into relation with the +interests of men. + +There is no prospect, in any time which we can conceive, that the +whole invisible environment will be so clear to all men that they will +spontaneously arrive at sound public opinions on the whole business of +government. And even if there were a prospect, it is extremely +doubtful whether many of us would wish to be bothered, or would take +the time to form an opinion on "any and every form of social action" +which affects us. The only prospect which is not visionary is that +each of us in his own sphere will act more and more on a realistic +picture of the invisible world, and that we shall develop more and +more men who are expert in keeping these pictures realistic. Outside +the rather narrow range of our own possible attention, social control +depends upon devising standards of living and methods of audit by +which the acts of public officials and industrial directors are +measured. We cannot ourselves inspire or guide all these acts, as the +mystical democrat has always imagined. But we can steadily increase +our real control over these acts by insisting that all of them shall +be plainly recorded, and their results objectively measured. I should +say, perhaps, that we can progressively hope to insist. For the +working out of such standards and of such audits has only begun. + + + + +PART VII + +NEWSPAPERS + +CHAPTER XXI. THE BUYING PUBLIC + " XXII. THE CONSTANT READER + " XXIII. THE NATURE OF NEWS + " XXIV. NEWS, TRUTH, AND A CONCLUSION + + + + +CHAPTER XXI + +THE BUYING PUBLIC + +1 + +THE idea that men have to go forth and study the world in order to +govern it, has played a very minor part in political thought. It could +figure very little, because the machinery for reporting the world in +any way useful to government made comparatively little progress from +the time of Aristotle to the age in which the premises of democracy +were established. + +Therefore, if you had asked a pioneer democrat where the information +was to come from on which the will of the people was to be based, he +would have been puzzled by the question. It would have seemed a little +as if you had asked him where his life or his soul came from. The will +of the people, he almost always assumed, exists at all times; the duty +of political science was to work out the inventions of the ballot and +representative government. If they were properly worked out and +applied under the right conditions, such as exist in the +self-contained village or the self-contained shop, the mechanism would +somehow overcome the brevity of attention which Aristotle had +observed, and the narrowness of its range, which the theory of a +self-contained community tacitly acknowledged. We have seen how even +at this late date the guild socialists are transfixed by the notion +that if only you can build on the right unit of voting and +representation, an intricate cooperative commonwealth is possible. + +Convinced that the wisdom was there if only you could find it, +democrats have treated the problem of making public opinions as a +problem in civil liberties. [Footnote: The best study is Prof. +Zechariah Chafee's, _Freedom of Speech_.] "Who ever knew Truth +put to the worse, in a free and open encounter?" [Footnote: Milton, +_Areopagitica_, cited at the opening of Mr. Chafee's book. For +comment on this classic doctrine of liberty as stated by Milton, John +Stuart Mill, and Mr. Bertrand Russel, see my _Liberty and the +News_, Ch. II.] Supposing that no one has ever seen it put to the +worse, are we to believe then that the truth is generated by the +encounter, like fire by rubbing two sticks? Behind this classic +doctrine of liberty, which American democrats embodied in their Bill +of Rights, there are, in fact, several different theories of the +origin of truth. One is a faith that in the competition of opinions, +the truest will win because there is a peculiar strength in the truth. +This is probably sound if you allow the competition to extend over a +sufficiently long time. When men argue in this vein they have in mind +the verdict of history, and they think specifically of heretics +persecuted when they lived, canonized after they were dead. Milton's +question rests also on a belief that the capacity to recognize truth +is inherent in all men, and that truth freely put in circulation will +win acceptance. It derives no less from the experience, which has +shown that men are not likely to discover truth if they cannot speak +it, except under the eye of an uncomprehending policeman. + +No one can possibly overestimate the practical value of these civil +liberties, nor the importance of maintaining them. When they are in +jeopardy, the human spirit is in jeopardy, and should there come a +time when they have to be curtailed, as during a war, the suppression +of thought is a risk to civilization which might prevent its recovery +from the effects of war, if the hysterics, who exploit the necessity, +were numerous enough to carry over into peace the taboos of war. +Fortunately, the mass of men is too tolerant long to enjoy the +professional inquisitors, as gradually, under the criticism of men not +willing to be terrorized, they are revealed as mean-spirited creatures +who nine-tenths of the time do not know what they are talking +about. [Footnote: _Cf._ for example, the publications of the Lusk +Committee in New York, and the public statements and prophecies of Mr. +Mitchell Palmer, who was Attorney-General of the United States during +the period of President Wilson's illness.] + +But in spite of its fundamental importance, civil liberty in this +sense does not guarantee public opinion in the modern world. For it +always assumes, either that truth is spontaneous, or that the means of +securing truth exist when there is no external interference. But when +you are dealing with an invisible environment, the assumption is +false. The truth about distant or complex matters is not self-evident, +and the machinery for assembling information is technical and +expensive. Yet political science, and especially democratic political +science, has never freed itself from the original assumption of +Aristotle's politics sufficiently to restate the premises, so that +political thought might come to grips with the problem of how to make +the invisible world visible to the citizens of a modern state. + +So deep is the tradition, that until quite recently, for example, +political science was taught in our colleges as if newspapers did not +exist. I am not referring to schools of journalism, for they are trade +schools, intended to prepare men and women for a career. I am +referring to political science as expounded to future business men, +lawyers, public officials, and citizens at large. In that science a +study of the press and the sources of popular information found no +place. It is a curious fact. To anyone not immersed in the routine +interests of political science, it is almost inexplicable that no +American student of government, no American sociologist, has ever +written a book on news-gathering. There are occasional references to +the press, and statements that it is not, or that it ought to be, +"free" and "truthful." But I can find almost nothing else. And this +disdain of the professionals finds its counterpart in public opinions. +Universally it is admitted that the press is the chief means of +contact with the unseen environment. And practically everywhere it is +assumed that the press should do spontaneously for us what primitive +democracy imagined each of us could do spontaneously for himself, that +every day and twice a day it will present us with a true picture of +all the outer world in which we are interested. + +2 + +This insistent and ancient belief that truth is not earned, but +inspired, revealed, supplied gratis, comes out very plainly in our +economic prejudices as readers of newspapers. We expect the newspaper +to serve us with truth however unprofitable the truth may be. For this +difficult and often dangerous service, which we recognize as +fundamental, we expected to pay until recently the smallest coin +turned out by the mint. We have accustomed ourselves now to paying two +and even three cents on weekdays, and on Sundays, for an illustrated +encyclopedia and vaudeville entertainment attached, we have screwed +ourselves up to paying a nickel or even a dime. Nobody thinks for a +moment that he ought to pay for his newspaper. He expects the +fountains of truth to bubble, but he enters into no contract, legal or +moral, involving any risk, cost or trouble to himself. He will pay a +nominal price when it suits him, will stop paying whenever it suits +him, will turn to another paper when that suits him. Somebody has said +quite aptly that the newspaper editor has to be re-elected every day. + +This casual and one-sided relationship between readers and press is an +anomaly of our civilization. There is nothing else quite like it, and +it is, therefore, hard to compare the press with any other business or +institution. It is not a business pure and simple, partly because the +product is regularly sold below cost, but chiefly because the +community applies one ethical measure to the press and another to +trade or manufacture. Ethically a newspaper is judged as if it were a +church or a school. But if you try to compare it with these you fail; +the taxpayer pays for the public school, the private school is endowed +or supported by tuition fees, there are subsidies and collections for +the church. You cannot compare journalism with law, medicine or +engineering, for in every one of these professions the consumer pays +for the service. A free press, if you judge by the attitude of the +readers, means newspapers that are virtually given away. + +Yet the critics of the press are merely voicing the moral standards of +the community, when they expect such an institution to live on the +same plane as that on which the school, the church, and the +disinterested professions are supposed to live. This illustrates again +the concave character of democracy. No need for artificially acquired +information is felt to exist. The information must come naturally, +that is to say gratis, if not out of the heart of the citizen, then +gratis out of the newspaper. The citizen will pay for his telephone, +his railroad rides, his motor car, his entertainment. But he does not +pay openly for his news. + +He will, however, pay handsomely for the privilege of having someone +read about him. He will pay directly to advertise. And he will pay +indirectly for the advertisements of other people, because that +payment, being concealed in the price of commodities is part of an +invisible environment that he does not effectively comprehend. It +would be regarded as an outrage to have to pay openly the price of a +good ice cream soda for all the news of the world, though the public +will pay that and more when it buys the advertised commodities. The +public pays for the press, but only when the payment is concealed. + +3 + +Circulation is, therefore, the means to an end. It becomes an asset +only when it can be sold to the advertiser, who buys it with revenues +secured through indirect taxation of the reader. [Footnote: "An +established newspaper is entitled to fix its advertising rates so that +its net receipts from circulation may be left on the credit side of +the profit and loss account. To arrive at net receipts, I would deduct +from the gross the cost of promotion, distribution, and other expenses +incidental to circulation." From an address by Mr. Adolph S. Ochs, +publisher of _the New York Times,_ at the Philadelphia Convention +of the Associated Advertising Clubs of The World, June 26, 1916. +Cited, Elmer Davis, _History of The New York Times,_ 1851-1921, +pp. 397-398.] The kind of circulation which the advertiser will buy +depends on what he has to sell. It may be "quality" or "mass." On the +whole there is no sharp dividing line, for in respect to most +commodities sold by advertising, the customers are neither the small +class of the very rich nor the very poor. They are the people with +enough surplus over bare necessities to exercise discretion in their +buying. The paper, therefore, which goes into the homes of the fairly +prosperous is by and large the one which offers most to the +advertiser. It may also go into the homes of the poor, but except for +certain lines of goods, an analytical advertising agent does not rate +that circulation as a great asset, unless, as seems to be the case +with certain of Mr. Hearst's properties, the circulation is enormous. + +A newspaper which angers those whom it pays best to reach through +advertisements is a bad medium for an advertiser. And since no one +ever claimed that advertising was philanthropy, advertisers buy space +in those publications which are fairly certain to reach their future +customers. One need not spend much time worrying about the unreported +scandals of the dry-goods merchants. They represent nothing really +significant, and incidents of this sort are less common than many +critics of the press suppose. The real problem is that the readers of +a newspaper, unaccustomed to paying the cost of newsgathering, can be +capitalized only by turning them into circulation that can be sold to +manufacturers and merchants. And those whom it is most important to +capitalize are those who have the most money to spend. Such a press is +bound to respect the point of view of the buying public. It is for +this buying public that newspapers are edited and published, for +without that support the newspaper cannot live. A newspaper can flout +an advertiser, it can attack a powerful banking or traction interest, +but if it alienates the buying public, it loses the one indispensable +asset of its existence. + +Mr. John L. Given, [Footnote: _Making a Newspaper_, p. 13. This +is the best technical book I know, and should be read by everyone who +undertakes to discuss the press. Mr. G. B. Diblee, who wrote the +volume on _The Newspaper_ in the Home University Library says (p. +253), that "on the press for pressmen I only know of one good book, +Mr. Given's."] formerly of the New York Evening Sun, stated in 1914 +that out of over two thousand three hundred dailies published in the +United States, there were about one hundred and seventy-five printed +in cities having over one hundred thousand inhabitants. These +constitute the press for "general news." They are the key papers which +collect the news dealing with great events, and even the people who do +not read any one of the one hundred and seventy-five depend ultimately +upon them for news of the outer world. For they make up the great +press associations which coöperate in the exchange of news. Each is, +therefore, not only the informant of its own readers, but it is the +local reporter for the newspapers of other cities. The rural press and +the special press by and large, take their general news from these key +papers. And among these there are some very much richer than others, +so that for international news, in the main, the whole press of the +nation may depend upon the reports of the press associations and the +special services of a few metropolitan dailies. + +Roughly speaking, the economic support for general news gathering is +in the price paid for advertised goods by the fairly prosperous +sections of cities with more than one hundred thousand inhabitants. +These buying publics are composed of the members of families, who +depend for their income chiefly on trade, merchandising, the direction +of manufacture, and finance. They are the clientele among whom it pays +best to advertise in a newspaper. They wield a concentrated purchasing +power, which may be less in volume than the aggregate for farmers and +workingmen; but within the radius covered by a daily newspaper they +are the quickest assets. + +4 + +They have, moreover, a double claim to attention. They are not only +the best customers for the advertiser, they include the advertisers. +Therefore the impression made by the newspapers on this public matters +deeply. Fortunately this public is not unanimous. It may be +"capitalistic" but it contains divergent views on what capitalism is, +and how it is to be run. Except in times of danger, this respectable +opinion is sufficiently divided to permit of considerable differences +of policy. These would be greater still if it were not that publishers +are themselves usually members of these urban communities, and +honestly see the world through the lenses of their associates and +friends. + +They are engaged in a speculative business, [Footnote: Sometimes so +speculative that in order to secure credit the publisher has to go +into bondage to his creditors. Information on this point is very +difficult to obtain, and for that reason its general importance is +often much exaggerated.] which depends on the general condition of +trade, and more peculiarly on a circulation based not on a marriage +contract with their readers, but on free love. The object of every +publisher is, therefore, to turn his circulation from a medley of +catch-as-catch-can news stand buyers into a devoted band of constant +readers. A newspaper that can really depend upon the loyalty of its +readers is as independent as a newspaper can be, given the economics +of modern journalism. [Footnote: "It is an axiom in newspaper +publishing--'more readers, more independence of the influence of +advertisers; fewer readers and more dependence on the advertiser' It +may seem like a contradiction (yet it is the truth) to assert: the +greater the number of advertisers, the less influence they are +individually able to exercise with the publisher." Adolph S. Ochs, +_of. supra._] A body of readers who stay by it through thick and +thin is a power greater than any which the individual advertiser can +wield, and a power great enough to break up a combination of +advertisers. Therefore, whenever you find a newspaper betraying its +readers for the sake of an advertiser, you can be fairly certain +either that the publisher sincerely shares the views of the +advertiser, or that he thinks, perhaps mistakenly, he cannot count +upon the support of his readers if he openly resists dictation. It is +a question of whether the readers, who do not pay in cash for their +news, will pay for it in loyalty. + + + + +CHAPTER XXII + +THE CONSTANT READER + +I + +THE loyalty of the buying public to a newspaper is not stipulated in +any bond. In almost every other enterprise the person who expects to +be served enters into an agreement that controls his passing whims. At +least he pays for what he obtains. In the publishing of periodicals +the nearest approach to an agreement for a definite time is the paid +subscription, and that is not, I believe, a great factor in the +economy of a metropolitan daily. The reader is the sole and the daily +judge of his loyalty, and there can be no suit against him for breach +of promise or nonsupport. + +Though everything turns on the constancy of the reader, there does not +exist even a vague tradition to call that fact to the reader's mind. +His constancy depends on how he happens to feel, or on his habits. And +these depend not simply on the quality of the news, but more often on +a number of obscure elements that in our casual relation to the press, +we hardly take the trouble to make conscious. The most important of +these is that each of us tends to judge a newspaper, if we judge it at +all, by its treatment of that part of the news in which we feel +ourselves involved. The newspaper deals with a multitude of events +beyond our experience. But it deals also with some events within our +experience. And by its handling of those events we most frequently +decide to like it or dislike it, to trust it or refuse to have the +sheet in the house. If the newspaper gives a satisfactory account of +that which we think we know, our business, our church, our party, it +is fairly certain to be immune from violent criticism by us. What +better criterion does the man at the breakfast table possess than that +the newspaper version checks up with his own opinion? Therefore, most +men tend to hold the newspaper most strictly accountable in their +capacity, not of general readers, but of special pleaders on matters +of their own experience. + +Rarely is anyone but the interested party able to test the accuracy of +a report. If the news is local, and if there is competition, the +editor knows that he will probably hear from the man who thinks his +portrait unfair and inaccurate. But if the news is not local, the +corrective diminishes as the subject matter recedes into the distance. +The only people who can correct what they think is a false picture of +themselves printed in another city are members of groups well enough +organized to hire publicity men. + +Now it is interesting to note that the general reader of a newspaper +has no standing in law if he thinks he is being misled by the news. It +is only the aggrieved party who can sue for slander or libel, and he +has to prove a material injury to himself. The law embodies the +tradition that general news is not a matter of common concern, +[Footnote: The reader will not mistake this as a plea for censorship. +It might, however, be a good thing if there were competent tribunals, +preferably not official ones, where charges of untruthfulness and +unfairness in the general news could be sifted. _Cf. Liberty and the +News,_ pp. 73-76. ] except as to matter which is vaguely described +as immoral or seditious. + +But the body of the news, though unchecked as a whole by the +disinterested reader, consists of items about which some readers have +very definite preconceptions. Those items are the data of his +judgment, and news which men read without this personal criterion, +they judge by some other standard than their standard of accuracy. +They are dealing here with a subject matter which to them is +indistinguishable from fiction. The canon of truth cannot be applied. +They do not boggle over such news if it conforms to their stereotypes, +and they continue to read it if it interests them. [Footnote: Note, for +example, how absent is indignation in Mr. Upton Sinclair against +socialist papers, even those which are as malignantly unfair to +employers as certain of the papers cited by him are unfair to +radicals.] + +2 + +There are newspapers, even in large cities, edited on the principle +that the readers wish to read about themselves. The theory is that if +enough people see their own names in the paper often enough, can read +about their weddings, funerals, sociables, foreign travels, lodge +meetings, school prizes, their fiftieth birthdays, their sixtieth +birthdays, their silver weddings, their outings and clambakes, they +will make a reliable circulation. + +The classic formula for such a newspaper is contained in a letter +written by Horace Greeley on April 3, 1860, to "Friend Fletcher" who +was about to start a country newspaper: [Footnote: Cited, James Melvin +Lee, _The History of American Journalism,_ p. 405.] + +"I. Begin with a clear conception that the subject of deepest interest +to an average human being is himself; next to that he is most +concerned about his neighbors. Asia and the Tongo Islands stand a long +way after these in his regard.... Do not let a new church be +organized, or new members be added to one already existing, a farm be +sold, a new house raised, a mill set in motion, a store opened, nor +anything of interest to a dozen families occur, without having the +fact duly, though briefly, chronicled in your columns. If a farmer +cuts a big tree, or grows a mammoth beet, or harvests a bounteous +yield of wheat or corn, set forth the fact as concisely and +unexceptionally as possible." + +The function of becoming, as Mr. Lee puts it, "the printed diary of +the home town" is one that every newspaper no matter where it is +published must in some measure fill. And where, as in a great city +like New York, the general newspapers circulated broadcast cannot fill +it, there exist small newspapers published on Greeley's pattern for +sections of the city. In the boroughs of Manhattan and the Bronx there +are perhaps twice as many local dailies as there are general +newspapers. [Footnote: _Cf._ John L. Given, _Making a Newspaper,_ +p. 13.] And they are supplemented by all kinds of special publications for +trades, religions, nationalities. + +These diaries are published for people who find their own lives +interesting. But there are also great numbers of people who find their +own lives dull, and wish, like Hedda Gabler, to live a more thrilling +life. For them there are published a few whole newspapers, and +sections of others, devoted to the personal lives of a set of +imaginary people, with whose gorgeous vices the reader can in his +fancy safely identify himself. Mr. Hearst's unflagging interest in +high society caters to people who never hope to be in high society, +and yet manage to derive some enhancement out of the vague feeling +that they are part of the life that they read about. In the great +cities "the printed diary of the home town" tends to be the printed +diary of a smart set. + +And it is, as we have already noted, the dailies of the cities which +carry the burden of bringing distant news to the private citizen. But +it is not primarily their political and social news which holds the +circulation. The interest in that is intermittent, and few publishers +can bank on it alone. The newspaper, therefore, takes to itself a +variety of other features, all primarily designed to hold a body of +readers together, who so far as big news is concerned, are not able to +be critical. Moreover, in big news the competition in any one +community is not very serious. The press services standardize the main +events; it is only once in a while that a great scoop is made; there +is apparently not a very great reading public for such massive +reporting as has made the New York Times of recent years indispensable +to men of all shades of opinion. In order to differentiate themselves +and collect a steady public most papers have to go outside the field +of general news. They go to the dazzling levels of society, to scandal +and crime, to sports, pictures, actresses, advice to the lovelorn, +highschool notes, women's pages, buyer's pages, cooking receipts, +chess, whist, gardening, comic strips, thundering partisanship, not +because publishers and editors are interested in everything but news, +but because they have to find some way of holding on to that alleged +host of passionately interested readers, who are supposed by some +critics of the press to be clamoring for the truth and nothing but the +truth. + +The newspaper editor occupies a strange position. His enterprises +depend upon indirect taxation levied by his advertisers upon his +readers; the patronage of the advertisers depends upon the editor's +skill in holding together an effective group of customers. These +customers deliver judgment according to their private experiences and +their stereotyped expectations, for in the nature of things they have +no independent knowledge of most news they read. If the judgment is +not unfavorable, the editor is at least within range of a circulation +that pays. But in order to secure that circulation, he cannot rely +wholly upon news of the greater environment. He handles that as +interestingly as he can, of course, but the quality of the general +news, especially about public affairs, is not in itself sufficient to +cause very large numbers of readers to discriminate among the dailies. + +This somewhat left-handed relationship between newspapers and public +information is reflected in the salaries of newspaper men. Reporting, +which theoretically constitutes the foundation of the whole +institution, is the most poorly paid branch of newspaper work, and is +the least regarded. By and large, able men go into it only by +necessity or for experience, and with the definite intention of being +graduated as soon as possible. For straight reporting is not a career +that offers many great rewards. The rewards in journalism go to +specialty work, to signed correspondence which has editorial quality, +to executives, and to men with a knack and flavor of their own. This +is due, no doubt, to what economists call the rent of ability. But +this economic principle operates with such peculiar violence in +journalism that newsgathering does not attract to itself anything like +the number of trained and able men which its public importance would +seem to demand. The fact that the able men take up "straight +reporting" with the intention of leaving it as soon as possible is, I +think, the chief reason why it has never developed in sufficient +measure those corporate traditions that give to a profession prestige +and a jealous self-respect. For it is these corporate traditions which +engender the pride of craft, which tend to raise the standards of +admission, punish breaches of the code, and give men the strength to +insist upon their status in society. + +3 + +Yet all this does not go to the root of the matter. For while the +economics of journalism is such as to depress the value of news +reporting, it is, I am certain, a false determinism which would +abandon the analysis at that point. The intrinsic power of the +reporter appears to be so great, the number of very able men who pass +through reporting is so large, that there must be some deeper reason +why, comparatively speaking, so little serious effort has gone into +raising the vocation to the level say of medicine, engineering, or +law. + +Mr. Upton Sinclair speaks for a large body of opinion in +America, [Footnote: Mr. Hilaire Belloc makes practically the same +analysis for English newspapers. _Cf. The Free Press._] when he +claims that in what he calls "The Brass Check" he has found this +deeper reason: + +"The Brass Check is found in your pay envelope every week--you who +write and print and distribute our newspapers and magazines. The Brass +check is the price of your shame--you who take the fair body of truth +and sell it in the market place, who betray the virgin hopes of +mankind into the loathsome brothel of Big Business." [Footnote: Upton +Sinclair, _The Brass Check. A Study of American Journalism._ p. +116.] + +It would seem from this that there exists a body of known truth, and a +set of well founded hopes, which are prostituted by a more or less +conscious conspiracy of the rich owners of newspapers. If this theory +is correct, then a certain conclusion follows. It is that the fair +body of truth would be inviolate in a press not in any way connected +with Big Business. For if it should happen that a press not controlled +by, and not even friendly with, Big Business somehow failed to contain +the fair body of truth, something would be wrong with Mr. Sinclair's +theory. + +There is such a press. Strange to say, in proposing a remedy Mr. +Sinclair does not advise his readers to subscribe to the nearest +radical newspaper. Why not? If the troubles of American journalism go +back to the Brass Check of Big Business why does not the remedy lie in +reading the papers that do not in any remote way accept the Brass +Check? Why subsidize a "National News" with a large board of directors +"of all creeds or causes" to print a paper full of facts "regardless +of what is injured, the Steel Trust or the I. W. W., the Standard Oil +Company or the Socialist Party?" If the trouble is Big Business, that +is, the Steel Trust, Standard Oil and the like, why not urge everybody +to read I. W. W. or Socialist papers? Mr. Sinclair does not say why +not. But the reason is simple. He cannot convince anybody, not even +himself, that the anti-capitalist press is the remedy for the +capitalist press. He ignores the anti-capitalist press both in his +theory of the Brass Check and in his constructive proposal. But if you +are diagnosing American journalism you cannot ignore it. If what you +care about is "the fair body of truth," you do not commit the gross +logical error of assembling all the instances of unfairness and lying +you can find in one set of newspapers, ignore all the instances you +could easily find in another set, and then assign as the cause of the +lying, the one supposedly common characteristic of the press to which +you have confined your investigation. If you are going to blame +"capitalism" for the faults of the press, you are compelled to prove +that those faults do not exist except where capitalism controls. That +Mr. Sinclair cannot do this, is shown by the fact that while in his +diagnosis he traces everything to capitalism, in his prescription he +ignores both capitalism and anti-capitalism. + +One would have supposed that the inability to take any non-capitalist +paper as a model of truthfulness and competence would have caused Mr. +Sinclair, and those who agree with him, to look somewhat more +critically at their assumptions. They would have asked themselves, for +example, where is the fair body of truth, that Big Business +prostitutes, but anti-Big Business does not seem to obtain? For that +question leads, I believe, to the heart of the matter, to the question +of what is news. + + + + +CHAPTER XXIII + +THE NATURE OF NEWS + +1 + +ALL the reporters in the world working all the hours of the day could +not witness all the happenings in the world. There are not a great +many reporters. And none of them has the power to be in more than one +place at a time. Reporters are not clairvoyant, they do not gaze into +a crystal ball and see the world at will, they are not assisted by +thought-transference. Yet the range of subjects these comparatively +few men manage to cover would be a miracle indeed, if it were not a +standardized routine. + +Newspapers do not try to keep an eye on all mankind. [Footnote: See the +illuminating chapter in Mr. John L. Given's book, already cited, on +"Uncovering the News," Ch. V.] They have watchers stationed at certain +places, like Police Headquarters, the Coroner's Office, the County +Clerk's Office, City Hall, the White House, the Senate, House of +Representatives, and so forth. They watch, or rather in the majority +of cases they belong to associations which employ men who watch "a +comparatively small number of places where it is made known when the +life of anyone... departs from ordinary paths, or when events worth +telling about occur. For example, John Smith, let it be supposed, +becomes a broker. For ten years he pursues the even tenor of his way +and except for his customers and his friends no one gives him a +thought. To the newspapers he is as if he were not. But in the +eleventh year he suffers heavy losses and, at last, his resources all +gone, summons his lawyer and arranges for the making of an assignment. +The lawyer posts off to the County Clerk's office, and a clerk there +makes the necessary entries in the official docket. Here in step the +newspapers. While the clerk is writing Smith's business obituary a +reporter glances over his shoulder and a few minutes later the +reporters know Smith's troubles and are as well informed concerning +his business status as they would be had they kept a reporter at his +door every day for over ten years. [Footnote: _Op. cit._, p. 57.] + +When Mr. Given says that the newspapers know "Smith's troubles" and +"his business status," he does not mean that they know them as Smith +knows them, or as Mr. Arnold Bennett would know them if he had made +Smith the hero of a three volume novel. The newspapers know only "in a +few minutes" the bald facts which are recorded in the County Clerk's +Office. That overt act "uncovers" the news about Smith. Whether the +news will be followed up or not is another matter. The point is that +before a series of events become news they have usually to make +themselves noticeable in some more or less overt act. Generally too, +in a crudely overt act. Smith's friends may have known for years that +he was taking risks, rumors may even have reached the financial editor +if Smith's friends were talkative. But apart from the fact that none +of this could be published because it would be libel, there is in +these rumors nothing definite on which to peg a story. Something +definite must occur that has unmistakable form. It may be the act of +going into bankruptcy, it may be a fire, a collision, an assault, a +riot, an arrest, a denunciation, the introduction of a bill, a speech, +a vote, a meeting, the expressed opinion of a well known citizen, an +editorial in a newspaper, a sale, a wage-schedule, a price change, the +proposal to build a bridge.... There must be a manifestation. The +course of events must assume a certain definable shape, and until it +is in a phase where some aspect is an accomplished fact, news does not +separate itself from the ocean of possible truth. + +2 + +Naturally there is room for wide difference of opinion as to when +events have a shape that can be reported. A good journalist will find +news oftener than a hack. If he sees a building with a dangerous list, +he does not have to wait until it falls into the street in order to +recognize news. It was a great reporter who guessed the name of the +next Indian Viceroy when he heard that Lord So-and-So was inquiring +about climates. There are lucky shots but the number of men who can +make them is small. Usually it is the stereotyped shape assumed by an +event at an obvious place that uncovers the run of the news. The most +obvious place is where people's affairs touch public authority. De +minimis non curat lex. It is at these places that marriages, births, +deaths, contracts, failures, arrivals, departures, lawsuits, +disorders, epidemics and calamities are made known. + +In the first instance, therefore, the news is not a mirror of social +conditions, but the report of an aspect that has obtruded itself. The +news does not tell you how the seed is germinating in the ground, but +it may tell you when the first sprout breaks through the surface. It +may even tell you what somebody says is happening to the seed under +ground. It may tell you that the sprout did not come up at the time it +was expected. The more points, then, at which any happening can be +fixed, objectified, measured, named, the more points there are at +which news can occur. + +So, if some day a legislature, having exhausted all other ways of +improving mankind, should forbid the scoring of baseball games, it +might still be possible to play some sort of game in which the umpire +decided according to his own sense of fair play how long the game +should last, when each team should go to bat, and who should be +regarded as the winner. If that game were reported in the newspapers +it would consist of a record of the umpire's decisions, plus the +reporter's impression of the hoots and cheers of the crowd, plus at +best a vague account of how certain men, who had no specified position +on the field moved around for a few hours on an unmarked piece of sod. +The more you try to imagine the logic of so absurd a predicament, the +more clear it becomes that for the purposes of newsgathering, (let +alone the purposes of playing the game) it is impossible to do much +without an apparatus and rules for naming, scoring, recording. Because +that machinery is far from perfect, the umpire's life is often a +distracted one. Many crucial plays he has to judge by eye. The last +vestige of dispute could be taken out of the game, as it has been +taken out of chess when people obey the rules, if somebody thought it +worth his while to photograph every play. It was the moving pictures +which finally settled a real doubt in many reporters' minds, owing to +the slowness of the human eye, as to just what blow of Dempsey's +knocked out Carpentier. + +Wherever there is a good machinery of record, the modern news service +works with great precision. There is one on the stock exchange, and +the news of price movements is flashed over tickers with dependable +accuracy. There is a machinery for election returns, and when the +counting and tabulating are well done, the result of a national +election is usually known on the night of the election. In civilized +communities deaths, births, marriages and divorces are recorded, and +are known accurately except where there is concealment or neglect. The +machinery exists for some, and only some, aspects of industry and +government, in varying degrees of precision for securities, money and +staples, bank clearances, realty transactions, wage scales. It exists +for imports and exports because they pass through a custom house and +can be directly recorded. It exists in nothing like the same degree +for internal trade, and especially for trade over the counter. + +It will be found, I think, that there is a very direct relation +between the certainty of news and the system of record. If you call to +mind the topics which form the principal indictment by reformers +against the press, you find they are subjects in which the newspaper +occupies the position of the umpire in the unscored baseball game. All +news about states of mind is of this character: so are all +descriptions of personalities, of sincerity, aspiration, motive, +intention, of mass feeling, of national feeling, of public opinion, +the policies of foreign governments. So is much news about what is +going to happen. So are questions turning on private profit, private +income, wages, working conditions, the efficiency of labor, +educational opportunity, unemployment, [Footnote: Think of what guess +work went into the Reports of Unemployment in 1921.] monotony, health, +discrimination, unfairness, restraint of trade, waste, "backward +peoples," conservatism, imperialism, radicalism, liberty, honor, +righteousness. All involve data that are at best spasmodically +recorded. The data may be hidden because of a censorship or a +tradition of privacy, they may not exist because nobody thinks record +important, because he thinks it red tape, or because nobody has yet +invented an objective system of measurement. Then the news on these +subjects is bound to be debatable, when it is not wholly neglected. +The events which are not scored are reported either as personal and +conventional opinions, or they are not news. They do not take shape +until somebody protests, or somebody investigates, or somebody +publicly, in the etymological meaning of the word, makes an +_issue_ of them. + +This is the underlying reason for the existence of the press agent. +The enormous discretion as to what facts and what impressions shall be +reported is steadily convincing every organized group of people that +whether it wishes to secure publicity or to avoid it, the exercise of +discretion cannot be left to the reporter. It is safer to hire a press +agent who stands between the group and the newspapers. Having hired +him, the temptation to exploit his strategic position is very great. +"Shortly before the war," says Mr. Frank Cobb, "the newspapers of New +York took a census of the press agents who were regularly employed and +regularly accredited and found that there were about twelve hundred of +them. How many there are now (1919) I do not pretend to know, but what +I do know is that many of the direct channels to news have been closed +and the information for the public is first filtered through publicity +agents. The great corporations have them, the banks have them, the +railroads have them, all the organizations of business and of social +and political activity have them, and they are the media through which +news comes. Even statesmen have them." [Footnote: Address before the +Women's City Club of New York, Dec. 11, 1919. Reprinted, _New +Republic_, Dec. 31, 1919, p. 44.] + +Were reporting the simple recovery of obvious facts, the press agent +would be little more than a clerk. But since, in respect to most of +the big topics of news, the facts are not simple, and not at all +obvious, but subject to choice and opinion, it is natural that +everyone should wish to make his own choice of facts for the +newspapers to print. The publicity man does that. And in doing it, he +certainly saves the reporter much trouble, by presenting him a clear +picture of a situation out of which he might otherwise make neither +head nor tail. But it follows that the picture which the publicity man +makes for the reporter is the one he wishes the public to see. He is +censor and propagandist, responsible only to his employers, and to the +whole truth responsible only as it accords with the employers' +conception of his own interests. + +The development of the publicity man is a clear sign that the facts of +modern life do not spontaneously take a shape in which they can be +known. They must be given a shape by somebody, and since in the daily +routine reporters cannot give a shape to facts, and since there is +little disinterested organization of intelligence, the need for some +formulation is being met by the interested parties. + +3 + +The good press agent understands that the virtues of his cause are not +news, unless they are such strange virtues that they jut right out of +the routine of life. This is not because the newspapers do not like +virtue, but because it is not worth while to say that nothing has +happened when nobody expected anything to happen. So if the publicity +man wishes free publicity he has, speaking quite accurately, to start +something. He arranges a stunt: obstructs the traffic, teases the +police, somehow manages to entangle his client or his cause with an +event that is already news. The suffragists knew this, did not +particularly enjoy the knowledge but acted on it, and kept suffrage in +the news long after the arguments pro and con were straw in their +mouths, and people were about to settle down to thinking of the +suffrage movement as one of the established institutions of American +life. [Footnote: _Cf._ Inez Haynes Irwin, _The Story of the +Woman's Party._ It is not only a good account of a vital part of a +great agitation, but a reservoir of material on successful, +non-revolutionary, non-conspiring agitation under modern conditions of +public attention, public interest, and political habit.] + +Fortunately the suffragists, as distinct from the feminists, had a +perfectly concrete objective, and a very simple one. What the vote +symbolizes is not simple, as the ablest advocates and the ablest +opponents knew. But the right to vote is a simple and familiar right. +Now in labor disputes, which are probably the chief item in the +charges against newspapers, the right to strike, like the right to +vote, is simple enough. But the causes and objects of a particular +strike are like the causes and objects of the woman's movement, +extremely subtle. + +Let us suppose the conditions leading up to a strike are bad. What is +the measure of evil? A certain conception of a proper standard of +living, hygiene, economic security, and human dignity. The industry +may be far below the theoretical standard of the community, and the +workers may be too wretched to protest. Conditions may be above the +standard, and the workers may protest violently. The standard is at +best a vague measure. However, we shall assume that the conditions are +below par, as par is understood by the editor. Occasionally without +waiting for the workers to threaten, but prompted say by a social +worker, he will send reporters to investigate, and will call attention +to bad conditions. Necessarily he cannot do that often. For these +investigations cost time, money, special talent, and a lot of space. +To make plausible a report that conditions are bad, you need a good +many columns of print. In order to tell the truth about the steel +worker in the Pittsburgh district, there was needed a staff of +investigators, a great deal of time, and several fat volumes of print. +It is impossible to suppose that any daily newspaper could normally +regard the making of Pittsburgh Surveys, or even Interchurch Steel +Reports, as one of its tasks. News which requires so much trouble as +that to obtain is beyond the resources of a daily press. [Footnote: Not +long ago Babe Ruth was jailed for speeding. Released from jail just +before the afternoon game started, he rushed into his waiting +automobile, and made up for time lost in jail by breaking the speed +laws on his way to the ball grounds. No policeman stopped him, but a +reporter timed him, and published his speed the next morning. Babe +Ruth is an exceptional man. Newspapers cannot time all motorists. They +have to take their news about speeding from the police.] + +The bad conditions as such are not news, because in all but +exceptional cases, journalism is not a first hand report of the raw +material. It is a report of that material after it has been stylized. +Thus bad conditions might become news if the Board of Health reported +an unusually high death rate in an industrial area. Failing an +intervention of this sort, the facts do not become news, until the +workers organize and make a demand upon their employers. Even then, if +an easy settlement is certain the news value is low, whether or not +the conditions themselves are remedied in the settlement. But if +industrial relations collapse into a strike or lockout the news value +increases. If the stoppage involves a service on which the readers of +the newspapers immediately depend, or if it involves a breach of +order, the news value is still greater. + +The underlying trouble appears in the news through certain easily +recognizable symptoms, a demand, a strike, disorder. From the point of +view of the worker, or of the disinterested seeker of justice, the +demand, the strike, and the disorder, are merely incidents in a +process that for them is richly complicated. But since all the +immediate realities lie outside the direct experience both of the +reporter, and of the special public by which most newspapers are +supported, they have normally to wait for a signal in the shape of an +overt act. When that signal comes, say through a walkout of the men or +a summons for the police, it calls into play the stereotypes people +have about strikes and disorders. The unseen struggle has none of its +own flavor. It is noted abstractly, and that abstraction is then +animated by the immediate experience of the reader and reporter. +Obviously this is a very different experience from that which the +strikers have. They feel, let us say, the temper of the foreman, the +nerve-racking monotony of the machine, the depressingly bad air, the +drudgery of their wives, the stunting of their children, the dinginess +of their tenements. The slogans of the strike are invested with these +feelings. But the reporter and reader see at first only a strike and +some catchwords. They invest these with their feelings. Their feelings +may be that their jobs are insecure because the strikers are stopping +goods they need in their work, that there will be shortage and higher +prices, that it is all devilishly inconvenient. These, too, are +realities. And when they give color to the abstract news that a strike +has been called, it is in the nature of things that the workers are at +a disadvantage. It is in the nature, that is to say, of the existing +system of industrial relations that news arising from grievances or +hopes by workers should almost invariably be uncovered by an overt +attack on production. + +You have, therefore, the circumstances in all their sprawling +complexity, the overt act which signalizes them, the stereotyped +bulletin which publishes the signal, and the meaning that the reader +himself injects, after he has derived that meaning from the experience +which directly affects him. Now the reader's experience of a strike +may be very important indeed, but from the point of view of the +central trouble which caused the strike, it is eccentric. Yet this +eccentric meaning is automatically the most interesting. [Footnote: +_Cf_. Ch. XI, "The Enlisting of Interest."] To enter imaginatively +into the central issues is for the reader to step out of himself, and into +very different lives. + +It follows that in the reporting of strikes, the easiest way is to let +the news be uncovered by the overt act, and to describe the event as +the story of interference with the reader's life. That is where his +attention is first aroused, and his interest most easily enlisted. A +great deal, I think myself the crucial part, of what looks to the +worker and the reformer as deliberate misrepresentation on the part of +newspapers, is the direct outcome of a practical difficulty in +uncovering the news, and the emotional difficulty of making distant +facts interesting unless, as Emerson says, we can "perceive (them) to +be only a new version of our familiar experience" and can "set about +translating (them) at once into our parallel facts." [Footnote: From +his essay entitled _Art and Criticism_. The quotation occurs in a +passage cited on page 87 of Professor R. W. Brown's, _The Writer's +Art._] + +If you study the way many a strike is reported in the press, you will +find, very often, that the issues are rarely in the headlines, barely +in the leading paragraphs, and sometimes not even mentioned anywhere. +A labor dispute in another city has to be very important before the +news account contains any definite information as to what is in +dispute. The routine of the news works that way, with modifications it +works that way in regard to political issues and international news as +well. The news is an account of the overt phases that are interesting, +and the pressure on the newspaper to adhere to this routine comes from +many sides. It comes from the economy of noting only the stereotyped +phase of a situation. It comes from the difficulty of finding +journalists who can see what they have not learned to see. It comes +from the almost unavoidable difficulty of finding sufficient space in +which even the best journalist can make plausible an unconventional +view. It comes from the economic necessity of interesting the reader +quickly, and the economic risk involved in not interesting him at all, +or of offending him by unexpected news insufficiently or clumsily +described. All these difficulties combined make for uncertainty in the +editor when there are dangerous issues at stake, and cause him +naturally to prefer the indisputable fact and a treatment more readily +adapted to the reader's interest. The indisputable fact and the easy +interest, are the strike itself and the reader's inconvenience. + +All the subtler and deeper truths are in the present organization of +industry very unreliable truths. They involve judgments about +standards of living, productivity, human rights that are endlessly +debatable in the absence of exact record and quantitative analysis. +And as long as these do not exist in industry, the run of news about +it will tend, as Emerson said, quoting from Isocrates, "to make of +moles mountains, and of mountains moles." [Footnote: _Id., +supra_] Where there is no constitutional procedure in industry, and +no expert sifting of evidence and the claims, the fact that is +sensational to the reader is the fact that almost every journalist +will seek. Given the industrial relations that so largely prevail, +even where there is conference or arbitration, but no independent +filtering of the facts for decision, the issue for the newspaper +public will tend not to be the issue for the industry. And so to try +disputes by an appeal through the newspapers puts a burden upon +newspapers and readers which they cannot and ought not to carry. As +long as real law and order do not exist, the bulk of the news will, +unless consciously and courageously corrected, work against those who +have no lawful and orderly method of asserting themselves. The +bulletins from the scene of action will note the trouble that arose +from the assertion, rather than the reasons which led to it. The +reasons are intangible. + +4 + +The editor deals with these bulletins. He sits in his office, reads +them, rarely does he see any large portion of the events themselves. +He must, as we have seen, woo at least a section of his readers every +day, because they will leave him without mercy if a rival paper +happens to hit their fancy. He works under enormous pressure, for the +competition of newspapers is often a matter of minutes. Every bulletin +requires a swift but complicated judgment. It must be understood, put +in relation to other bulletins also understood, and played up or +played down according to its probable interest for the public, as the +editor conceives it. Without standardization, without stereotypes, +without routine judgments, without a fairly ruthless disregard of +subtlety, the editor would soon die of excitement. The final page is +of a definite size, must be ready at a precise moment; there can be +only a certain number of captions on the items, and in each caption +there must be a definite number of letters. Always there is the +precarious urgency of the buying public, the law of libel, and the +possibility of endless trouble. The thing could not be managed at all +without systematization, for in a standardized product there is +economy of time and effort, as well as a partial guarantee against +failure. + +It is here that newspapers influence each other most deeply. Thus when +the war broke out, the American newspapers were confronted with a +subject about which they had no previous experience. Certain dailies, +rich enough to pay cable tolls, took the lead in securing news, and +the way that news was presented became a model for the whole press. +But where did that model come from? It came from the English press, +not because Northcliffe owned American newspapers, but because at +first it was easier to buy English correspondence, and because, later, +it was easier for American journalists to read English newspapers than +it was for them to read any others. London was the cable and news +center, and it was there that a certain technic for reporting the war +was evolved. Something similar occurred in the reporting of the +Russian Revolution. In that instance, access to Russia was closed by +military censorship, both Russian and Allied, and closed still more +effectively by the difficulties of the Russian language. But above all +it was closed to effective news reporting by the fact that the hardest +thing to report is chaos, even though it is an evolving chaos. This +put the formulating of Russian news at its source in Helsingfors, +Stockholm, Geneva, Paris and London, into the hands of censors and +propagandists. They were for a long time subject to no check of any +kind. Until they had made themselves ridiculous they created, let us +admit, out of some genuine aspects of the huge Russian maelstrom, a +set of stereotypes so evocative of hate and fear, that the very best +instinct of journalism, its desire to go and see and tell, was for a +long time crushed. [Footnote: _Cf. A Test of the News,_ by Walter +Lippmann and Charles Merz, assisted by Faye Lippmann, _New +Republic,_ August 4, 1920.] + +5 + +Every newspaper when it reaches the reader is the result of a whole +series of selections as to what items shall be printed, in what +position they shall be printed, how much space each shall occupy, what +emphasis each shall have. There are no objective standards here. There +are conventions. Take two newspapers published in the same city on the +same morning. The headline of one reads: "Britain pledges aid to +Berlin against French aggression; France openly backs Poles." The +headline of the second is "Mrs. Stillman's Other Love." Which you +prefer is a matter of taste, but not entirely a matter of the editor's +taste. It is a matter of his judgment as to what will absorb the half +hour's attention a certain set of readers will give to his newspaper. +Now the problem of securing attention is by no means equivalent to +displaying the news in the perspective laid down by religious teaching +or by some form of ethical culture. It is a problem of provoking +feeling in the reader, of inducing him to feel a sense of personal +identification with the stories he is reading. News which does not +offer this opportunity to introduce oneself into the struggle which it +depicts cannot appeal to a wide audience. The audience must +participate in the news, much as it participates in the drama, by +personal identification. Just as everyone holds his breath when the +heroine is in danger, as he helps Babe Ruth swing his bat, so in +subtler form the reader enters into the news. In order that he shall +enter he must find a familiar foothold in the story, and this is +supplied to him by the use of stereotypes. They tell him that if an +association of plumbers is called a "combine" it is appropriate to +develop his hostility; if it is called a "group of leading business +men" the cue is for a favorable reaction. + +It is in a combination of these elements that the power to create +opinion resides. Editorials reinforce. Sometimes in a situation that +on the news pages is too confusing to permit of identification, they +give the reader a clue by means of which he engages himself. A clue he +must have if, as most of us must, he is to seize the news in a hurry. +A suggestion of some sort he demands, which tells him, so to speak, +where he, a man conceiving himself to be such and such a person, shall +integrate his feelings with the news he reads. + +"It has been said" writes Walter Bagehot, [Footnote: On the Emotion of +Conviction, _Literary Studies_, Vol. Ill, p. 172.] "that if you +can only get a middleclass Englishman to think whether there are +'snails in Sirius,' he will soon have an opinion on it. It will be +difficult to make him think, but if he does think, he cannot rest in a +negative, he will come to some decision. And on any ordinary topic, of +course, it is so. A grocer has a full creed as to foreign policy, a +young lady a complete theory of the sacraments, as to which neither +has any doubt whatever." + +Yet that same grocer will have many doubts about his groceries, and +that young lady, marvelously certain about the sacraments, may have +all kinds of doubts as to whether to marry the grocer, and if not +whether it is proper to accept his attentions. The ability to rest in +the negative implies either a lack of interest in the result, or a +vivid sense of competing alternatives. In the case of foreign policy +or the sacraments, the interest in the results is intense, while means +for checking the opinion are poor. This is the plight of the reader of +the general news. If he is to read it at all he must be interested, +that is to say, he must enter into the situation and care about the +outcome. But if he does that he cannot rest in a negative, and unless +independent means of checking the lead given him by his newspaper +exists, the very fact that he is interested may make it difficult to +arrive at that balance of opinions which may most nearly approximate +the truth. The more passionately involved he becomes, the more he will +tend to resent not only a different view, but a disturbing bit of +news. That is why many a newspaper finds that, having honestly evoked +the partisanship of its readers, it can not easily, supposing the +editor believes the facts warrant it, change position. If a change is +necessary, the transition has to be managed with the utmost skill and +delicacy. Usually a newspaper will not attempt so hazardous a +performance. It is easier and safer to have the news of that subject +taper off and disappear, thus putting out the fire by starving it. + + + + +CHAPTER XXIV + +NEWS, TRUTH, AND A CONCLUSION + +As we begin to make more and more exact studies of the press, much +will depend upon the hypothesis we hold. If we assume with Mr. +Sinclair, and most of his opponents, that news and truth are two words +for the same thing, we shall, I believe, arrive nowhere. We shall +prove that on this point the newspaper lied. We shall prove that on +that point Mr. Sinclair's account lied. We shall demonstrate that Mr. +Sinclair lied when he said that somebody lied, and that somebody lied +when he said Mr. Sinclair lied. We shall vent our feelings, but we +shall vent them into air. + +The hypothesis, which seems to me the most fertile, is that news and +truth are not the same thing, and must be clearly distinguished. +[Footnote: When I wrote _Liberty and the News,_ I did not +understand this distinction clearly enough to state it, but _cf._ +p. 89 ff.] The function of news is to signalize an event, the function +of truth is to bring to light the hidden facts, to set them into +relation with each other, and make a picture of reality on which men +can act. Only at those points, where social conditions take +recognizable and measurable shape, do the body of truth and the body +of news coincide. That is a comparatively small part of the whole +field of human interest. In this sector, and only in this sector, the +tests of the news are sufficiently exact to make the charges of +perversion or suppression more than a partisan judgment. There is no +defense, no extenuation, no excuse whatever, for stating six times +that Lenin is dead, when the only information the paper possesses is a +report that he is dead from a source repeatedly shown to be +unreliable. The news, in that instance, is not "Lenin Dead" but +"Helsingfors Says Lenin is Dead." And a newspaper can be asked to take +the responsibility of not making Lenin more dead than the source of +the news is reliable; if there is one subject on which editors are +most responsible it is in their judgment of the reliability of the +source. But when it comes to dealing, for example, with stories of +what the Russian people want, no such test exists. + +The absence of these exact tests accounts, I think, for the character +of the profession, as no other explanation does. There is a very small +body of exact knowledge, which it requires no outstanding ability or +training to deal with. The rest is in the journalist's own discretion. +Once he departs from the region where it is definitely recorded at the +County Clerk's office that John Smith has gone into bankruptcy, all +fixed standards disappear. The story of why John Smith failed, his +human frailties, the analysis of the economic conditions on which he +was shipwrecked, all of this can be told in a hundred different ways. +There is no discipline in applied psychology, as there is a discipline +in medicine, engineering, or even law, which has authority to direct +the journalist's mind when he passes from the news to the vague realm +of truth. There are no canons to direct his own mind, and no canons +that coerce the reader's judgment or the publisher's. His version of +the truth is only his version. How can he demonstrate the truth as he +sees it? He cannot demonstrate it, any more than Mr. Sinclair Lewis +can demonstrate that he has told the whole truth about Main Street. +And the more he understands his own weaknesses, the more ready he is +to admit that where there is no objective test, his own opinion is in +some vital measure constructed out of his own stereotypes, according +to his own code, and by the urgency of his own interest. He knows that +he is seeing the world through subjective lenses. He cannot deny that +he too is, as Shelley remarked, a dome of many-colored glass which +stains the white radiance of eternity. + +And by this knowledge his assurance is tempered. He may have all kinds +of moral courage, and sometimes has, but he lacks that sustaining +conviction of a certain technic which finally freed the physical +sciences from theological control. It was the gradual development of +an irrefragable method that gave the physicist his intellectual +freedom as against all the powers of the world. His proofs were so +clear, his evidence so sharply superior to tradition, that he broke +away finally from all control. But the journalist has no such support +in his own conscience or in fact. The control exercised over him by +the opinions of his employers and his readers, is not the control of +truth by prejudice, but of one opinion by another opinion that it is +not demonstrably less true. Between Judge Gary's assertion that the +unions will destroy American institutions, and Mr. Gomper's assertion +that they are agencies of the rights of man, the choice has, in large +measure, to be governed by the will to believe. + +The task of deflating these controversies, and reducing them to a +point where they can be reported as news, is not a task which the +reporter can perform. It is possible and necessary for journalists to +bring home to people the uncertain character of the truth on which +their opinions are founded, and by criticism and agitation to prod +social science into making more usable formulations of social facts, +and to prod statesmen into establishing more visible institutions. The +press, in other words, can fight for the extension of reportable +truth. But as social truth is organized to-day, the press is not +constituted to furnish from one edition to the next the amount of +knowledge which the democratic theory of public opinion demands. This +is not due to the Brass Check, as the quality of news in radical +papers shows, but to the fact that the press deals with a society in +which the governing forces are so imperfectly recorded. The theory +that the press can itself record those forces is false. It can +normally record only what has been recorded for it by the working of +institutions. Everything else is argument and opinion, and fluctuates +with the vicissitudes, the self-consciousness, and the courage of the +human mind. + +If the press is not so universally wicked, nor so deeply conspiring, +as Mr. Sinclair would have us believe, it is very much more frail than +the democratic theory has as yet admitted. It is too frail to carry +the whole burden of popular sovereignty, to supply spontaneously the +truth which democrats hoped was inborn. And when we expect it to +supply such a body of truth we employ a misleading standard of +judgment. We misunderstand the limited nature of news, the illimitable +complexity of society; we overestimate our own endurance, public +spirit, and all-round competence. We suppose an appetite for +uninteresting truths which is not discovered by any honest analysis of +our own tastes. + +If the newspapers, then, are to be charged with the duty of +translating the whole public life of mankind, so that every adult can +arrive at an opinion on every moot topic, they fail, they are bound to +fail, in any future one can conceive they will continue to fail. It is +not possible to assume that a world, carried on by division of labor +and distribution of authority, can be governed by universal opinions +in the whole population. Unconsciously the theory sets up the single +reader as theoretically omnicompetent, and puts upon the press the +burden of accomplishing whatever representative government, industrial +organization, and diplomacy have failed to accomplish. Acting upon +everybody for thirty minutes in twenty-four hours, the press is asked +to create a mystical force called Public Opinion that will take up the +slack in public institutions. The press has often mistakenly pretended +that it could do just that. It has at great moral cost to itself, +encouraged a democracy, still bound to its original premises, to +expect newspapers to supply spontaneously for every organ of +government, for every social problem, the machinery of information +which these do not normally supply themselves. Institutions, having +failed to furnish themselves with instruments of knowledge, have +become a bundle of "problems," which the population as a whole, +reading the press as a whole, is supposed to solve. + +The press, in other words, has come to be regarded as an organ of +direct democracy, charged on a much wider scale, and from day to day, +with the function often attributed to the initiative, referendum, and +recall. The Court of Public Opinion, open day and night, is to lay +down the law for everything all the time. It is not workable. And when +you consider the nature of news, it is not even thinkable. For the +news, as we have seen, is precise in proportion to the precision with +which the event is recorded. Unless the event is capable of being +named, measured, given shape, made specific, it either fails to take +on the character of news, or it is subject to the accidents and +prejudices of observation. + +Therefore, on the whole, the quality of the news about modern society +is an index of its social organization. The better the institutions, +the more all interests concerned are formally represented, the more +issues are disentangled, the more objective criteria are introduced, +the more perfectly an affair can be presented as news. At its best the +press is a servant and guardian of institutions; at its worst it is a +means by which a few exploit social disorganization to their own ends. +In the degree to which institutions fail to function, the unscrupulous +journalist can fish in troubled waters, and the conscientious one must +gamble with uncertainties. + +The press is no substitute for institutions. It is like the beam of a +searchlight that moves restlessly about, bringing one episode and then +another out of darkness into vision. Men cannot do the work of the +world by this light alone. They cannot govern society by episodes, +incidents, and eruptions. It is only when they work by a steady light +of their own, that the press, when it is turned upon them, reveals a +situation intelligible enough for a popular decision. The trouble lies +deeper than the press, and so does the remedy. It lies in social +organization based on a system of analysis and record, and in all the +corollaries of that principle; in the abandonment of the theory of the +omnicompetent citizen, in the decentralization of decision, in the +coordination of decision by comparable record and analysis. If at the +centers of management there is a running audit, which makes work +intelligible to those who do it, and those who superintend it, issues +when they arise are not the mere collisions of the blind. Then, too, +the news is uncovered for the press by a system of intelligence that +is also a check upon the press. + +That is the radical way. For the troubles of the press, like the +troubles of representative government, be it territorial or +functional, like the troubles of industry, be it capitalist, +cooperative, or communist, go back to a common source: to the failure +of self-governing people to transcend their casual experience and +their prejudice, by inventing, creating, and organizing a machinery of +knowledge. It is because they are compelled to act without a reliable +picture of the world, that governments, schools, newspapers and +churches make such small headway against the more obvious failings of +democracy, against violent prejudice, apathy, preference for the +curious trivial as against the dull important, and the hunger for +sideshows and three legged calves. This is the primary defect of +popular government, a defect inherent in its traditions, and all its +other defects can, I believe, be traced to this one. + + + + +PART VIII + +ORGANIZED INTELLIGENCE + +CHAPTER XXV. THE ENTERING WEDGE + " XXVI. INTELLIGENCE WORK + " XXVII. THE APPEAL TO THE PUBLIC + " XXVIII. THE APPEAL TO REASON + + + + +CHAPTER XXV + +THE ENTERING WEDGE + +1 + +If the remedy were interesting, American pioneers like Charles +McCarthy, Robert Valentine, and Frederick W. Taylor would not have had +to fight so hard for a hearing. But it is clear why they had to fight, +and why bureaus of governmental research, industrial audits, budgeting +and the like are the ugly ducklings of reform. They reverse the +process by which interesting public opinions are built up. Instead of +presenting a casual fact, a large screen of stereotypes, and a +dramatic identification, they break down the drama, break through the +stereotypes, and offer men a picture of facts, which is unfamiliar and +to them impersonal. When this is not painful, it is dull, and those to +whom it is painful, the trading politician and the partisan who has +much to conceal, often exploit the dullness that the public feels, in +order to remove the pain that they feel. + +2 + +Yet every complicated community has sought the assistance of special +men, of augurs, priests, elders. Our own democracy, based though it +was on a theory of universal competence, sought lawyers to manage its +government, and to help manage its industry. It was recognized that +the specially trained man was in some dim way oriented to a wider +system of truth than that which arises spontaneously in the amateur's +mind. But experience has shown that the traditional lawyer's equipment +was not enough assistance. The Great Society had grown furiously and +to colossal dimensions by the application of technical knowledge. It +was made by engineers who had learned to use exact measurements and +quantitative analysis. It could not be governed, men began to +discover, by men who thought deductively about rights and wrongs. It +could be brought under human control only by the technic which had +created it. Gradually, then, the more enlightened directing minds have +called in experts who were trained, or had trained themselves, to make +parts of this Great Society intelligible to those who manage it. These +men are known by all kinds of names, as statisticians, accountants, +auditors, industrial counsellors, engineers of many species, +scientific managers, personnel administrators, research men, +"scientists," and sometimes just as plain private secretaries. They +have brought with them each a jargon of his own, as well as filing +cabinets, card catalogues, graphs, loose-leaf contraptions, and above +all the perfectly sound ideal of an executive who sits before a +flat-top desk, one sheet of typewritten paper before him, and decides +on matters of policy presented in a form ready for his rejection or +approval. + +This whole development has been the work, not so much of a spontaneous +creative evolution, as of blind natural selection. The statesman, the +executive, the party leader, the head of a voluntary association, +found that if he had to discuss two dozen different subjects in the +course of the day, somebody would have to coach him. He began to +clamor for memoranda. He found he could not read his mail. He demanded +somebody who would blue-pencil the interesting sentences in the +important letters. He found he could not digest the great stacks of +type-written reports that grew mellow on his desk. He demanded +summaries. He found he could not read an unending series of figures. +He embraced the man who made colored pictures of them. He found that +he really did not know one machine from another. He hired engineers to +pick them, and tell him how much they cost and what they could do. He +peeled off one burden after another, as a man will take off first his +hat, then his coat, then his collar, when he is struggling to move an +unwieldy load. + +3 + +Yet curiously enough, though he knew that he needed help, he was slow +to call in the social scientist. The chemist, the physicist, the +geologist, had a much earlier and more friendly reception. +Laboratories were set up for them, inducements offered, for there was +quick appreciation of the victories over nature. But the scientist who +has human nature as his problem is in a different case. There are many +reasons for this: the chief one, that he has so few victories to +exhibit. He has so few, because unless he deals with the historic +past, he cannot prove his theories before offering them to the public. +The physical scientist can make an hypothesis, test it, revise the +hypothesis hundreds of times, and, if after all that, he is wrong, no +one else has to pay the price. But the social scientist cannot begin +to offer the assurance of a laboratory test, and if his advice is +followed, and he is wrong, the consequences may be incalculable. He is +in the nature of things far more responsible, and far less certain. + +But more than that. In the laboratory sciences the student has +conquered the dilemma of thought and action. He brings a sample of the +action to a quiet place, where it can be repeated at will, and +examined at leisure. But the social scientist is constantly being +impaled on a dilemma. If he stays in his library, where he has the +leisure to think, he has to rely upon the exceedingly casual and +meager printed record that comes to him through official reports, +newspapers, and interviews. If he goes out into "the world" where +things are happening, he has to serve a long, often wasteful, +apprenticeship, before he is admitted to the sanctum where they are +being decided. What he cannot do is to dip into action and out again +whenever it suits him. There are no privileged listeners. The man of +affairs, observing that the social scientist knows only from the +outside what he knows, in part at least, from the inside, recognizing +that the social scientist's hypothesis is not in the nature of things +susceptible of laboratory proof, and that verification is possible +only in the "real" world, has developed a rather low opinion of social +scientists who do not share his views of public policy. + +In his heart of hearts the social scientist shares this estimate of +himself. He has little inner certainty about his own work. He only +half believes in it, and being sure of nothing, he can find no +compelling reason for insisting on his own freedom of thought. What +can he actually claim for it, in the light of his own conscience? +[Footnote: Cf. Charles E. Merriam, _The Present State of the Study +of Politics_, _American Political Science Review_, Vol. XV. +No. 2, May, 1921.] His data are uncertain, his means of verification +lacking. The very best qualities in him are a source of frustration. +For if he is really critical and saturated in the scientific spirit, +he cannot be doctrinaire, and go to Armageddon against the trustees +and the students and the Civic Federation and the conservative press +for a theory of which he is not sure. If you are going to Armageddon, +you have to battle for the Lord, but the political scientist is always +a little doubtful whether the Lord called him. + +Consequently if so much of social science is apologetic rather than +constructive, the explanation lies in the opportunities of social +science, not in "capitalism." The physical scientists achieved their +freedom from clericalism by working out a method that produced +conclusions of a sort that could not be suppressed or ignored. They +convinced themselves and acquired dignity, and knew what they were +fighting for. The social scientist will acquire his dignity and his +strength when he has worked out his method. He will do that by turning +into opportunity the need among directing men of the Great Society for +instruments of analysis by which an invisible and made intelligible. + +But as things go now, the social scientist assembles his data out of a +mass of unrelated material. Social processes are recorded +spasmodically, quite often as accidents of administration. A report to +Congress, a debate, an investigation, legal briefs, a census, a +tariff, a tax schedule; the material, like the skull of the Piltdown +man, has to be put together by ingenious inference before the student +obtains any sort of picture of the event he is studying. Though it +deals with the conscious life of his fellow citizens, it is all too +often distressingly opaque, because the man who is trying to +generalize has practically no supervision of the way his data are +collected. Imagine medical research conducted by students who could +rarely go into a hospital, were deprived of animal experiment, and +compelled to draw conclusions from the stories of people who had been +ill, the reports of nurses, each of whom had her own system of +diagnosis, and the statistics compiled by the Bureau of Internal +Revenue on the excess profits of druggists. The social scientist has +usually to make what he can out of categories that were uncritically +in the mind of an official who administered some part of a law, or who +was out to justify, to persuade, to claim, or to prove. The student +knows this, and, as a protection against it, has developed that branch +of scholarship which is an elaborated suspicion about where to +discount his information. + +That is a virtue, but it becomes a very thin virtue when it is merely +a corrective for the unwholesome position of social science. For the +scholar is condemned to guess as shrewdly as he can why in a situation +not clearly understood something or other may have happened. But the +expert who is employed as the mediator among representatives, and as +the mirror and measure of administration, has a very different control +of the facts. Instead of being the man who generalizes from the facts +dropped to him by the men of action, he becomes the man who prepares +the facts for the men of action. This is a profound change in his +strategic position. He no longer stands outside, chewing the cud +provided by busy men of affairs, but he takes his place in front of +decision instead of behind it. To-day the sequence is that the man of +affairs finds his facts, and decides on the basis of them; then, some +time later, the social scientist deduces excellent reasons why he did +or did not decide wisely. This ex post facto relationship is academic +in the bad sense of that fine word. The real sequence should be one +where the disinterested expert first finds and formulates the facts +for the man of action, and later makes what wisdom he can out of +comparison between the decision, which he understands, and the facts, +which he organized. + +4 + +For the physical sciences this change in strategic position began +slowly, and then accelerated rapidly. There was a time when the +inventor and the engineer were romantic half-starved outsiders, +treated as cranks. The business man and the artisan knew all the +mysteries of their craft. Then the mysteries grew more mysterious, and +at last industry began to depend upon physical laws and chemical +combinations that no eye could see, and only a trained mind could +conceive. The scientist moved from his noble garret in the Latin +Quarter into office buildings and laboratories. For he alone could +construct a working image of the reality on which industry rested. +From the new relationship he took as much as he gave, perhaps more: +pure science developed faster than applied, though it drew its +economic support, a great deal of its inspiration, and even more of +its relevancy, from constant contact with practical decision. But +physical science still labored under the enormous limitation that the +men who made decisions had only their commonsense to guide them. They +administered without scientific aid a world complicated by scientists. +Again they had to deal with facts they could not apprehend, and as +once they had to call in engineers, they now have to call in +statisticians, accountants, experts of all sorts. + +These practical students are the true pioneers of a new social +science. They are "in mesh with the driving wheels" [Footnote: Cf. The +Address of the President of the American Philosophical Association, +Mr. Ralph Barton Perry, Dec. 28, 1920. Published in the Proceedings of +the Twentieth Annual Meeting.] and from this practical engagement of +science and action, both will benefit radically: action by the +clarification of its beliefs; beliefs by a continuing test in action. +We are in the earliest beginnings. But if it is conceded that all +large forms of human association must, because of sheer practical +difficulty, contain men who will come to see the need for an expert +reporting of their particular environment, then the imagination has a +premise on which to work. In the exchange of technic and result among +expert staffs, one can see, I think, the beginning of experimental +method in social science. When each school district and budget, and +health department, and factory, and tariff schedule, is the material +of knowledge for every other, the number of comparable experiences +begins to approach the dimensions of genuine experiment. In +forty-eight states, and 2400 cities, and 277,000 school houses, +270,000 manufacturing establishments, 27,000 mines and quarries, there +is a wealth of experience, if only it were recorded and available. And +there is, too, opportunity for trial and error at such slight risk +that any reasonable hypothesis might be given a fair test without +shaking the foundations of society. + +The wedge has been driven, not only by some directors of industry and +some statesmen who had to have help, but by the bureaus of municipal +research, [Footnote: The number of these organizations in the United +States is very great. Some are alive, some half dead. They are in +rapid flux. Lists of them supplied to me by Dr. L. D. Upson of the +Detroit Bureau of Governmental Research, Miss Rebecca B. Rankin of the +Municipal Reference Library of New York City, Mr. Edward A. +Fitzpatrick, Secretary of the State Board of Education (Wisconsin), +Mr. Savel Zimand of the Bureau of Industrial Research (New York City), +run into the hundreds.] the legislative reference libraries, the +specialized lobbies of corporations and trade unions and public +causes, and by voluntary organizations like the League of Women +Voters, the Consumers' League, the Manufacturers' Associations: by +hundreds of trade associations, and citizens' unions; by publications +like the _Searchlight on Congress_ and the _Survey_; and by +foundations like the General Education Board. Not all by any means are +disinterested. That is not the point. All of them do begin to +demonstrate the need for interposing some form of expertness between +the private citizen and the vast environment in which he is entangled. + + + + +CHAPTER XXVI + +INTELLIGENCE WORK + +1 + +THE practice of democracy has been ahead of its theory. For the theory +holds that the adult electors taken together make decisions out of a +will that is in them. But just as there grew up governing hierarchies +which were invisible in theory, so there has been a large amount of +constructive adaptation, also unaccounted for in the image of +democracy. Ways have been found to represent many interests and +functions that are normally out of sight. + +We are most conscious of this in our theory of the courts, when we +explain their legislative powers and their vetoes on the theory that +there are interests to be guarded which might be forgotten by the +elected officials. But the Census Bureau, when it counts, classifies, +and correlates people, things, and changes, is also speaking for +unseen factors in the environment. The Geological Survey makes mineral +resources evident, the Department of Agriculture represents in the +councils of the nation factors of which each farmer sees only an +infinitesimal part. School authorities, the Tariff Commission, the +consular service, the Bureau of Internal Revenue give representation +to persons, ideas, and objects which would never automatically find +themselves represented in this perspective by an election. The +Children's Bureau is the spokesman of a whole complex of interests and +functions not ordinarily visible to the voter, and, therefore, +incapable of becoming spontaneously a part of his public opinions. +Thus the printing of comparative statistics of infant mortality is +often followed by a reduction of the death rate of babies. Municipal +officials and voters did not have, before publication, a place in +their picture of the environment for those babies. The statistics made +them visible, as visible as if the babies had elected an alderman to +air their grievances. + +In the State Department the government maintains a Division of Far +Eastern Affairs. What is it for? The Japanese and the Chinese +Governments both maintain ambassadors in Washington. Are they not +qualified to speak for the Far East? They are its representatives. Yet +nobody would argue that the American Government could learn all that +it needed to know about the Far East by consulting these ambassadors. +Supposing them to be as candid as they know how to be, they are still +limited channels of information. Therefore, to supplement them we +maintain embassies in Tokio and Peking, and consular agents at many +points. Also, I assume, some secret agents. These people are supposed +to send reports which pass through the Division of Far Eastern Affairs +to the Secretary of State. Now what does the Secretary expect of the +Division? I know one who expected it to spend its appropriation. But +there are Secretaries to whom special revelation is denied, and they +turn to their divisions for help. The last thing they expect to find +is a neat argument justifying the American position. + +What they demand is that the experts shall bring the Far East to the +Secretary's desk, with all the elements in such relation that it is as +if he were in contact with the Far East itself. The expert must +translate, simplify, generalize, but the inference from the result +must apply in the East, not merely on the premises of the report. If +the Secretary is worth his salt, the very last thing he will tolerate +in his experts is the suspicion that they have a "policy." He does not +want to know from them whether they like Japanese policy in China. He +wants to know what different classes of Chinese and Japanese, English, +Frenchmen, Germans, and Russians, think about it, and what they are +likely to do because of what they think. He wants all that represented +to him as the basis of his decision. The more faithfully the Division +represents what is not otherwise represented, either by the Japanese +or American ambassadors, or the Senators and Congressmen from the +Pacific coast, the better Secretary of State he will be. He may decide +to take his policy from the Pacific Coast, but he will take his view +of Japan from Japan. + +2 + +It is no accident that the best diplomatic service in the world is the +one in which the divorce between the assembling of knowledge and the +control of policy is most perfect. During the war in many British +Embassies and in the British Foreign Office there were nearly always +men, permanent officials or else special appointees, who quite +successfully discounted the prevailing war mind. They discarded the +rigmarole of being pro and con, of having favorite nationalities, and +pet aversions, and undelivered perorations in their bosoms. They left +that to the political chiefs. But in an American Embassy I once heard +an ambassador say that he never reported anything to Washington which +would not cheer up the folks at home. He charmed all those who met +him, helped many a stranded war worker, and was superb when he +unveiled a monument. + +He did not understand that the power of the expert depends upon +separating himself from those who make the decisions, upon not caring, +in his expert self, what decision is made. The man who, like the +ambassador, takes a line, and meddles with the decision, is soon +discounted. There he is, just one more on that side of the question. +For when he begins to care too much, he begins to see what he wishes +to see, and by that fact ceases to see what he is there to see. He is +there to represent the unseen. He represents people who are not +voters, functions of voters that are not evident, events that are out +of sight, mute people, unborn people, relations between things and +people. He has a constituency of intangibles. And intangibles cannot +be used to form a political majority, because voting is in the last +analysis a test of strength, a sublimated battle, and the expert +represents no strength available in the immediate. But he can exercise +force by disturbing the line up of the forces. By making the invisible +visible, he confronts the people who exercise material force with a +new environment, sets ideas and feelings at work in them, throws them +out of position, and so, in the profoundest way, affects the decision. + +Men cannot long act in a way that they know is a contradiction of the +environment as they conceive it. If they are bent on acting in a +certain way they have to reconceive the environment, they have to +censor out, to rationalize. But if in their presence, there is an +insistent fact which is so obtrusive that they cannot explain it away, +one of three courses is open. They can perversely ignore it, though +they will cripple themselves in the process, will overact their part +and come to grief. They can take it into account but refuse to act. +They pay in internal discomfort and frustration. Or, and I believe +this to be the most frequent case, they adjust their whole behavior to +the enlarged environment. + +The idea that the expert is an ineffectual person because he lets +others make the decisions is quite contrary to experience. The more +subtle the elements that enter into the decision, the more +irresponsible power the expert wields. He is certain, moreover, to +exercise more power in the future than ever he did before, because +increasingly the relevant facts will elude the voter and the +administrator. All governing agencies will tend to organize bodies of +research and information, which will throw out tentacles and expand, +as have the intelligence departments of all the armies in the world. +But the experts will remain human beings. They will enjoy power, and +their temptation will be to appoint themselves censors, and so absorb +the real function of decision. Unless their function is correctly +defined they will tend to pass on the facts they think appropriate, +and to pass down the decisions they approve. They will tend, in short, +to become a bureaucracy. + +The only institutional safeguard is to separate as absolutely as it is +possible to do so the staff which executes from the staff which +investigates. The two should be parallel but quite distinct bodies of +men, recruited differently, paid if possible from separate funds, +responsible to different heads, intrinsically uninterested in each +other's personal success. In industry, the auditors, accountants, and +inspectors should be independent of the manager, the superintendents, +foremen, and in time, I believe, we shall come to see that in order to +bring industry under social control the machinery of record will have +to be independent of the boards of directors and the shareholders. + +3 + +But in building the intelligence sections of industry and politics, we +do not start on cleared ground. And, apart from insisting on this +basic separation of function, it would be cumbersome to insist too +precisely on the form which in any particular instance the principle +shall take. There are men who believe in intelligence work, and will +adopt it; there are men who do not understand it, but cannot do their +work without it; there are men who will resist. But provided the +principle has a foothold somewhere in every social agency it will make +progress, and the way to begin is to begin. In the federal government, +for example, it is not necessary to straighten out the administrative +tangle and the illogical duplications of a century's growth in order +to find a neat place for the intelligence bureaus which Washington so +badly needs. Before election you can promise to rush bravely into the +breach. But when you arrive there all out of breath, you find that +each absurdity is invested with habits, strong interests, and chummy +Congressmen. Attack all along the line and you engage every force of +reaction. You go forth to battle, as the poet said, and you always +fall. You can lop off an antiquated bureau here, a covey of clerks +there, you can combine two bureaus. And by that time you are busy with +the tariff and the railroads, and the era of reform is over. Besides, +in order to effect a truly logical reorganization of the government, +such as all candidates always promise, you would have to disturb more +passions than you have time to quell. And any new scheme, supposing +you had one ready, would require officials to man it. Say what one +will about officeholders, even Soviet Russia was glad to get many of +the old ones back; and these old officials, if they are too ruthlessly +treated, will sabotage Utopia itself. + +No administrative scheme is workable without good will, and good will +about strange practices is impossible without education. The better +way is to introduce into the existing machinery, wherever you can find +an opening, agencies that will hold up a mirror week by week, month by +month. You can hope, then, to make the machine visible to those who +work it, as well as to the chiefs who are responsible, and to the +public outside. When the office-holders begin to see themselves,--or +rather when the outsiders, the chiefs, and the subordinates all begin +to see the same facts, the same damning facts if you like, the +obstruction will diminish. The reformer's opinion that a certain +bureau is inefficient is just his opinion, not so good an opinion in +the eyes of the bureau, as its own. But let the work of that bureau be +analysed and recorded, and then compared with other bureaus and with +private corporations, and the argument moves to another plane. + +There are ten departments at Washington represented in the Cabinet. +Suppose, then, there was a permanent intelligence section for each. +What would be some of the conditions of effectiveness? Beyond all +others that the intelligence officials should be independent both of +the Congressional Committees dealing with that department, and of the +Secretary at the head of it; that they should not be entangled either +in decision or in action. Independence, then, would turn mainly on +three points on funds, tenure, and access to the facts. For clearly if +a particular Congress or departmental official can deprive them of +money, dismiss them, or close the files, the staff becomes its +creature. + +4 + +The question of funds is both important and difficult. No agency of +research can be really free if it depends upon annual doles from what +may be a jealous or a parsimonious congress. Yet the ultimate control +of funds cannot be removed from the legislature. The financial +arrangement should insure the staff against left-handed, joker and +rider attack, against sly destruction, and should at the same time +provide for growth. The staff should be so well entrenched that an +attack on its existence would have to be made in the open. It might, +perhaps, work behind a federal charter creating a trust fund, and a +sliding scale over a period of years based on the appropriation for +the department to which the intelligence bureau belonged. No great +sums of money are involved anyway. The trust fund might cover the +overhead and capital charges for a certain minimum staff, the sliding +scale might cover the enlargements. At any rate the appropriation +should be put beyond accident, like the payment of any long term +obligation. This is a much less serious way of "tying the hands of +Congress" than is the passage of a Constitutional amendment or the +issuance of government bonds. Congress could repeal the charter. But +it would have to repeal it, not throw monkey wrenches into it. + +Tenure should be for life, with provision for retirement on a liberal +pension, with sabbatical years set aside for advanced study and +training, and with dismissal only after a trial by professional +colleagues. The conditions which apply to any non-profit-making +intellectual career should apply here. If the work is to be salient, +the men who do it must have dignity, security, and, in the upper ranks +at least, that freedom of mind which you find only where men are not +too immediately concerned in practical decision. + +Access to the materials should be established in the organic act. The +bureau should have the right to examine all papers, and to question +any official or any outsider. Continuous investigation of this sort +would not at all resemble the sensational legislative inquiry and the +spasmodic fishing expedition which are now a common feature of our +government. The bureau should have the right to propose accounting +methods to the department, and if the proposal is rejected, or +violated after it has been accepted, to appeal under its charter to +Congress. + +In the first instance each intelligence bureau would be the connecting +link between Congress and the Department, a better link, in my +judgment, than the appearance of cabinet officers on the floor of both +House and Senate, though the one proposal in no way excludes the +other. The bureau would be the Congressional eye on the execution of +its policy. It would be the departmental answer to Congressional +criticism. And then, since operation of the Department would be +permanently visible, perhaps Congress would cease to feel the need of +that minute legislation born of distrust and a false doctrine of the +separation of powers, which does so much to make efficient +administration difficult. + +5 + +But, of course, each of the ten bureaus could not work in a watertight +compartment. In their relation one to another lies the best chance for +that "coordination" of which so much is heard and so little seen. +Clearly the various staffs would need to adopt, wherever possible, +standards of measurement that were comparable. They would exchange +their records. Then if the War Department and the Post Office both buy +lumber, hire carpenters, or construct brick walls they need not +necessarily do them through the same agency, for that might mean +cumbersome over-centralization; but they would be able to use the same +measure for the same things, be conscious of the comparisons, and be +treated as competitors. And the more competition of this sort the +better. + +For the value of competition is determined by the value of the +standards used to measure it. Instead, then, of asking ourselves +whether we believe in competition, we should ask ourselves whether we +believe in that for which the competitors compete. No one in his +senses expects to "abolish competition," for when the last vestige of +emulation had disappeared, social effort would consist in mechanical +obedience to a routine, tempered in a minority by native inspiration. +Yet no one expects to work out competition to its logical conclusion +in a murderous struggle of each against all. The problem is to select +the goals of competition and the rules of the game. Almost always the +most visible and obvious standard of measurement will determine the +rules of the game: such as money, power, popularity, applause, or Mr. +Veblen's "conspicuous waste." What other standards of measurement does +our civilization normally provide? How does it measure efficiency, +productivity, service, for which we are always clamoring? + +By and large there are no measures, and there is, therefore, not so +much competition to achieve these ideals. For the difference between +the higher and the lower motives is not, as men often assert, a +difference between altruism and selfishness. [Footnote: _Cf._ +Ch. XII] It is a difference between acting for easily understood aims, +and for aims that are obscure and vague. Exhort a man to make more +profit than his neighbor, and he knows at what to aim. Exhort him to +render more social service, and how is he to be certain what service +is social? What is the test, what is the measure? A subjective +feeling, somebody's opinion. Tell a man in time of peace that he ought +to serve his country and you have uttered a pious platitude, Tell him +in time of war, and the word service has a meaning; it is a number of +concrete acts, enlistment, or buying bonds, or saving food, or working +for a dollar a year, and each one of these services he sees definitely +as part of a concrete purpose to put at the front an army larger and +better armed, than the enemy's. + +So the more you are able to analyze administration and work out +elements that can be compared, the more you invent quantitative +measures for the qualities you wish to promote, the more you can turn +competition to ideal ends. If you can contrive the right index numbers +[Footnote: I am not using the term index numbers in its purely +technical meaning, but to cover any device for the comparative +measurement of social phenomena.] you can set up a competition between +individual workers in a shop; between shops; between factories; +between schools; [Footnote: See, for example, _An Index Number for +State School Systems_ by Leonard P. Ayres, Russell Sage Foundation, +1920. The principle of the quota was very successfully applied in the +Liberty Loan Campaigns, and under very much more difficult +circumstances by the Allied Maritime Transport Council.] between +government departments; between regiments; between divisions; between +ships; between states; counties; cities; and the better your index +numbers the more useful the competition. + +6 + +The possibilities that lie in the exchange of material are evident. +Each department of government is all the time asking for information +that may already have been obtained by another department, though +perhaps in a somewhat different form. The State Department needs to +know, let us say, the extent of the Mexican oil reserves, their +relation to the rest of the world's supply, the present ownership of +Mexican oil lands, the importance of oil to warships now under +construction or planned, the comparative costs in different fields. +How does it secure such information to-day? The information is +probably scattered through the Departments of Interior, Justice, +Commerce, Labor and Navy. Either a clerk in the State Department looks +up Mexican oil in a book of reference, which may or may not be +accurate, or somebody's private secretary telephones somebody else's +private secretary, asks for a memorandum, and in the course of time a +darkey messenger arrives with an armful of unintelligible reports. The +Department should be able to call on its own intelligence bureau to +assemble the facts in a way suited to the diplomatic problem up for +decision. And these facts the diplomatic intelligence bureau would +obtain from the central clearing house. [Footnote: There has been a +vast development of such services among the trade associations. The +possibilities of a perverted use were revealed by the New York +Building Trades investigation of 1921.] + +This establishment would pretty soon become a focus of information of +the most extraordinary kind. And the men in it would be made aware of +what the problems of government really are. They would deal with +problems of definition, of terminology, of statistical technic, of +logic; they would traverse concretely the whole gamut of the social +sciences. It is difficult to see why all this material, except a few +diplomatic and military secrets, should not be open to the scholars of +the country. It is there that the political scientist would find the +real nuts to crack and the real researches for his students to make. +The work need not all be done in Washington, but it could be done in +reference to Washington. The central agency would, thus, have in it +the makings of a national university. The staff could be recruited +there for the bureaus from among college graduates. They would be +working on theses selected after consultation between the curators of +the national university and teachers scattered over the country. If +the association was as flexible as it ought to be, there would be, as +a supplement to the permanent staff, a steady turnover of temporary +and specialist appointments from the universities, and exchange +lecturers called out from Washington. Thus the training and the +recruiting of the staff would go together. A part of the research +itself would be done by students, and political science in the +universities would be associated with politics in America. + +7 + +In its main outlines the principle is equally applicable to state +governments, to cities, and to rural counties. The work of comparison +and interchange could take place by federations of state and city and +county bureaus. And within those federations any desirable regional +combination could be organized. So long as the accounting systems were +comparable, a great deal of duplication would be avoided. Regional +coordination is especially desirable. For legal frontiers often do not +coincide with the effective environments. Yet they have a certain +basis in custom that it would be costly to disturb. By coordinating +their information several administrative areas could reconcile +autonomy of decision with cooperation. New York City, for example, is +already an unwieldy unit for good government from the City Hall. Yet +for many purposes, such as health and transportation, the metropolitan +district is the true unit of administration. In that district, +however, there are large cities, like Yonkers, Jersey City, Paterson, +Elizabeth, Hoboken, Bayonne. They could not all be managed from one +center, and yet they should act together for many functions. +Ultimately perhaps some such flexible scheme of local government as +Sidney and Beatrice Webb have suggested may be the proper +solution. [Footnote: "The Reorganization of Local Government" (Ch. IV), +in _A Constitution for the Socialist Commonwealth of Great +Britain_.] But the first step would be a coordination, not of +decision and action, but of information and research. Let the +officials of the various municipalities see their common problems in +the light of the same facts. + +8 + +It would be idle to deny that such a net work of intelligence bureaus +in politics and industry might become a dead weight and a perpetual +irritation. One can easily imagine its attraction for men in search of +soft jobs, for pedants, for meddlers. One can see red tape, mountains +of papers, questionnaires ad nauseam, seven copies of every document, +endorsements, delays, lost papers, the use of form 136 instead of form +2gb, the return of the document because pencil was used instead of +ink, or black ink instead of red ink. The work could be done very +badly. There are no fool-proof institutions. + +But if one could assume that there was circulation through the whole +system between government departments, factories, offices, and the +universities; a circulation of men, a circulation of data and of +criticism, the risks of dry rot would not be so great. Nor would it be +true to say that these intelligence bureaus will complicate life. They +will tend, on the contrary, to simplify, by revealing a complexity now +so great as to be humanly unmanageable. The present fundamentally +invisible system of government is so intricate that most people have +given up trying to follow it, and because they do not try, they are +tempted to think it comparatively simple. It is, on the contrary, +elusive, concealed, opaque. The employment of an intelligence system +would mean a reduction of personnel per unit of result, because by +making available to all the experience of each, it would reduce the +amount of trial and error; and because by making the social process +visible, it would assist the personnel to self-criticism. It does not +involve a great additional band of officials, if you take into account +the time now spent vainly by special investigating committees, grand +juries, district attorneys, reform organizations, and bewildered +office holders, in trying to find their way through a dark muddle. + +If the analysis of public opinion and of the democratic theories in +relation to the modern environment is sound in principle, then I do +not see how one can escape the conclusion that such intelligence work +is the clue to betterment. I am not referring to the few suggestions +contained in this chapter. They are merely illustrations. The task of +working out the technic is in the hands of men trained to do it, and +not even they can to-day completely foresee the form, much less the +details. The number of social phenomena which are now recorded is +small, the instruments of analysis are very crude, the concepts often +vague and uncriticized. But enough has been done to demonstrate, I +think, that unseen environments can be reported effectively, that they +can be reported to divergent groups of people in a way which is +neutral to their prejudice, and capable of overcoming their +subjectivism. + +If that is true, then in working out the intelligence principle men +will find the way to overcome the central difficulty of +self-government, the difficulty of dealing with an unseen reality. +Because of that difficulty, it has been impossible for any +self-governing community to reconcile its need for isolation with the +necessity for wide contact, to reconcile the dignity and individuality +of local decision with security and wide coordination, to secure +effective leaders without sacrificing responsibility, to have useful +public opinions without attempting universal public opinions on all +subjects. As long as there was no way of establishing common versions +of unseen events, common measures for separate actions, the only image +of democracy that would work, even in theory, was one based on an +isolated community of people whose political faculties were limited, +according to Aristotle's famous maxim, by the range of their vision. + +But now there is a way out, a long one to be sure, but a way. It is +fundamentally the same way as that which has enabled a citizen of +Chicago, with no better eyes or ears than an Athenian, to see and hear +over great distances. It is possible to-day, it will become more +possible when more labor has gone into it, to reduce the discrepancies +between the conceived environment and the effective environment. As +that is done, federalism will work more and more by consent, less and +less by coercion. For while federalism is the only possible method of +union among self-governing groups, [Footnote: _Cf._ H. J. Laski, +_The Foundations of Sovereignty_, and other Essays, particularly +the Essay of this name, as well as the Problems of Administrative +Areas, The Theory of Popular Sovereignty, and The Pluralistic State.] +federalism swings either towards imperial centralization or towards +parochial anarchy wherever the union is not based on correct and +commonly accepted ideas of federal matters. These ideas do not arise +spontaneously. They have to be pieced together by generalization based +on analysis, and the instruments for that analysis have to be invented +and tested by research. + +No electoral device, no manipulation of areas, no change in the system +of property, goes to the root of the matter. You cannot take more +political wisdom out of human beings than there is in them. And no +reform, however sensational, is truly radical, which does not +consciously provide a way of overcoming the subjectivism of human +opinion based on the limitation of individual experience. There are +systems of government, of voting, and representation which extract +more than others. But in the end knowledge must come not from the +conscience but from the environment with which that conscience deals. +When men act on the principle of intelligence they go out to find the +facts and to make their wisdom. When they ignore it, they go inside +themselves and find only what is there. They elaborate their +prejudice, instead of increasing their knowledge. + + + + +CHAPTER XXVII + +THE APPEAL TO THE PUBLIC + +1 + +IN real life no one acts on the theory that he can have a public +opinion on every public question, though this fact is often concealed +where a person thinks there is no public question because he has no +public opinion. But in the theory of our politics we continue to think +more literally than Lord Bryce intended, that "the action of Opinion +is continuous," [Footnote: _Modern Democracies_, Vol. I, p. 159.] +even though "its action... deals with broad principles only." +[Footnote: Id., footnote, p. 158.] And then because we try to think of +ourselves having continuous opinions, without being altogether certain +what a broad principle is, we quite naturally greet with an anguished +yawn an argument that seems to involve the reading of more government +reports, more statistics, more curves and more graphs. For all these +are in the first instance just as confusing as partisan rhetoric, and +much less entertaining. + +The amount of attention available is far too small for any scheme in +which it was assumed that all the citizens of the nation would, after +devoting themselves to the publications of all the intelligence +bureaus, become alert, informed, and eager on the multitude of real +questions that never do fit very well into any broad principle. I am +not making that assumption. Primarily, the intelligence bureau is an +instrument of the man of action, of the representative charged with +decision, of the worker at his work, and if it does not help them, it +will help nobody in the end. But in so far as it helps them to +understand the environment in which they are working, it makes what +they do visible. And by that much they become more responsible to the +general public. + +The purpose, then, is not to burden every citizen with expert opinions +on all questions, but to push that burden away from him towards the +responsible administrator. An intelligence system has value, of +course, as a source of general information, and as a check on the +daily press. But that is secondary. Its real use is as an aid to +representative government and administration both in politics and +industry. The demand for the assistance of expert reporters in the +shape of accountants, statisticians, secretariats, and the like, comes +not from the public, but from men doing public business, who can no +longer do it by rule of thumb. It is in origin and in ideal an +instrument for doing public business better, rather than an instrument +for knowing better how badly public business is done. + +2 + +As a private citizen, as a sovereign voter, no one could attempt to +digest these documents. But as one party to a dispute, as a +committeeman in a legislature, as an officer in government, business, +or a trade union, as a member of an industrial council, reports on the +specific matter at issue will be increasingly welcome. The private +citizen interested in some cause would belong, as he does now, to +voluntary societies which employed a staff to study the documents, and +make reports that served as a check on officialdom. There would be +some study of this material by newspaper men, and a good deal by +experts and by political scientists. But the outsider, and every one +of us is an outsider to all but a few aspects of modern life, has +neither time, nor attention, nor interest, nor the equipment for +specific judgment. It is on the men inside, working under conditions +that are sound, that the daily administrations of society must rest. + +The general public outside can arrive at judgments about whether these +conditions are sound only on the result after the event, and on the +procedure before the event. The broad principles on which the action +of public opinion can be continuous are essentially principles of +procedure. The outsider can ask experts to tell him whether the +relevant facts were duly considered; he cannot in most cases decide +for himself what is relevant or what is due consideration. The +outsider can perhaps judge whether the groups interested in the +decision were properly heard, whether the ballot, if there was one, +was honestly taken, and perhaps whether the result was honestly +accepted. He can watch the procedure when the news indicates that +there is something to watch. He can raise a question as to whether the +procedure itself is right, if its normal results conflict with his +ideal of a good life. [Footnote: _Cf._ Chapter XX. ] But if he +tries in every case to substitute himself for the procedure, to bring +in Public Opinion like a providential uncle in the crisis of a play, +he will confound his own confusion. He will not follow any train of +thought consecutively. + +For the practice of appealing to the public on all sorts of intricate +matters means almost always a desire to escape criticism from those +who know by enlisting a large majority which has had no chance to +know. The verdict is made to depend on who has the loudest or the most +entrancing voice, the most skilful or the most brazen publicity man, +the best access to the most space in the newspapers. For even when the +editor is scrupulously fair to "the other side," fairness is not +enough. There may be several other sides, unmentioned by any of the +organized, financed and active partisans. + +The private citizen, beset by partisan appeals for the loan of his +Public Opinion, will soon see, perhaps, that these appeals are not a +compliment to his intelligence, but an imposition on his good nature +and an insult to his sense of evidence. As his civic education takes +account of the complexity of his environment, he will concern himself +about the equity and the sanity of procedure, and even this he will in +most cases expect his elected representative to watch for him. He will +refuse himself to accept the burden of these decisions, and will turn +down his thumbs in most cases on those who, in their hurry to win, +rush from the conference table with the first dope for the reporters. + +Only by insisting that problems shall not come up to him until they +have passed through a procedure, can the busy citizen of a modern +state hope to deal with them in a form that is intelligible. For +issues, as they are stated by a partisan, almost always consist of an +intricate series of facts, as he has observed them, surrounded by a +large fatty mass of stereotyped phrases charged with his emotion. +According to the fashion of the day, he will emerge from the +conference room insisting that what he wants is some soulfilling idea +like Justice, Welfare, Americanism, Socialism. On such issues the +citizen outside can sometimes be provoked to fear or admiration, but +to judgment never. Before he can do anything with the argument, the +fat has to be boiled out of it for him. + +3 + +That can be done by having the representative inside carry on +discussion in the presence of some one, chairman or mediator, who +forces the discussion to deal with the analyses supplied by experts. +This is the essential organization of any representative body dealing +with distant matters. The partisan voices should be there, but the +partisans should find themselves confronted with men, not personally +involved, who control enough facts and have the dialectical skill to +sort out what is real perception from what is stereotype, pattern and +elaboration. It is the Socratic dialogue, with all of Socrates's +energy for breaking through words to meanings, and something more than +that, because the dialectic in modern life must be done by men who +have explored the environment as well as the human mind. + +There is, for example, a grave dispute in the steel industry. Each +side issues a manifesto full of the highest ideals. The only public +opinion that is worth respect at this stage is the opinion which +insists that a conference be organized. For the side which says its +cause is too just to be contaminated by conference there can be little +sympathy, since there is no such cause anywhere among mortal men. +Perhaps those who object to conference do not say quite that. Perhaps +they say that the other side is too wicked; they cannot shake hands +with traitors. All that public opinion can do then is to organize a +hearing by public officials to hear the proof of wickedness. It cannot +take the partisans' word for it. But suppose a conference is agreed +to, and suppose there is a neutral chairman who has at his beck and +call the consulting experts of the corporation, the union, and, let us +say, the Department of Labor. + +Judge Gary states with perfect sincerity that his men are well paid +and not overworked, and then proceeds to sketch the history of Russia +from the time of Peter the Great to the murder of the Czar. Mr. Foster +rises, states with equal sincerity that the men are exploited, and +then proceeds to outline the history of human emancipation from Jesus +of Nazareth to Abraham Lincoln. At this point the chairman calls upon +the intelligence men for wage tables in order to substitute for the +words "well paid" and "exploited" a table showing what the different +classes _are_ paid. Does Judge Gary think they are all well paid? +He does. Does Mr. Foster think they are all exploited? No, he thinks +that groups C, M, and X are exploited. What does he mean by exploited? +He means they are not paid a living wage. They are, says Judge Gary. +What can a man buy on that wage, asks the chairman. Nothing, says Mr. +Foster. Everything he needs, says Judge Gary. The chairman consults +the budgets and price statistics of the government. [Footnote: See an +article on "The Cost of Living and Wage Cuts," in the _New +Republic_, July 27, 1921, by Dr. Leo Wolman, for a brilliant +discussion of the naive use of such figures and "pseudo-principles." +The warning is of particular importance because it comes from an +economist and statistician who has himself done so much to improve the +technic of industrial disputes.] He rules that X can meet an average +budget, but that C and M cannot. Judge Gary serves notice that he does +not regard the official statistics as sound. The budgets are too high, +and prices have come down. Mr. Foster also serves notice of exception. +The budget is too low, prices have gone up. The chairman rules that +this point is not within the jurisdiction of the conference, that the +official figures stand, and that Judge Gary's experts and Mr. Foster's +should carry their appeals to the standing committee of the federated +intelligence bureaus. + +Nevertheless, says Judge Gary, we shall be ruined if we change these +wage scales. What do you mean by ruined, asks the chairman, produce +your books. I can't, they are private, says Judge Gary. What is +private does not interest us, says the chairman, and, therefore, +issues a statement to the public announcing that the wages of workers +in groups C and M are so-and-so much below the official minimum living +wage, and that Judge Gary declines to increase them for reasons that +he refuses to state. After a procedure of that sort, a public opinion +in the eulogistic sense of the term [Footnote: As used by Mr. Lowell +in his _Public Opinion and Popular Government_.] can exist. + +The value of expert mediation is not that it sets up opinion to coerce +the partisans, but that it disintegrates partisanship. Judge Gary and +Mr. Foster may remain as little convinced as when they started, though +even they would have to talk in a different strain. But almost +everyone else who was not personally entangled would save himself from +being entangled. For the entangling stereotypes and slogans to which +his reflexes are so ready to respond are by this kind of dialectic +untangled. + +4 + +On many subjects of great public importance, and in varying degree +among different people for more personal matters, the threads of +memory and emotion are in a snarl. The same word will connote any +number of different ideas: emotions are displaced from the images to +which they belong to names which resemble the names of these images. +In the uncriticized parts of the mind there is a vast amount of +association by mere clang, contact, and succession. There are stray +emotional attachments, there are words that were names and are masks. +In dreams, reveries, and panic, we uncover some of the disorder, +enough to see how the naive mind is composed, and how it behaves when +not disciplined by wakeful effort and external resistance. We see that +there is no more natural order than in a dusty old attic. There is +often the same incongruity between fact, idea, and emotion as there +might be in an opera house, if all the wardrobes were dumped in a heap +and all the scores mixed up, so that Madame Butterfly in a Valkyr's +dress waited lyrically for the return of Faust. "At Christmas-tide" +says an editorial, "old memories soften the heart. Holy teachings are +remembered afresh as thoughts run back to childhood. The world does +not seem so bad when seen through the mist of half-happy, half-sad +recollections of loved ones now with God. No heart is untouched by the +mysterious influence.... The country is honeycombed with red +propaganda--but there is a good supply of ropes, muscles and +lampposts... while this world moves the spirit of liberty will burn in +the breast of man." + +The man who found these phrases in his mind needs help. He needs a +Socrates who will separate the words, cross-examine him until he has +defined them, and made words the names of ideas. Made them mean a +particular object and nothing else. For these tense syllables have got +themselves connected in his mind by primitive association, and are +bundled together by his memories of Christmas, his indignation as a +conservative, and his thrills as the heir to a revolutionary +tradition. Sometimes the snarl is too huge and ancient for quick +unravelling. Sometimes, as in modern psychotherapy, there are layers +upon layers of memory reaching back to infancy, which have to be +separated and named. + +The effect of naming, the effect, that is, of saying that the labor +groups C and M, but not X, are underpaid, instead of saying that Labor +is Exploited, is incisive. Perceptions recover their identity, and the +emotion they arouse is specific, since it is no longer reinforced by +large and accidental connections with everything from Christmas to +Moscow. The disentangled idea with a name of its own, and an emotion +that has been scrutinized, is ever so much more open to correction by +new data in the problem. It had been imbedded in the whole +personality, had affiliations of some sort with the whole ego: a +challenge would reverberate through the whole soul. After it has been +thoroughly criticized, the idea is no longer _me_ but _that_. +It is objectified, it is at arm's length. Its fate is not bound up with my +fate, but with the fate of the outer world upon which I am acting. + +5 + +Re-education of this kind will help to bring our public opinions into +grip with the environment. That is the way the enormous censoring, +stereotyping, and dramatizing apparatus can be liquidated. Where there +is no difficulty in knowing what the relevant environment is, the +critic, the teacher, the physician, can unravel the mind. But where +the environment is as obscure to the analyst as to his pupil, no +analytic technic is sufficient. Intelligence work is required. In +political and industrial problems the critic as such can do something, +but unless he can count upon receiving from expert reporters a valid +picture of the environment, his dialectic cannot go far. + +Therefore, though here, as in most other matters, "education" is the +supreme remedy, the value of this education will depend upon the +evolution of knowledge. And our knowledge of human institutions is +still extraordinarily meager and impressionistic. The gathering of +social knowledge is, on the whole, still haphazard; not, as it will +have to become, the normal accompaniment of action. And yet the +collection of information will not be made, one may be sure, for the +sake of its ultimate use. It will be made because modern decision +requires it to be made. But as it is being made, there will accumulate +a body of data which political science can turn into generalization, +and build up for the schools into a conceptual picture of the world. +When that picture takes form, civic education can become a preparation +for dealing with an unseen environment. + +As a working model of the social system becomes available to the +teacher, he can use it to make the pupil acutely aware of how his mind +works on unfamiliar facts. Until he has such a model, the teacher +cannot hope to prepare men fully for the world they will find. What he +can do is to prepare them to deal with that world with a great deal +more sophistication about their own minds. He can, by the use of the +case method, teach the pupil the habit of examining the sources of his +information. He can teach him, for example, to look in his newspaper +for the place where the dispatch was filed, for the name of the +correspondent, the name of the press service, the authority given for +the statement, the circumstances under which the statement was +secured. He can teach the pupil to ask himself whether the reporter +saw what he describes, and to remember how that reporter described +other events in the past. He can teach him the character of +censorship, of the idea of privacy, and furnish him with knowledge of +past propaganda. He can, by the proper use of history, make him aware +of the stereotype, and can educate a habit of introspection about the +imagery evoked by printed words. He can, by courses in comparative +history and anthropology, produce a life-long realization of the way +codes impose a special pattern upon the imagination. He can teach men +to catch themselves making allegories, dramatizing relations, and +personifying abstractions. He can show the pupil how he identifies +himself with these allegories, how he becomes interested, and how he +selects the attitude, heroic, romantic, economic which he adopts while +holding a particular opinion. The study of error is not only in the +highest degree prophylactic, but it serves as a stimulating +introduction to the study of truth. As our minds become more deeply +aware of their own subjectivism, we find a zest in objective method +that is not otherwise there. We see vividly, as normally we should +not, the enormous mischief and casual cruelty of our prejudices. And +the destruction of a prejudice, though painful at first, because of +its connection with our self-respect, gives an immense relief and a +fine pride when it is successfully done. There is a radical +enlargement of the range of attention. As the current categories +dissolve, a hard, simple version of the world breaks up. The scene +turns vivid and full. There follows an emotional incentive to hearty +appreciation of scientific method, which otherwise it is not easy to +arouse, and is impossible to sustain. Prejudices are so much easier +and more interesting. For if you teach the principles of science as if +they had always been accepted, their chief virtue as a discipline, +which is objectivity, will make them dull. But teach them at first as +victories over the superstitions of the mind, and the exhilaration of +the chase and of the conquest may carry the pupil over that hard +transition from his own self-bound experience to the phase where his +curiosity has matured, and his reason has acquired passion. + + + + +CHAPTER XXVIII + +THE APPEAL TO REASON + +1 + +I HAVE written, and then thrown away, several endings to this book. +Over all of them there hung that fatality of last chapters, in which +every idea seems to find its place, and all the mysteries, that the +writer has not forgotten, are unravelled. In politics the hero does +not live happily ever after, or end his life perfectly. There is no +concluding chapter, because the hero in politics has more future +before him than there is recorded history behind him. The last chapter +is merely a place where the writer imagines that the polite reader has +begun to look furtively at his watch. + +2 + +When Plato came to the point where it was fitting that he should sum +up, his assurance turned into stage-fright as he thought how absurd it +would sound to say what was in him about the place of reason in +politics. Those sentences in book five of the Republic were hard even +for Plato to speak; they are so sheer and so stark that men can +neither forget them nor live by them. So he makes Socrates say to +Glaucon that he will be broken and drowned in laughter for telling +"what is the least change which will enable a state to pass into the +truer form," [Footnote: _Republic_, Bk. V, 473. Jowett transl.] +because the thought he "would fain have uttered if it had not seemed +too extravagant" was that "until philosophers are kings, or the kings +and princes of this world have the spirit and power of philosophy, and +political greatness and wisdom meet in one... cities will never cease +from ill,--no, nor the human race..." + +Hardly had he said these awful words, when he realized they were a +counsel of perfection, and felt embarrassed at the unapproachable +grandeur of his idea. So he hastens to add that, of course, "the true +pilot" will be called "a prater, a star-gazer, a good-for-nothing." +[Footnote: 2 Bk. VI, 488-489.] But this wistful admission, though it +protects him against whatever was the Greek equivalent for the charge +that he lacked a sense of humor, furnished a humiliating tailpiece to +a solemn thought. He becomes defiant and warns Adeimantus that he must +"attribute the uselessness" of philosophers "to the fault of those who +will not use them, and not to themselves. The pilot should not humbly +beg the sailors to be commanded by him--that is not the order of +nature." And with this haughty gesture, he hurriedly picked up the +tools of reason, and disappeared into the Academy, leaving the world +to Machiavelli. + +Thus, in the first great encounter between reason and politics, the +strategy of reason was to retire in anger. But meanwhile, as Plato +tells us, the ship is at sea. There have been many ships on the sea, +since Plato wrote, and to-day, whether we are wise or foolish in our +belief, we could no longer call a man a true pilot, simply because he +knows how to "pay attention to the year and seasons and sky and stars +and winds, and whatever else belongs to his art." [Footnote: Bk. VI, +488-489.] He can dismiss nothing which is necessary to make that ship +sail prosperously. Because there are mutineers aboard, he cannot say: +so much the worse for us all... it is not in the order of nature that +I should handle a mutiny... it is not in the order of philosophy that +I should consider mutiny... I know how to navigate... I do not know +how to navigate a ship full of sailors... and if they do not see that +I am the man to steer, I cannot help it. We shall all go on the rocks, +they to be punished for their sins; I, with the assurance that I knew +better.... + +3 + +Whenever we make an appeal to reason in politics, the difficulty in +this parable recurs. For there is an inherent difficulty about using +the method of reason to deal with an unreasoning world. Even if you +assume with Plato that the true pilot knows what is best for the ship, +you have to recall that he is not so easy to recognize, and that this +uncertainty leaves a large part of the crew unconvinced. By definition +the crew does not know what he knows, and the pilot, fascinated by the +stars and winds, does not know how to make the crew realize the +importance of what he knows. There is no time during mutiny at sea to +make each sailor an expert judge of experts. There is no time for the +pilot to consult his crew and find out whether he is really as wise as +he thinks he is. For education is a matter of years, the emergency a +matter of hours. It would be altogether academic, then, to tell the +pilot that the true remedy is, for example, an education that will +endow sailors with a better sense of evidence. You can tell that only +to shipmasters on dry land. In the crisis, the only advice is to use a +gun, or make a speech, utter a stirring slogan, offer a compromise, +employ any quick means available to quell the mutiny, the sense of +evidence being what it is. It is only on shore where men plan for many +voyages, that they can afford to, and must for their own salvation, +deal with those causes that take a long time to remove. They will be +dealing in years and generations, not in emergencies alone. And +nothing will put a greater strain upon their wisdom than the necessity +of distinguishing false crises from real ones. For when there is panic +in the air, with one crisis tripping over the heels of another, actual +dangers mixed with imaginary scares, there is no chance at all for the +constructive use of reason, and any order soon seems preferable to any +disorder. + +It is only on the premise of a certain stability over a long run of +time that men can hope to follow the method of reason. This is not +because mankind is inept, or because the appeal to reason is +visionary, but because the evolution of reason on political subjects +is only in its beginnings. Our rational ideas in politics are still +large, thin generalities, much too abstract and unrefined for +practical guidance, except where the aggregates are large enough to +cancel out individual peculiarity and exhibit large uniformities. +Reason in politics is especially immature in predicting the behavior +of individual men, because in human conduct the smallest initial +variation often works out into the most elaborate differences. That, +perhaps, is why when we try to insist solely upon an appeal to reason +in dealing with sudden situations, we are broken and drowned in +laughter. + +4 + +For the rate at which reason, as we possess it, can advance itself is +slower than the rate at which action has to be taken. In the present +state of political science there is, therefore, a tendency for one +situation to change into another, before the first is clearly understood, +and so to make much political criticism hindsight and little else. Both in +the discovery of what is unknown, and in the propagation of that which +has been proved, there is a time-differential, which ought to, in a much +greater degree than it ever has, occupy the political philosopher. We +have begun, chiefly under the inspiration of Mr. Graham Wallas, to +examine the effect of an invisible environment upon our opinions. +We do not, as yet, understand, except a little by rule of thumb, the +element of time in politics, though it bears most directly upon the +practicability of any constructive proposal. [Footnote: _Cf_. H. G. +Wells in the opening chapters of _Mankind in the Making._] We +can see, for example, that somehow the relevancy of any plan depends +upon the length of time the operation requires. Because on the length +of time it will depend whether the data which the plan assumes as +given, will in truth remain the same. [Footnote: The better the +current analysis in the intelligence work of any institution, the less +likely, of course, that men will deal with tomorrow's problems in the +light of yesterday's facts.] There is a factor here which realistic +and experienced men do take into account, and it helps to mark +them off somehow from the opportunist, the visionary, the philistine +and the pedant. [Footnote: Not all, but some of the differences +between reactionaries, conservatives, liberals, and radicals are +due, I think, to a different intuitive estimate of the rate of change +in social affairs.] But just how the calculation of time enters into +politics we do not know at present in any systematic way. + +Until we understand these matters more clearly, we can at least +remember that there is a problem of the utmost theoretical difficulty +and practical consequence. It will help us to cherish Plato's ideal, +without sharing his hasty conclusion about the perversity of those who +do not listen to reason. It is hard to obey reason in politics, +because you are trying to make two processes march together, which +have as yet a different gait and a different pace. Until reason is +subtle and particular, the immediate struggle of politics will +continue to require an amount of native wit, force, and unprovable +faith, that reason can neither provide nor control, because the facts +of life are too undifferentiated for its powers of understanding. The +methods of social science are so little perfected that in many of the +serious decisions and most of the casual ones, there is as yet no +choice but to gamble with fate as intuition prompts. + +But we can make a belief in reason one of those intuitions. We can use +our wit and our force to make footholds for reason. Behind our +pictures of the world, we can try to see the vista of a longer +duration of events, and wherever it is possible to escape from the +urgent present, allow this longer time to control our decisions. And +yet, even when there is this will to let the future count, we find +again and again that we do not know for certain how to act according +to the dictates of reason. The number of human problems on which +reason is prepared to dictate is small. + +5 + +There is, however, a noble counterfeit in that charity which comes +from self-knowledge and an unarguable belief that no one of our +gregarious species is alone in his longing for a friendlier world. So +many of the grimaces men make at each other go with a flutter of their +pulse, that they are not all of them important. And where so much is +uncertain, where so many actions have to be carried out on guesses, +the demand upon the reserves of mere decency is enormous, and it is +necessary to live as if good will would work. We cannot prove in every +instance that it will, nor why hatred, intolerance, suspicion, +bigotry, secrecy, fear, and lying are the seven deadly sins against +public opinion. We can only insist that they have no place in the +appeal to reason, that in the longer run they are a poison; and taking +our stand upon a view of the world which outlasts our own +predicaments, and our own lives, we can cherish a hearty prejudice +against them. + +We can do this all the better if we do not allow frightfulness and +fanaticism to impress us so deeply that we throw up our hands +peevishly, and lose interest in the longer run of time because we have +lost faith in the future of man. There is no ground for this despair, +because all the _ifs_ on which, as James said, our destiny hangs, +are as pregnant as they ever were. What we have seen of brutality, we +have seen, and because it was strange, it was not conclusive. It was +only Berlin, Moscow, Versailles in 1914 to 1919, not Armageddon, as we +rhetorically said. The more realistically men have faced out the +brutality and the hysteria, the more they have earned the right to say +that it is not foolish for men to believe, because another great war +took place, that intelligence, courage and effort cannot ever contrive +a good life for all men. + +Great as was the horror, it was not universal. There were corrupt, and +there were incorruptible. There was muddle and there were miracles. +There was huge lying. There were men with the will to uncover it. It +is no judgment, but only a mood, when men deny that what some men have +been, more men, and ultimately enough men, might be. You can despair +of what has never been. You can despair of ever having three heads, +though Mr. Shaw has declined to despair even of that. But you cannot +despair of the possibilities that could exist by virtue of any human +quality which a human being has exhibited. And if amidst all the evils +of this decade, you have not seen men and women, known moments that +you would like to multiply, the Lord himself cannot help you. + + + + + + + +End of the Project Gutenberg EBook of Public Opinion, by Walter Lippmann + +*** END OF THIS PROJECT GUTENBERG EBOOK PUBLIC OPINION *** + +***** This file should be named 6456-8.txt or 6456-8.zip ***** +This and all associated files of various formats will be found in: + http://www.gutenberg.org/6/4/5/6456/ + +Produced by David Phillips, Charles Franks and the Online +Distributed Proofreading Team. + +Updated editions will replace the previous one--the old editions will +be renamed. + +Creating the works from print editions not protected by U.S. copyright +law means that no one owns a United States copyright in these works, +so the Foundation (and you!) can copy and distribute it in the United +States without permission and without paying copyright +royalties. Special rules, set forth in the General Terms of Use part +of this license, apply to copying and distributing Project +Gutenberg-tm electronic works to protect the PROJECT GUTENBERG-tm +concept and trademark. Project Gutenberg is a registered trademark, +and may not be used if you charge for the eBooks, unless you receive +specific permission. If you do not charge anything for copies of this +eBook, complying with the rules is very easy. You may use this eBook +for nearly any purpose such as creation of derivative works, reports, +performances and research. They may be modified and printed and given +away--you may do practically ANYTHING in the United States with eBooks +not protected by U.S. copyright law. Redistribution is subject to the +trademark license, especially commercial redistribution. + +START: FULL LICENSE + +THE FULL PROJECT GUTENBERG LICENSE +PLEASE READ THIS BEFORE YOU DISTRIBUTE OR USE THIS WORK + +To protect the Project Gutenberg-tm mission of promoting the free +distribution of electronic works, by using or distributing this work +(or any other work associated in any way with the phrase "Project +Gutenberg"), you agree to comply with all the terms of the Full +Project Gutenberg-tm License available with this file or online at +www.gutenberg.org/license. + +Section 1. General Terms of Use and Redistributing Project +Gutenberg-tm electronic works + +1.A. By reading or using any part of this Project Gutenberg-tm +electronic work, you indicate that you have read, understand, agree to +and accept all the terms of this license and intellectual property +(trademark/copyright) agreement. If you do not agree to abide by all +the terms of this agreement, you must cease using and return or +destroy all copies of Project Gutenberg-tm electronic works in your +possession. If you paid a fee for obtaining a copy of or access to a +Project Gutenberg-tm electronic work and you do not agree to be bound +by the terms of this agreement, you may obtain a refund from the +person or entity to whom you paid the fee as set forth in paragraph +1.E.8. + +1.B. "Project Gutenberg" is a registered trademark. It may only be +used on or associated in any way with an electronic work by people who +agree to be bound by the terms of this agreement. There are a few +things that you can do with most Project Gutenberg-tm electronic works +even without complying with the full terms of this agreement. See +paragraph 1.C below. There are a lot of things you can do with Project +Gutenberg-tm electronic works if you follow the terms of this +agreement and help preserve free future access to Project Gutenberg-tm +electronic works. See paragraph 1.E below. + +1.C. The Project Gutenberg Literary Archive Foundation ("the +Foundation" or PGLAF), owns a compilation copyright in the collection +of Project Gutenberg-tm electronic works. Nearly all the individual +works in the collection are in the public domain in the United +States. If an individual work is unprotected by copyright law in the +United States and you are located in the United States, we do not +claim a right to prevent you from copying, distributing, performing, +displaying or creating derivative works based on the work as long as +all references to Project Gutenberg are removed. Of course, we hope +that you will support the Project Gutenberg-tm mission of promoting +free access to electronic works by freely sharing Project Gutenberg-tm +works in compliance with the terms of this agreement for keeping the +Project Gutenberg-tm name associated with the work. You can easily +comply with the terms of this agreement by keeping this work in the +same format with its attached full Project Gutenberg-tm License when +you share it without charge with others. + +1.D. The copyright laws of the place where you are located also govern +what you can do with this work. Copyright laws in most countries are +in a constant state of change. If you are outside the United States, +check the laws of your country in addition to the terms of this +agreement before downloading, copying, displaying, performing, +distributing or creating derivative works based on this work or any +other Project Gutenberg-tm work. The Foundation makes no +representations concerning the copyright status of any work in any +country outside the United States. + +1.E. Unless you have removed all references to Project Gutenberg: + +1.E.1. The following sentence, with active links to, or other +immediate access to, the full Project Gutenberg-tm License must appear +prominently whenever any copy of a Project Gutenberg-tm work (any work +on which the phrase "Project Gutenberg" appears, or with which the +phrase "Project Gutenberg" is associated) is accessed, displayed, +performed, viewed, copied or distributed: + + This eBook is for the use of anyone anywhere in the United States and + most other parts of the world at no cost and with almost no + restrictions whatsoever. You may copy it, give it away or re-use it + under the terms of the Project Gutenberg License included with this + eBook or online at www.gutenberg.org. If you are not located in the + United States, you'll have to check the laws of the country where you + are located before using this ebook. + +1.E.2. If an individual Project Gutenberg-tm electronic work is +derived from texts not protected by U.S. copyright law (does not +contain a notice indicating that it is posted with permission of the +copyright holder), the work can be copied and distributed to anyone in +the United States without paying any fees or charges. If you are +redistributing or providing access to a work with the phrase "Project +Gutenberg" associated with or appearing on the work, you must comply +either with the requirements of paragraphs 1.E.1 through 1.E.7 or +obtain permission for the use of the work and the Project Gutenberg-tm +trademark as set forth in paragraphs 1.E.8 or 1.E.9. + +1.E.3. If an individual Project Gutenberg-tm electronic work is posted +with the permission of the copyright holder, your use and distribution +must comply with both paragraphs 1.E.1 through 1.E.7 and any +additional terms imposed by the copyright holder. Additional terms +will be linked to the Project Gutenberg-tm License for all works +posted with the permission of the copyright holder found at the +beginning of this work. + +1.E.4. Do not unlink or detach or remove the full Project Gutenberg-tm +License terms from this work, or any files containing a part of this +work or any other work associated with Project Gutenberg-tm. + +1.E.5. Do not copy, display, perform, distribute or redistribute this +electronic work, or any part of this electronic work, without +prominently displaying the sentence set forth in paragraph 1.E.1 with +active links or immediate access to the full terms of the Project +Gutenberg-tm License. + +1.E.6. You may convert to and distribute this work in any binary, +compressed, marked up, nonproprietary or proprietary form, including +any word processing or hypertext form. However, if you provide access +to or distribute copies of a Project Gutenberg-tm work in a format +other than "Plain Vanilla ASCII" or other format used in the official +version posted on the official Project Gutenberg-tm web site +(www.gutenberg.org), you must, at no additional cost, fee or expense +to the user, provide a copy, a means of exporting a copy, or a means +of obtaining a copy upon request, of the work in its original "Plain +Vanilla ASCII" or other form. Any alternate format must include the +full Project Gutenberg-tm License as specified in paragraph 1.E.1. + +1.E.7. Do not charge a fee for access to, viewing, displaying, +performing, copying or distributing any Project Gutenberg-tm works +unless you comply with paragraph 1.E.8 or 1.E.9. + +1.E.8. You may charge a reasonable fee for copies of or providing +access to or distributing Project Gutenberg-tm electronic works +provided that + +* You pay a royalty fee of 20% of the gross profits you derive from + the use of Project Gutenberg-tm works calculated using the method + you already use to calculate your applicable taxes. The fee is owed + to the owner of the Project Gutenberg-tm trademark, but he has + agreed to donate royalties under this paragraph to the Project + Gutenberg Literary Archive Foundation. Royalty payments must be paid + within 60 days following each date on which you prepare (or are + legally required to prepare) your periodic tax returns. Royalty + payments should be clearly marked as such and sent to the Project + Gutenberg Literary Archive Foundation at the address specified in + Section 4, "Information about donations to the Project Gutenberg + Literary Archive Foundation." + +* You provide a full refund of any money paid by a user who notifies + you in writing (or by e-mail) within 30 days of receipt that s/he + does not agree to the terms of the full Project Gutenberg-tm + License. You must require such a user to return or destroy all + copies of the works possessed in a physical medium and discontinue + all use of and all access to other copies of Project Gutenberg-tm + works. + +* You provide, in accordance with paragraph 1.F.3, a full refund of + any money paid for a work or a replacement copy, if a defect in the + electronic work is discovered and reported to you within 90 days of + receipt of the work. + +* You comply with all other terms of this agreement for free + distribution of Project Gutenberg-tm works. + +1.E.9. If you wish to charge a fee or distribute a Project +Gutenberg-tm electronic work or group of works on different terms than +are set forth in this agreement, you must obtain permission in writing +from both the Project Gutenberg Literary Archive Foundation and The +Project Gutenberg Trademark LLC, the owner of the Project Gutenberg-tm +trademark. Contact the Foundation as set forth in Section 3 below. + +1.F. + +1.F.1. Project Gutenberg volunteers and employees expend considerable +effort to identify, do copyright research on, transcribe and proofread +works not protected by U.S. copyright law in creating the Project +Gutenberg-tm collection. Despite these efforts, Project Gutenberg-tm +electronic works, and the medium on which they may be stored, may +contain "Defects," such as, but not limited to, incomplete, inaccurate +or corrupt data, transcription errors, a copyright or other +intellectual property infringement, a defective or damaged disk or +other medium, a computer virus, or computer codes that damage or +cannot be read by your equipment. + +1.F.2. LIMITED WARRANTY, DISCLAIMER OF DAMAGES - Except for the "Right +of Replacement or Refund" described in paragraph 1.F.3, the Project +Gutenberg Literary Archive Foundation, the owner of the Project +Gutenberg-tm trademark, and any other party distributing a Project +Gutenberg-tm electronic work under this agreement, disclaim all +liability to you for damages, costs and expenses, including legal +fees. YOU AGREE THAT YOU HAVE NO REMEDIES FOR NEGLIGENCE, STRICT +LIABILITY, BREACH OF WARRANTY OR BREACH OF CONTRACT EXCEPT THOSE +PROVIDED IN PARAGRAPH 1.F.3. YOU AGREE THAT THE FOUNDATION, THE +TRADEMARK OWNER, AND ANY DISTRIBUTOR UNDER THIS AGREEMENT WILL NOT BE +LIABLE TO YOU FOR ACTUAL, DIRECT, INDIRECT, CONSEQUENTIAL, PUNITIVE OR +INCIDENTAL DAMAGES EVEN IF YOU GIVE NOTICE OF THE POSSIBILITY OF SUCH +DAMAGE. + +1.F.3. LIMITED RIGHT OF REPLACEMENT OR REFUND - If you discover a +defect in this electronic work within 90 days of receiving it, you can +receive a refund of the money (if any) you paid for it by sending a +written explanation to the person you received the work from. If you +received the work on a physical medium, you must return the medium +with your written explanation. The person or entity that provided you +with the defective work may elect to provide a replacement copy in +lieu of a refund. If you received the work electronically, the person +or entity providing it to you may choose to give you a second +opportunity to receive the work electronically in lieu of a refund. If +the second copy is also defective, you may demand a refund in writing +without further opportunities to fix the problem. + +1.F.4. Except for the limited right of replacement or refund set forth +in paragraph 1.F.3, this work is provided to you 'AS-IS', WITH NO +OTHER WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PURPOSE. + +1.F.5. Some states do not allow disclaimers of certain implied +warranties or the exclusion or limitation of certain types of +damages. If any disclaimer or limitation set forth in this agreement +violates the law of the state applicable to this agreement, the +agreement shall be interpreted to make the maximum disclaimer or +limitation permitted by the applicable state law. The invalidity or +unenforceability of any provision of this agreement shall not void the +remaining provisions. + +1.F.6. INDEMNITY - You agree to indemnify and hold the Foundation, the +trademark owner, any agent or employee of the Foundation, anyone +providing copies of Project Gutenberg-tm electronic works in +accordance with this agreement, and any volunteers associated with the +production, promotion and distribution of Project Gutenberg-tm +electronic works, harmless from all liability, costs and expenses, +including legal fees, that arise directly or indirectly from any of +the following which you do or cause to occur: (a) distribution of this +or any Project Gutenberg-tm work, (b) alteration, modification, or +additions or deletions to any Project Gutenberg-tm work, and (c) any +Defect you cause. + +Section 2. Information about the Mission of Project Gutenberg-tm + +Project Gutenberg-tm is synonymous with the free distribution of +electronic works in formats readable by the widest variety of +computers including obsolete, old, middle-aged and new computers. It +exists because of the efforts of hundreds of volunteers and donations +from people in all walks of life. + +Volunteers and financial support to provide volunteers with the +assistance they need are critical to reaching Project Gutenberg-tm's +goals and ensuring that the Project Gutenberg-tm collection will +remain freely available for generations to come. In 2001, the Project +Gutenberg Literary Archive Foundation was created to provide a secure +and permanent future for Project Gutenberg-tm and future +generations. To learn more about the Project Gutenberg Literary +Archive Foundation and how your efforts and donations can help, see +Sections 3 and 4 and the Foundation information page at +www.gutenberg.org Section 3. Information about the Project Gutenberg +Literary Archive Foundation + +The Project Gutenberg Literary Archive Foundation is a non profit +501(c)(3) educational corporation organized under the laws of the +state of Mississippi and granted tax exempt status by the Internal +Revenue Service. The Foundation's EIN or federal tax identification +number is 64-6221541. Contributions to the Project Gutenberg Literary +Archive Foundation are tax deductible to the full extent permitted by +U.S. federal laws and your state's laws. + +The Foundation's principal office is in Fairbanks, Alaska, with the +mailing address: PO Box 750175, Fairbanks, AK 99775, but its +volunteers and employees are scattered throughout numerous +locations. Its business office is located at 809 North 1500 West, Salt +Lake City, UT 84116, (801) 596-1887. Email contact links and up to +date contact information can be found at the Foundation's web site and +official page at www.gutenberg.org/contact + +For additional contact information: + + Dr. Gregory B. Newby + Chief Executive and Director + gbnewby@pglaf.org + +Section 4. Information about Donations to the Project Gutenberg +Literary Archive Foundation + +Project Gutenberg-tm depends upon and cannot survive without wide +spread public support and donations to carry out its mission of +increasing the number of public domain and licensed works that can be +freely distributed in machine readable form accessible by the widest +array of equipment including outdated equipment. Many small donations +($1 to $5,000) are particularly important to maintaining tax exempt +status with the IRS. + +The Foundation is committed to complying with the laws regulating +charities and charitable donations in all 50 states of the United +States. Compliance requirements are not uniform and it takes a +considerable effort, much paperwork and many fees to meet and keep up +with these requirements. We do not solicit donations in locations +where we have not received written confirmation of compliance. To SEND +DONATIONS or determine the status of compliance for any particular +state visit www.gutenberg.org/donate + +While we cannot and do not solicit contributions from states where we +have not met the solicitation requirements, we know of no prohibition +against accepting unsolicited donations from donors in such states who +approach us with offers to donate. + +International donations are gratefully accepted, but we cannot make +any statements concerning tax treatment of donations received from +outside the United States. U.S. laws alone swamp our small staff. + +Please check the Project Gutenberg Web pages for current donation +methods and addresses. Donations are accepted in a number of other +ways including checks, online payments and credit card donations. To +donate, please visit: www.gutenberg.org/donate + +Section 5. General Information About Project Gutenberg-tm electronic works. + +Professor Michael S. Hart was the originator of the Project +Gutenberg-tm concept of a library of electronic works that could be +freely shared with anyone. For forty years, he produced and +distributed Project Gutenberg-tm eBooks with only a loose network of +volunteer support. + +Project Gutenberg-tm eBooks are often created from several printed +editions, all of which are confirmed as not protected by copyright in +the U.S. unless a copyright notice is included. Thus, we do not +necessarily keep eBooks in compliance with any particular paper +edition. + +Most people start at our Web site which has the main PG search +facility: www.gutenberg.org + +This Web site includes information about Project Gutenberg-tm, +including how to make donations to the Project Gutenberg Literary +Archive Foundation, how to help produce our new eBooks, and how to +subscribe to our email newsletter to hear about new eBooks. + + +Updated editions will replace the previous one--the old editions +will be renamed. + +Creating the works from public domain print editions means that no +one owns a United States copyright in these works, so the Foundation +(and you!) can copy and distribute it in the United States without +permission and without paying copyright royalties. Special rules, +set forth in the General Terms of Use part of this license, apply to +copying and distributing Project Gutenberg-tm electronic works to +protect the PROJECT GUTENBERG-tm concept and trademark. Project +Gutenberg is a registered trademark, and may not be used if you +charge for the eBooks, unless you receive specific permission. If you +do not charge anything for copies of this eBook, complying with the +rules is very easy. You may use this eBook for nearly any purpose +such as creation of derivative works, reports, performances and +research. They may be modified and printed and given away--you may do +practically ANYTHING with public domain eBooks. Redistribution is +subject to the trademark license, especially commercial +redistribution. + + + +*** START: FULL LICENSE *** + +THE FULL PROJECT GUTENBERG LICENSE +PLEASE READ THIS BEFORE YOU DISTRIBUTE OR USE THIS WORK + +To protect the Project Gutenberg-tm mission of promoting the free +distribution of electronic works, by using or distributing this work +(or any other work associated in any way with the phrase "Project +Gutenberg"), you agree to comply with all the terms of the Full Project +Gutenberg-tm License (available with this file or online at +http://gutenberg.org/license). + + +Section 1. General Terms of Use and Redistributing Project Gutenberg-tm +electronic works + +1.A. By reading or using any part of this Project Gutenberg-tm +electronic work, you indicate that you have read, understand, agree to +and accept all the terms of this license and intellectual property +(trademark/copyright) agreement. If you do not agree to abide by all +the terms of this agreement, you must cease using and return or destroy +all copies of Project Gutenberg-tm electronic works in your possession. +If you paid a fee for obtaining a copy of or access to a Project +Gutenberg-tm electronic work and you do not agree to be bound by the +terms of this agreement, you may obtain a refund from the person or +entity to whom you paid the fee as set forth in paragraph 1.E.8. + +1.B. "Project Gutenberg" is a registered trademark. It may only be +used on or associated in any way with an electronic work by people who +agree to be bound by the terms of this agreement. There are a few +things that you can do with most Project Gutenberg-tm electronic works +even without complying with the full terms of this agreement. See +paragraph 1.C below. There are a lot of things you can do with Project +Gutenberg-tm electronic works if you follow the terms of this agreement +and help preserve free future access to Project Gutenberg-tm electronic +works. See paragraph 1.E below. + +1.C. The Project Gutenberg Literary Archive Foundation ("the Foundation" +or PGLAF), owns a compilation copyright in the collection of Project +Gutenberg-tm electronic works. Nearly all the individual works in the +collection are in the public domain in the United States. If an +individual work is in the public domain in the United States and you are +located in the United States, we do not claim a right to prevent you from +copying, distributing, performing, displaying or creating derivative +works based on the work as long as all references to Project Gutenberg +are removed. Of course, we hope that you will support the Project +Gutenberg-tm mission of promoting free access to electronic works by +freely sharing Project Gutenberg-tm works in compliance with the terms of +this agreement for keeping the Project Gutenberg-tm name associated with +the work. You can easily comply with the terms of this agreement by +keeping this work in the same format with its attached full Project +Gutenberg-tm License when you share it without charge with others. + +1.D. The copyright laws of the place where you are located also govern +what you can do with this work. Copyright laws in most countries are in +a constant state of change. If you are outside the United States, check +the laws of your country in addition to the terms of this agreement +before downloading, copying, displaying, performing, distributing or +creating derivative works based on this work or any other Project +Gutenberg-tm work. The Foundation makes no representations concerning +the copyright status of any work in any country outside the United +States. + +1.E. Unless you have removed all references to Project Gutenberg: + +1.E.1. The following sentence, with active links to, or other immediate +access to, the full Project Gutenberg-tm License must appear prominently +whenever any copy of a Project Gutenberg-tm work (any work on which the +phrase "Project Gutenberg" appears, or with which the phrase "Project +Gutenberg" is associated) is accessed, displayed, performed, viewed, +copied or distributed: + +This eBook is for the use of anyone anywhere at no cost and with +almost no restrictions whatsoever. You may copy it, give it away or +re-use it under the terms of the Project Gutenberg License included +with this eBook or online at www.gutenberg.org/license + +1.E.2. If an individual Project Gutenberg-tm electronic work is derived +from the public domain (does not contain a notice indicating that it is +posted with permission of the copyright holder), the work can be copied +and distributed to anyone in the United States without paying any fees +or charges. If you are redistributing or providing access to a work +with the phrase "Project Gutenberg" associated with or appearing on the +work, you must comply either with the requirements of paragraphs 1.E.1 +through 1.E.7 or obtain permission for the use of the work and the +Project Gutenberg-tm trademark as set forth in paragraphs 1.E.8 or +1.E.9. + +1.E.3. If an individual Project Gutenberg-tm electronic work is posted +with the permission of the copyright holder, your use and distribution +must comply with both paragraphs 1.E.1 through 1.E.7 and any additional +terms imposed by the copyright holder. Additional terms will be linked +to the Project Gutenberg-tm License for all works posted with the +permission of the copyright holder found at the beginning of this work. + +1.E.4. Do not unlink or detach or remove the full Project Gutenberg-tm +License terms from this work, or any files containing a part of this +work or any other work associated with Project Gutenberg-tm. + +1.E.5. Do not copy, display, perform, distribute or redistribute this +electronic work, or any part of this electronic work, without +prominently displaying the sentence set forth in paragraph 1.E.1 with +active links or immediate access to the full terms of the Project +Gutenberg-tm License. + +1.E.6. You may convert to and distribute this work in any binary, +compressed, marked up, nonproprietary or proprietary form, including any +word processing or hypertext form. However, if you provide access to or +distribute copies of a Project Gutenberg-tm work in a format other than +"Plain Vanilla ASCII" or other format used in the official version +posted on the official Project Gutenberg-tm web site (www.gutenberg.org), +you must, at no additional cost, fee or expense to the user, provide a +copy, a means of exporting a copy, or a means of obtaining a copy upon +request, of the work in its original "Plain Vanilla ASCII" or other +form. Any alternate format must include the full Project Gutenberg-tm +License as specified in paragraph 1.E.1. + +1.E.7. Do not charge a fee for access to, viewing, displaying, +performing, copying or distributing any Project Gutenberg-tm works +unless you comply with paragraph 1.E.8 or 1.E.9. + +1.E.8. You may charge a reasonable fee for copies of or providing +access to or distributing Project Gutenberg-tm electronic works provided +that + +- You pay a royalty fee of 20% of the gross profits you derive from + the use of Project Gutenberg-tm works calculated using the method + you already use to calculate your applicable taxes. The fee is + owed to the owner of the Project Gutenberg-tm trademark, but he + has agreed to donate royalties under this paragraph to the + Project Gutenberg Literary Archive Foundation. Royalty payments + must be paid within 60 days following each date on which you + prepare (or are legally required to prepare) your periodic tax + returns. Royalty payments should be clearly marked as such and + sent to the Project Gutenberg Literary Archive Foundation at the + address specified in Section 4, "Information about donations to + the Project Gutenberg Literary Archive Foundation." + +- You provide a full refund of any money paid by a user who notifies + you in writing (or by e-mail) within 30 days of receipt that s/he + does not agree to the terms of the full Project Gutenberg-tm + License. You must require such a user to return or + destroy all copies of the works possessed in a physical medium + and discontinue all use of and all access to other copies of + Project Gutenberg-tm works. + +- You provide, in accordance with paragraph 1.F.3, a full refund of any + money paid for a work or a replacement copy, if a defect in the + electronic work is discovered and reported to you within 90 days + of receipt of the work. + +- You comply with all other terms of this agreement for free + distribution of Project Gutenberg-tm works. + +1.E.9. If you wish to charge a fee or distribute a Project Gutenberg-tm +electronic work or group of works on different terms than are set +forth in this agreement, you must obtain permission in writing from +both the Project Gutenberg Literary Archive Foundation and Michael +Hart, the owner of the Project Gutenberg-tm trademark. Contact the +Foundation as set forth in Section 3 below. + +1.F. + +1.F.1. Project Gutenberg volunteers and employees expend considerable +effort to identify, do copyright research on, transcribe and proofread +public domain works in creating the Project Gutenberg-tm +collection. Despite these efforts, Project Gutenberg-tm electronic +works, and the medium on which they may be stored, may contain +"Defects," such as, but not limited to, incomplete, inaccurate or +corrupt data, transcription errors, a copyright or other intellectual +property infringement, a defective or damaged disk or other medium, a +computer virus, or computer codes that damage or cannot be read by +your equipment. + +1.F.2. LIMITED WARRANTY, DISCLAIMER OF DAMAGES - Except for the "Right +of Replacement or Refund" described in paragraph 1.F.3, the Project +Gutenberg Literary Archive Foundation, the owner of the Project +Gutenberg-tm trademark, and any other party distributing a Project +Gutenberg-tm electronic work under this agreement, disclaim all +liability to you for damages, costs and expenses, including legal +fees. YOU AGREE THAT YOU HAVE NO REMEDIES FOR NEGLIGENCE, STRICT +LIABILITY, BREACH OF WARRANTY OR BREACH OF CONTRACT EXCEPT THOSE +PROVIDED IN PARAGRAPH 1.F.3. YOU AGREE THAT THE FOUNDATION, THE +TRADEMARK OWNER, AND ANY DISTRIBUTOR UNDER THIS AGREEMENT WILL NOT BE +LIABLE TO YOU FOR ACTUAL, DIRECT, INDIRECT, CONSEQUENTIAL, PUNITIVE OR +INCIDENTAL DAMAGES EVEN IF YOU GIVE NOTICE OF THE POSSIBILITY OF SUCH +DAMAGE. + +1.F.3. LIMITED RIGHT OF REPLACEMENT OR REFUND - If you discover a +defect in this electronic work within 90 days of receiving it, you can +receive a refund of the money (if any) you paid for it by sending a +written explanation to the person you received the work from. If you +received the work on a physical medium, you must return the medium with +your written explanation. The person or entity that provided you with +the defective work may elect to provide a replacement copy in lieu of a +refund. If you received the work electronically, the person or entity +providing it to you may choose to give you a second opportunity to +receive the work electronically in lieu of a refund. If the second copy +is also defective, you may demand a refund in writing without further +opportunities to fix the problem. + +1.F.4. Except for the limited right of replacement or refund set forth +in paragraph 1.F.3, this work is provided to you 'AS-IS' WITH NO OTHER +WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PURPOSE. + +1.F.5. Some states do not allow disclaimers of certain implied +warranties or the exclusion or limitation of certain types of damages. +If any disclaimer or limitation set forth in this agreement violates the +law of the state applicable to this agreement, the agreement shall be +interpreted to make the maximum disclaimer or limitation permitted by +the applicable state law. The invalidity or unenforceability of any +provision of this agreement shall not void the remaining provisions. + +1.F.6. INDEMNITY - You agree to indemnify and hold the Foundation, the +trademark owner, any agent or employee of the Foundation, anyone +providing copies of Project Gutenberg-tm electronic works in accordance +with this agreement, and any volunteers associated with the production, +promotion and distribution of Project Gutenberg-tm electronic works, +harmless from all liability, costs and expenses, including legal fees, +that arise directly or indirectly from any of the following which you do +or cause to occur: (a) distribution of this or any Project Gutenberg-tm +work, (b) alteration, modification, or additions or deletions to any +Project Gutenberg-tm work, and (c) any Defect you cause. + + +Section 2. Information about the Mission of Project Gutenberg-tm + +Project Gutenberg-tm is synonymous with the free distribution of +electronic works in formats readable by the widest variety of computers +including obsolete, old, middle-aged and new computers. It exists +because of the efforts of hundreds of volunteers and donations from +people in all walks of life. + +Volunteers and financial support to provide volunteers with the +assistance they need, are critical to reaching Project Gutenberg-tm's +goals and ensuring that the Project Gutenberg-tm collection will +remain freely available for generations to come. In 2001, the Project +Gutenberg Literary Archive Foundation was created to provide a secure +and permanent future for Project Gutenberg-tm and future generations. +To learn more about the Project Gutenberg Literary Archive Foundation +and how your efforts and donations can help, see Sections 3 and 4 +and the Foundation web page at http://www.pglaf.org. + + +Section 3. Information about the Project Gutenberg Literary Archive +Foundation + +The Project Gutenberg Literary Archive Foundation is a non profit +501(c)(3) educational corporation organized under the laws of the +state of Mississippi and granted tax exempt status by the Internal +Revenue Service. The Foundation's EIN or federal tax identification +number is 64-6221541. Its 501(c)(3) letter is posted at +http://pglaf.org/fundraising. Contributions to the Project Gutenberg +Literary Archive Foundation are tax deductible to the full extent +permitted by U.S. federal laws and your state's laws. + +The Foundation's principal office is located at 4557 Melan Dr. S. +Fairbanks, AK, 99712., but its volunteers and employees are scattered +throughout numerous locations. Its business office is located at +809 North 1500 West, Salt Lake City, UT 84116, (801) 596-1887, email +business@pglaf.org. Email contact links and up to date contact +information can be found at the Foundation's web site and official +page at http://pglaf.org + +For additional contact information: + Dr. Gregory B. Newby + Chief Executive and Director + gbnewby@pglaf.org + + +Section 4. Information about Donations to the Project Gutenberg +Literary Archive Foundation + +Project Gutenberg-tm depends upon and cannot survive without wide +spread public support and donations to carry out its mission of +increasing the number of public domain and licensed works that can be +freely distributed in machine readable form accessible by the widest +array of equipment including outdated equipment. Many small donations +($1 to $5,000) are particularly important to maintaining tax exempt +status with the IRS. + +The Foundation is committed to complying with the laws regulating +charities and charitable donations in all 50 states of the United +States. Compliance requirements are not uniform and it takes a +considerable effort, much paperwork and many fees to meet and keep up +with these requirements. We do not solicit donations in locations +where we have not received written confirmation of compliance. To +SEND DONATIONS or determine the status of compliance for any +particular state visit http://pglaf.org + +While we cannot and do not solicit contributions from states where we +have not met the solicitation requirements, we know of no prohibition +against accepting unsolicited donations from donors in such states who +approach us with offers to donate. + +International donations are gratefully accepted, but we cannot make +any statements concerning tax treatment of donations received from +outside the United States. U.S. laws alone swamp our small staff. + +Please check the Project Gutenberg Web pages for current donation +methods and addresses. Donations are accepted in a number of other +ways including checks, online payments and credit card donations. +To donate, please visit: http://pglaf.org/donate + + +Section 5. General Information About Project Gutenberg-tm electronic +works. + +Professor Michael S. Hart is the originator of the Project Gutenberg-tm +concept of a library of electronic works that could be freely shared +with anyone. For thirty years, he produced and distributed Project +Gutenberg-tm eBooks with only a loose network of volunteer support. + + +Project Gutenberg-tm eBooks are often created from several printed +editions, all of which are confirmed as Public Domain in the U.S. +unless a copyright notice is included. Thus, we do not necessarily +keep eBooks in compliance with any particular paper edition. + + +Most people start at our Web site which has the main PG search facility: + + http://www.gutenberg.org + +This Web site includes information about Project Gutenberg-tm, +including how to make donations to the Project Gutenberg Literary +Archive Foundation, how to help produce our new eBooks, and how to +subscribe to our email newsletter to hear about new eBooks. diff --git a/public/example-files/animation.guida b/public/example-files/animation.guida deleted file mode 100644 index 7d50def..0000000 --- a/public/example-files/animation.guida +++ /dev/null @@ -1,26 +0,0 @@ -module Main exposing (main) - --- Create animations that spin, wave, and zig-zag. --- This one is a little red wagon bumping along a dirt road. --- --- Learn more about the playground here: --- https://package.elm-lang.org/packages/evancz/elm-playground/latest/ - -import Playground exposing (..) - - -main = - animation view - - -view time = - [ octagon darkGray 36 - |> moveLeft 100 - |> rotate (spin 3 time) - , octagon darkGray 36 - |> moveRight 100 - |> rotate (spin 3 time) - , rectangle red 300 80 - |> moveUp (wave 50 54 2 time) - |> rotate (zigzag -2 2 8 time) - ] diff --git a/public/example-files/book.guida b/public/example-files/book.guida deleted file mode 100644 index a9b61ce..0000000 --- a/public/example-files/book.guida +++ /dev/null @@ -1,89 +0,0 @@ -module Main exposing (main) - --- Make a GET request to load a book called "Public Opinion" --- --- Read how it works: --- https://guide.elm-lang.org/effects/http.html - -import Browser -import Html exposing (Html, pre, text) -import Http - - - --- MAIN - - -main = - Browser.element - { init = init - , update = update - , subscriptions = subscriptions - , view = view - } - - - --- MODEL - - -type Model - = Failure - | Loading - | Success String - - -init : () -> ( Model, Cmd Msg ) -init _ = - ( Loading - , Http.get - { url = "https://elm-lang.org/assets/public-opinion.txt" - , expect = Http.expectString GotText - } - ) - - - --- UPDATE - - -type Msg - = GotText (Result Http.Error String) - - -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg model = - case msg of - GotText result -> - case result of - Ok fullText -> - ( Success fullText, Cmd.none ) - - Err _ -> - ( Failure, Cmd.none ) - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions model = - Sub.none - - - --- VIEW - - -view : Model -> Html Msg -view model = - case model of - Failure -> - text "I was unable to load your book." - - Loading -> - text "Loading..." - - Success fullText -> - pre [] [ text fullText ] diff --git a/public/example-files/buttons.guida b/public/example-files/buttons.guida deleted file mode 100644 index 4da4fde..0000000 --- a/public/example-files/buttons.guida +++ /dev/null @@ -1,63 +0,0 @@ -module Main exposing (main) - --- Press buttons to increment and decrement a counter. --- --- Read how it works: --- https://guide.elm-lang.org/architecture/buttons.html - -import Browser -import Html exposing (Html, button, div, text) -import Html.Events exposing (onClick) - - - --- MAIN - - -main = - Browser.sandbox { init = init, update = update, view = view } - - - --- MODEL - - -type alias Model = - Int - - -init : Model -init = - 0 - - - --- UPDATE - - -type Msg - = Increment - | Decrement - - -update : Msg -> Model -> Model -update msg model = - case msg of - Increment -> - model + 1 - - Decrement -> - model - 1 - - - --- VIEW - - -view : Model -> Html Msg -view model = - div [] - [ button [ onClick Decrement ] [ text "-" ] - , div [] [ text (String.fromInt model) ] - , button [ onClick Increment ] [ text "+" ] - ] diff --git a/public/example-files/cards.guida b/public/example-files/cards.guida deleted file mode 100644 index 513ca7f..0000000 --- a/public/example-files/cards.guida +++ /dev/null @@ -1,162 +0,0 @@ -module Main exposing (main) - --- Press a button to draw a random card. --- --- Dependencies: --- guida install elm/random - -import Browser -import Html exposing (..) -import Html.Attributes exposing (style) -import Html.Events exposing (..) -import Random - - - --- MAIN - - -main = - Browser.element - { init = init - , update = update - , subscriptions = subscriptions - , view = view - } - - - --- MODEL - - -type alias Model = - { card : Card - } - - -init : () -> ( Model, Cmd Msg ) -init _ = - ( Model Three - , Cmd.none - ) - - -type Card - = Ace - | Two - | Three - | Four - | Five - | Six - | Seven - | Eight - | Nine - | Ten - | Jack - | Queen - | King - - - --- UPDATE - - -type Msg - = Draw - | NewCard Card - - -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg model = - case msg of - Draw -> - ( model - , Random.generate NewCard cardGenerator - ) - - NewCard newCard -> - ( Model newCard - , Cmd.none - ) - - -cardGenerator : Random.Generator Card -cardGenerator = - Random.uniform Ace - [ Two - , Three - , Four - , Five - , Six - , Seven - , Eight - , Nine - , Ten - , Jack - , Queen - , King - ] - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions model = - Sub.none - - - --- VIEW - - -view : Model -> Html Msg -view model = - div [] - [ button [ onClick Draw ] [ text "Draw" ] - , div [ style "font-size" "12em" ] [ text (viewCard model.card) ] - ] - - -viewCard : Card -> String -viewCard card = - case card of - Ace -> - "🂡" - - Two -> - "🂢" - - Three -> - "🂣" - - Four -> - "🂤" - - Five -> - "🂥" - - Six -> - "🂦" - - Seven -> - "🂧" - - Eight -> - "🂨" - - Nine -> - "🂩" - - Ten -> - "🂪" - - Jack -> - "🂫" - - Queen -> - "🂭" - - King -> - "🂮" diff --git a/public/example-files/clock.guida b/public/example-files/clock.guida deleted file mode 100644 index 5969c34..0000000 --- a/public/example-files/clock.guida +++ /dev/null @@ -1,134 +0,0 @@ -module Main exposing (main) - --- Show an analog clock for your time zone. --- --- Dependencies: --- guida install elm/svg --- guida install elm/time --- --- For a simpler version, check out: --- https://guida-lang.org/examples/time - -import Browser -import Html exposing (Html) -import Svg exposing (..) -import Svg.Attributes exposing (..) -import Task -import Time - - - --- MAIN - - -main = - Browser.element - { init = init - , view = view - , update = update - , subscriptions = subscriptions - } - - - --- MODEL - - -type alias Model = - { zone : Time.Zone - , time : Time.Posix - } - - -init : () -> ( Model, Cmd Msg ) -init _ = - ( Model Time.utc (Time.millisToPosix 0) - , Cmd.batch - [ Task.perform AdjustTimeZone Time.here - , Task.perform Tick Time.now - ] - ) - - - --- UPDATE - - -type Msg - = Tick Time.Posix - | AdjustTimeZone Time.Zone - - -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg model = - case msg of - Tick newTime -> - ( { model | time = newTime } - , Cmd.none - ) - - AdjustTimeZone newZone -> - ( { model | zone = newZone } - , Cmd.none - ) - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions model = - Time.every 1000 Tick - - - --- VIEW - - -view : Model -> Html Msg -view model = - let - hour = - toFloat (Time.toHour model.zone model.time) - - minute = - toFloat (Time.toMinute model.zone model.time) - - second = - toFloat (Time.toSecond model.zone model.time) - in - svg - [ viewBox "0 0 400 400" - , width "400" - , height "400" - ] - [ circle [ cx "200", cy "200", r "120", fill "#1293D8" ] [] - , viewHand 6 60 (hour / 12) - , viewHand 6 90 (minute / 60) - , viewHand 3 90 (second / 60) - ] - - -viewHand : Int -> Float -> Float -> Svg msg -viewHand width length turns = - let - t = - 2 * pi * (turns - 0.25) - - x = - 200 + length * cos t - - y = - 200 + length * sin t - in - line - [ x1 "200" - , y1 "200" - , x2 (String.fromFloat x) - , y2 (String.fromFloat y) - , stroke "white" - , strokeWidth (String.fromInt width) - , strokeLinecap "round" - ] - [] diff --git a/public/example-files/crate.guida b/public/example-files/crate.guida deleted file mode 100644 index 20c70ac..0000000 --- a/public/example-files/crate.guida +++ /dev/null @@ -1,227 +0,0 @@ -module Main exposing (main) - --- Demonstrate how to load textures and put them on a cube. --- --- Dependencies: --- guida install elm-explorations/linear-algebra --- guida install elm-explorations/webgl - -import Browser -import Browser.Events as E -import Html exposing (Html) -import Html.Attributes exposing (height, style, width) -import Math.Matrix4 as Mat4 exposing (Mat4) -import Math.Vector2 as Vec2 exposing (Vec2, vec2) -import Math.Vector3 as Vec3 exposing (Vec3, vec3) -import Result -import Task -import WebGL -import WebGL.Texture as Texture - - - --- MAIN - - -main = - Browser.element - { init = init - , view = view - , update = \msg model -> ( update msg model, Cmd.none ) - , subscriptions = subscriptions - } - - - --- MODEL - - -type alias Model = - { angle : Float - , texture : Maybe Texture.Texture - } - - -init : () -> ( Model, Cmd Msg ) -init () = - ( { angle = 0 - , texture = Nothing - } - , Task.attempt GotTexture (Texture.load "https://elm-lang.org/images/wood-crate.jpg") - ) - - - --- UPDATE - - -type Msg - = TimeDelta Float - | GotTexture (Result Texture.Error Texture.Texture) - - -update : Msg -> Model -> Model -update msg model = - case msg of - TimeDelta dt -> - { model | angle = model.angle + dt / 5000 } - - GotTexture result -> - { model | texture = Result.toMaybe result } - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions _ = - E.onAnimationFrameDelta TimeDelta - - - --- VIEW - - -view : Model -> Html Msg -view model = - case model.texture of - Nothing -> - Html.text "Loading texture..." - - Just texture -> - WebGL.toHtml - [ width 400 - , height 400 - , style "display" "block" - ] - [ WebGL.entity vertexShader fragmentShader crateMesh (toUniforms model.angle texture) - ] - - - --- UNIFORMS - - -type alias Uniforms = - { rotation : Mat4 - , perspective : Mat4 - , camera : Mat4 - , texture : Texture.Texture - } - - -toUniforms : Float -> Texture.Texture -> Uniforms -toUniforms angle texture = - { rotation = - Mat4.mul - (Mat4.makeRotate (3 * angle) (vec3 0 1 0)) - (Mat4.makeRotate (2 * angle) (vec3 1 0 0)) - , perspective = perspective - , camera = camera - , texture = texture - } - - -perspective : Mat4 -perspective = - Mat4.makePerspective 45 1 0.01 100 - - -camera : Mat4 -camera = - Mat4.makeLookAt (vec3 0 0 5) (vec3 0 0 0) (vec3 0 1 0) - - - --- MESH - - -type alias Vertex = - { position : Vec3 - , coord : Vec2 - } - - -crateMesh : WebGL.Mesh Vertex -crateMesh = - WebGL.triangles <| - List.concatMap rotatedSquare <| - [ ( 0, 0 ) - , ( 90, 0 ) - , ( 180, 0 ) - , ( 270, 0 ) - , ( 0, 90 ) - , ( 0, 270 ) - ] - - -rotatedSquare : ( Float, Float ) -> List ( Vertex, Vertex, Vertex ) -rotatedSquare ( angleXZ, angleYZ ) = - let - transformMat = - Mat4.mul - (Mat4.makeRotate (degrees angleXZ) Vec3.j) - (Mat4.makeRotate (degrees angleYZ) Vec3.i) - - transform vertex = - { vertex | position = Mat4.transform transformMat vertex.position } - - transformTriangle ( a, b, c ) = - ( transform a, transform b, transform c ) - in - List.map transformTriangle square - - -square : List ( Vertex, Vertex, Vertex ) -square = - let - topLeft = - Vertex (vec3 -1 1 1) (vec2 0 1) - - topRight = - Vertex (vec3 1 1 1) (vec2 1 1) - - bottomLeft = - Vertex (vec3 -1 -1 1) (vec2 0 0) - - bottomRight = - Vertex (vec3 1 -1 1) (vec2 1 0) - in - [ ( topLeft, topRight, bottomLeft ) - , ( bottomLeft, topRight, bottomRight ) - ] - - - --- SHADERS - - -vertexShader : WebGL.Shader Vertex Uniforms { vcoord : Vec2 } -vertexShader = - [glsl| - attribute vec3 position; - attribute vec2 coord; - uniform mat4 perspective; - uniform mat4 camera; - uniform mat4 rotation; - varying vec2 vcoord; - - void main () { - gl_Position = perspective * camera * rotation * vec4(position, 1.0); - vcoord = coord; - } - |] - - -fragmentShader : WebGL.Shader {} Uniforms { vcoord : Vec2 } -fragmentShader = - [glsl| - precision mediump float; - uniform sampler2D texture; - varying vec2 vcoord; - - void main () { - gl_FragColor = texture2D(texture, vcoord); - } - |] diff --git a/public/example-files/cube.guida b/public/example-files/cube.guida deleted file mode 100644 index 23cc4a7..0000000 --- a/public/example-files/cube.guida +++ /dev/null @@ -1,200 +0,0 @@ -module Main exposing (main) - --- Render a spinning cube. --- --- Dependencies: --- guida install elm-explorations/linear-algebra --- guida install elm-explorations/webgl - -import Browser -import Browser.Events as E -import Html exposing (Html) -import Html.Attributes exposing (height, style, width) -import Math.Matrix4 as Mat4 exposing (Mat4) -import Math.Vector3 as Vec3 exposing (Vec3, vec3) -import WebGL - - - --- MAIN - - -main = - Browser.element - { init = init - , view = view - , update = update - , subscriptions = subscriptions - } - - - --- MODEL - - -type alias Model = - Float - - -init : () -> ( Model, Cmd Msg ) -init () = - ( 0, Cmd.none ) - - - --- UPDATE - - -type Msg - = TimeDelta Float - - -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg angle = - case msg of - TimeDelta dt -> - ( angle + dt / 5000, Cmd.none ) - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions _ = - E.onAnimationFrameDelta TimeDelta - - - --- VIEW - - -view : Model -> Html Msg -view angle = - WebGL.toHtml - [ width 400 - , height 400 - , style "display" "block" - ] - [ WebGL.entity vertexShader fragmentShader cubeMesh (uniforms angle) - ] - - -type alias Uniforms = - { rotation : Mat4 - , perspective : Mat4 - , camera : Mat4 - } - - -uniforms : Float -> Uniforms -uniforms angle = - { rotation = - Mat4.mul - (Mat4.makeRotate (3 * angle) (vec3 0 1 0)) - (Mat4.makeRotate (2 * angle) (vec3 1 0 0)) - , perspective = Mat4.makePerspective 45 1 0.01 100 - , camera = Mat4.makeLookAt (vec3 0 0 5) (vec3 0 0 0) (vec3 0 1 0) - } - - - --- MESH - - -type alias Vertex = - { color : Vec3 - , position : Vec3 - } - - -cubeMesh : WebGL.Mesh Vertex -cubeMesh = - let - rft = - vec3 1 1 1 - - lft = - vec3 -1 1 1 - - lbt = - vec3 -1 -1 1 - - rbt = - vec3 1 -1 1 - - rbb = - vec3 1 -1 -1 - - rfb = - vec3 1 1 -1 - - lfb = - vec3 -1 1 -1 - - lbb = - vec3 -1 -1 -1 - in - WebGL.triangles <| - List.concat <| - [ face (vec3 115 210 22) rft rfb rbb rbt - - -- green - , face (vec3 52 101 164) rft rfb lfb lft - - -- blue - , face (vec3 237 212 0) rft lft lbt rbt - - -- yellow - , face (vec3 204 0 0) rfb lfb lbb rbb - - -- red - , face (vec3 117 80 123) lft lfb lbb lbt - - -- purple - , face (vec3 245 121 0) rbt rbb lbb lbt - - -- orange - ] - - -face : Vec3 -> Vec3 -> Vec3 -> Vec3 -> Vec3 -> List ( Vertex, Vertex, Vertex ) -face color a b c d = - let - vertex position = - Vertex (Vec3.scale (1 / 255) color) position - in - [ ( vertex a, vertex b, vertex c ) - , ( vertex c, vertex d, vertex a ) - ] - - - --- SHADERS - - -vertexShader : WebGL.Shader Vertex Uniforms { vcolor : Vec3 } -vertexShader = - [glsl| - attribute vec3 position; - attribute vec3 color; - uniform mat4 perspective; - uniform mat4 camera; - uniform mat4 rotation; - varying vec3 vcolor; - void main () { - gl_Position = perspective * camera * rotation * vec4(position, 1.0); - vcolor = color; - } - |] - - -fragmentShader : WebGL.Shader {} Uniforms { vcolor : Vec3 } -fragmentShader = - [glsl| - precision mediump float; - varying vec3 vcolor; - void main () { - gl_FragColor = 0.8 * vec4(vcolor, 1.0); - } - |] diff --git a/public/example-files/drag-and-drop.guida b/public/example-files/drag-and-drop.guida deleted file mode 100644 index adc5118..0000000 --- a/public/example-files/drag-and-drop.guida +++ /dev/null @@ -1,140 +0,0 @@ -module Main exposing (main) - --- Image upload with a drag and drop zone. --- --- Dependencies: --- guida install elm/file --- guida install elm/json - -import Browser -import File exposing (File) -import File.Select as Select -import Html exposing (..) -import Html.Attributes exposing (..) -import Html.Events exposing (..) -import Json.Decode as D - - - --- MAIN - - -main = - Browser.element - { init = init - , view = view - , update = update - , subscriptions = subscriptions - } - - - --- MODEL - - -type alias Model = - { hover : Bool - , files : List File - } - - -init : () -> ( Model, Cmd Msg ) -init _ = - ( Model False [], Cmd.none ) - - - --- UPDATE - - -type Msg - = Pick - | DragEnter - | DragLeave - | GotFiles File (List File) - - -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg model = - case msg of - Pick -> - ( model - , Select.files [ "image/*" ] GotFiles - ) - - DragEnter -> - ( { model | hover = True } - , Cmd.none - ) - - DragLeave -> - ( { model | hover = False } - , Cmd.none - ) - - GotFiles file files -> - ( { model - | files = - file :: files - , hover = - False - } - , Cmd.none - ) - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions model = - Sub.none - - - --- VIEW - - -view : Model -> Html Msg -view model = - div - [ style "border" - (if model.hover then - "6px dashed purple" - - else - "6px dashed #ccc" - ) - , style "border-radius" "20px" - , style "width" "480px" - , style "height" "100px" - , style "margin" "100px auto" - , style "padding" "20px" - , style "display" "flex" - , style "flex-direction" "column" - , style "justify-content" "center" - , style "align-items" "center" - , hijackOn "dragenter" (D.succeed DragEnter) - , hijackOn "dragover" (D.succeed DragEnter) - , hijackOn "dragleave" (D.succeed DragLeave) - , hijackOn "drop" dropDecoder - ] - [ button [ onClick Pick ] [ text "Upload Images" ] - , span [ style "color" "#ccc" ] [ text (Debug.toString model) ] - ] - - -dropDecoder : D.Decoder Msg -dropDecoder = - D.at [ "dataTransfer", "files" ] (D.oneOrMore GotFiles File.decoder) - - -hijackOn : String -> D.Decoder msg -> Attribute msg -hijackOn event decoder = - preventDefaultOn event (D.map hijack decoder) - - -hijack : msg -> ( msg, Bool ) -hijack msg = - ( msg, True ) diff --git a/public/example-files/first-person.guida b/public/example-files/first-person.guida deleted file mode 100644 index 03a5e69..0000000 --- a/public/example-files/first-person.guida +++ /dev/null @@ -1,377 +0,0 @@ -module Main exposing (main) - --- Walk around in 3D space using the keyboard. --- --- Dependencies: --- guida install elm-explorations/linear-algebra --- guida install elm-explorations/webgl --- --- Try adding the ability to crouch or to land on top of the crate! - -import Browser -import Browser.Dom as Dom -import Browser.Events as E -import Html exposing (Html, div, p, text) -import Html.Attributes exposing (height, style, width) -import Json.Decode as D -import Math.Matrix4 as Mat4 exposing (Mat4) -import Math.Vector2 as Vec2 exposing (Vec2, vec2) -import Math.Vector3 as Vec3 exposing (Vec3, vec3) -import Task -import WebGL -import WebGL.Texture as Texture - - - --- MAIN - - -main : Program () Model Msg -main = - Browser.element - { init = init - , view = view - , update = \msg model -> ( update msg model, Cmd.none ) - , subscriptions = subscriptions - } - - - --- MODEL - - -type alias Model = - { keys : Keys - , width : Float - , height : Float - , person : Person - , texture : Maybe Texture.Texture - } - - -type alias Keys = - { up : Bool - , left : Bool - , down : Bool - , right : Bool - , space : Bool - } - - -type alias Person = - { position : Vec3 - , velocity : Vec3 - } - - -init : () -> ( Model, Cmd Msg ) -init _ = - ( { keys = noKeys - , width = 400 - , height = 400 - , person = Person (vec3 0 eyeLevel -10) (vec3 0 0 0) - , texture = Nothing - } - , Cmd.batch - [ Task.attempt GotTexture (Texture.load "https://elm-lang.org/images/wood-crate.jpg") - , Task.perform (\{ viewport } -> Resized viewport.width viewport.height) Dom.getViewport - ] - ) - - -eyeLevel : Float -eyeLevel = - 2 - - -noKeys : Keys -noKeys = - Keys False False False False False - - - --- UPDATE - - -type Msg - = GotTexture (Result Texture.Error Texture.Texture) - | KeyChanged Bool String - | TimeDelta Float - | Resized Float Float - | VisibilityChanged E.Visibility - - -update : Msg -> Model -> Model -update msg model = - case msg of - GotTexture result -> - { model | texture = Result.toMaybe result } - - KeyChanged isDown key -> - { model | keys = updateKeys isDown key model.keys } - - TimeDelta dt -> - { model | person = updatePerson dt model.keys model.person } - - Resized width height -> - { model - | width = - width - , height = - height - } - - VisibilityChanged _ -> - { model | keys = noKeys } - - -updateKeys : Bool -> String -> Keys -> Keys -updateKeys isDown key keys = - case key of - " " -> - { keys | space = isDown } - - "ArrowUp" -> - { keys | up = isDown } - - "ArrowLeft" -> - { keys | left = isDown } - - "ArrowDown" -> - { keys | down = isDown } - - "ArrowRight" -> - { keys | right = isDown } - - _ -> - keys - - -updatePerson : Float -> Keys -> Person -> Person -updatePerson dt keys person = - let - velocity = - stepVelocity dt keys person - - position = - Vec3.add person.position (Vec3.scale (dt / 500) velocity) - in - if Vec3.getY position < eyeLevel then - { position = Vec3.setY eyeLevel position - , velocity = Vec3.setY 0 velocity - } - - else - { position = position - , velocity = velocity - } - - -stepVelocity : Float -> Keys -> Person -> Vec3 -stepVelocity dt { left, right, up, down, space } person = - if Vec3.getY person.position > eyeLevel then - Vec3.setY (Vec3.getY person.velocity - dt / 250) person.velocity - - else - let - toV positive negative = - (if positive then - 1 - - else - 0 - ) - - (if negative then - 1 - - else - 0 - ) - in - vec3 (toV left right) - (if space then - 2 - - else - 0 - ) - (toV up down) - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions model = - Sub.batch - [ E.onResize (\w h -> Resized (toFloat w) (toFloat h)) - , E.onKeyUp (D.map (KeyChanged False) (D.field "key" D.string)) - , E.onKeyDown (D.map (KeyChanged True) (D.field "key" D.string)) - , E.onAnimationFrameDelta TimeDelta - , E.onVisibilityChange VisibilityChanged - ] - - - --- VIEW - - -view : Model -> Html Msg -view model = - let - entities = - case model.texture of - Nothing -> - [] - - Just texture -> - [ viewCrate model.width model.height model.person texture ] - in - div - [ style "position" "absolute" - , style "left" "0" - , style "top" "0" - , style "width" (String.fromFloat model.width ++ "px") - , style "height" (String.fromFloat model.height ++ "px") - ] - [ WebGL.toHtmlWith [ WebGL.depth 1, WebGL.clearColor 1 1 1 1 ] - [ style "display" "block" - , width (round model.width) - , height (round model.height) - ] - entities - , keyboardInstructions model.keys - ] - - -viewCrate : Float -> Float -> Person -> Texture.Texture -> WebGL.Entity -viewCrate width height person texture = - let - perspective = - Mat4.mul - (Mat4.makePerspective 45 (width / height) 0.01 100) - (Mat4.makeLookAt person.position (Vec3.add person.position Vec3.k) Vec3.j) - in - WebGL.entity vertexShader - fragmentShader - crate - { texture = texture - , perspective = perspective - } - - -keyboardInstructions : Keys -> Html msg -keyboardInstructions keys = - div - [ style "position" "absolute" - , style "font-family" "monospace" - , style "text-align" "center" - , style "left" "20px" - , style "right" "20px" - , style "top" "20px" - ] - [ p [] [ text "Walk around with a first person perspective." ] - , p [] [ text "Arrows keys to move, space bar to jump." ] - ] - - - --- MESH - - -type alias Vertex = - { position : Vec3 - , coord : Vec2 - } - - -crate : WebGL.Mesh Vertex -crate = - WebGL.triangles <| - List.concatMap rotatedSquare <| - [ ( 0, 0 ) - , ( 90, 0 ) - , ( 180, 0 ) - , ( 270, 0 ) - , ( 0, 90 ) - , ( 0, -90 ) - ] - - -rotatedSquare : ( Float, Float ) -> List ( Vertex, Vertex, Vertex ) -rotatedSquare ( angleXZ, angleYZ ) = - let - transformMat = - Mat4.mul - (Mat4.makeRotate (degrees angleXZ) Vec3.j) - (Mat4.makeRotate (degrees angleYZ) Vec3.i) - - transform vertex = - { vertex - | position = - Mat4.transform transformMat vertex.position - } - - transformTriangle ( a, b, c ) = - ( transform a, transform b, transform c ) - in - List.map transformTriangle square - - -square : List ( Vertex, Vertex, Vertex ) -square = - let - topLeft = - Vertex (vec3 -1 1 1) (vec2 0 1) - - topRight = - Vertex (vec3 1 1 1) (vec2 1 1) - - bottomLeft = - Vertex (vec3 -1 -1 1) (vec2 0 0) - - bottomRight = - Vertex (vec3 1 -1 1) (vec2 1 0) - in - [ ( topLeft, topRight, bottomLeft ) - , ( bottomLeft, topRight, bottomRight ) - ] - - - --- SHADERS - - -type alias Uniforms = - { texture : Texture.Texture - , perspective : Mat4 - } - - -vertexShader : WebGL.Shader Vertex Uniforms { vcoord : Vec2 } -vertexShader = - [glsl| - attribute vec3 position; - attribute vec2 coord; - uniform mat4 perspective; - varying vec2 vcoord; - - void main () { - gl_Position = perspective * vec4(position, 1.0); - vcoord = coord; - } - |] - - -fragmentShader : WebGL.Shader {} Uniforms { vcoord : Vec2 } -fragmentShader = - [glsl| - precision mediump float; - uniform sampler2D texture; - varying vec2 vcoord; - - void main () { - gl_FragColor = texture2D(texture, vcoord); - } - |] diff --git a/public/example-files/forms.guida b/public/example-files/forms.guida deleted file mode 100644 index b135513..0000000 --- a/public/example-files/forms.guida +++ /dev/null @@ -1,86 +0,0 @@ -module Main exposing (main) - --- Input a user name and password. Make sure the password matches. --- --- Read how it works: --- https://guide.elm-lang.org/architecture/forms.html - -import Browser -import Html exposing (..) -import Html.Attributes exposing (..) -import Html.Events exposing (onInput) - - - --- MAIN - - -main = - Browser.sandbox { init = init, update = update, view = view } - - - --- MODEL - - -type alias Model = - { name : String - , password : String - , passwordAgain : String - } - - -init : Model -init = - Model "" "" "" - - - --- UPDATE - - -type Msg - = Name String - | Password String - | PasswordAgain String - - -update : Msg -> Model -> Model -update msg model = - case msg of - Name name -> - { model | name = name } - - Password password -> - { model | password = password } - - PasswordAgain password -> - { model | passwordAgain = password } - - - --- VIEW - - -view : Model -> Html Msg -view model = - div [] - [ viewInput "text" "Name" model.name Name - , viewInput "password" "Password" model.password Password - , viewInput "password" "Re-enter Password" model.passwordAgain PasswordAgain - , viewValidation model - ] - - -viewInput : String -> String -> String -> (String -> msg) -> Html msg -viewInput t p v toMsg = - input [ type_ t, placeholder p, value v, onInput toMsg ] [] - - -viewValidation : Model -> Html msg -viewValidation model = - if model.password == model.passwordAgain then - div [ style "color" "green" ] [ text "OK" ] - - else - div [ style "color" "red" ] [ text "Passwords do not match!" ] diff --git a/public/example-files/groceries.guida b/public/example-files/groceries.guida deleted file mode 100644 index 9624a71..0000000 --- a/public/example-files/groceries.guida +++ /dev/null @@ -1,22 +0,0 @@ -module Main exposing (main) - --- Show a list of items I need to buy at the grocery store. - -import Html exposing (..) - - -main = - div [] - [ h1 [] [ text "My Grocery List" ] - , ul [] - [ li [] [ text "Black Beans" ] - , li [] [ text "Limes" ] - , li [] [ text "Greek Yogurt" ] - , li [] [ text "Cilantro" ] - , li [] [ text "Honey" ] - , li [] [ text "Sweet Potatoes" ] - , li [] [ text "Cumin" ] - , li [] [ text "Chili Powder" ] - , li [] [ text "Quinoa" ] - ] - ] diff --git a/public/example-files/hello.guida b/public/example-files/hello.guida deleted file mode 100644 index e7b9c6a..0000000 --- a/public/example-files/hello.guida +++ /dev/null @@ -1,7 +0,0 @@ -module Main exposing (main) - -import Html exposing (text) - - -main = - text "Hello!" diff --git a/public/example-files/image-previews.guida b/public/example-files/image-previews.guida deleted file mode 100644 index 6087161..0000000 --- a/public/example-files/image-previews.guida +++ /dev/null @@ -1,162 +0,0 @@ -module Main exposing (main) - --- Image upload with a drag and drop zone. See image previews! --- --- Dependencies: --- guida install elm/file --- guida install elm/json - -import Browser -import File exposing (File) -import File.Select as Select -import Html exposing (..) -import Html.Attributes exposing (..) -import Html.Events exposing (..) -import Json.Decode as D -import Task - - - --- MAIN - - -main = - Browser.element - { init = init - , view = view - , update = update - , subscriptions = subscriptions - } - - - --- MODEL - - -type alias Model = - { hover : Bool - , previews : List String - } - - -init : () -> ( Model, Cmd Msg ) -init _ = - ( Model False [], Cmd.none ) - - - --- UPDATE - - -type Msg - = Pick - | DragEnter - | DragLeave - | GotFiles File (List File) - | GotPreviews (List String) - - -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg model = - case msg of - Pick -> - ( model - , Select.files [ "image/*" ] GotFiles - ) - - DragEnter -> - ( { model | hover = True } - , Cmd.none - ) - - DragLeave -> - ( { model | hover = False } - , Cmd.none - ) - - GotFiles file files -> - ( { model | hover = False } - , Task.perform GotPreviews <| - Task.sequence <| - List.map File.toUrl (file :: files) - ) - - GotPreviews urls -> - ( { model | previews = urls } - , Cmd.none - ) - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions model = - Sub.none - - - --- VIEW - - -view : Model -> Html Msg -view model = - div - [ style "border" - (if model.hover then - "6px dashed purple" - - else - "6px dashed #ccc" - ) - , style "border-radius" "20px" - , style "width" "480px" - , style "margin" "100px auto" - , style "padding" "40px" - , style "display" "flex" - , style "flex-direction" "column" - , style "justify-content" "center" - , style "align-items" "center" - , hijackOn "dragenter" (D.succeed DragEnter) - , hijackOn "dragover" (D.succeed DragEnter) - , hijackOn "dragleave" (D.succeed DragLeave) - , hijackOn "drop" dropDecoder - ] - [ button [ onClick Pick ] [ text "Upload Images" ] - , div - [ style "display" "flex" - , style "align-items" "center" - , style "height" "60px" - , style "padding" "20px" - ] - (List.map viewPreview model.previews) - ] - - -viewPreview : String -> Html msg -viewPreview url = - div - [ style "width" "60px" - , style "height" "60px" - , style "background-image" ("url('" ++ url ++ "')") - , style "background-position" "center" - , style "background-repeat" "no-repeat" - , style "background-size" "contain" - ] - [] - - -dropDecoder : D.Decoder Msg -dropDecoder = - D.at [ "dataTransfer", "files" ] (D.oneOrMore GotFiles File.decoder) - - -hijackOn : String -> D.Decoder msg -> Attribute msg -hijackOn event decoder = - preventDefaultOn event (D.map hijack decoder) - - -hijack : msg -> ( msg, Bool ) -hijack msg = - ( msg, True ) diff --git a/public/example-files/keyboard.guida b/public/example-files/keyboard.guida deleted file mode 100644 index bd059ee..0000000 --- a/public/example-files/keyboard.guida +++ /dev/null @@ -1,25 +0,0 @@ -module Main exposing (main) - --- Move a square around with the arrow keys: UP, DOWN, LEFT, RIGHT --- Try making it move around more quickly! --- --- Learn more about the playground here: --- https://package.elm-lang.org/packages/evancz/elm-playground/latest/ - -import Playground exposing (..) - - -main = - game view update ( 0, 0 ) - - -view computer ( x, y ) = - [ square blue 40 - |> move x y - ] - - -update computer ( x, y ) = - ( x + toX computer.keyboard - , y + toY computer.keyboard - ) diff --git a/public/example-files/mario.guida b/public/example-files/mario.guida deleted file mode 100644 index 979f56c..0000000 --- a/public/example-files/mario.guida +++ /dev/null @@ -1,102 +0,0 @@ -module Main exposing (main) - --- Walk around with the arrow keys. Press the UP arrow to jump! --- --- Learn more about the playground here: --- https://package.elm-lang.org/packages/evancz/elm-playground/latest/ - -import Playground exposing (..) - - - --- MAIN - - -main = - game view - update - { x = 0 - , y = 0 - , vx = 0 - , vy = 0 - , dir = "right" - } - - - --- VIEW - - -view computer mario = - let - w = - computer.screen.width - - h = - computer.screen.height - - b = - computer.screen.bottom - in - [ rectangle (rgb 174 238 238) w h - , rectangle (rgb 74 163 41) w 100 - |> moveY b - , image 70 70 (toGif mario) - |> move mario.x (b + 76 + mario.y) - ] - - -toGif mario = - if mario.y > 0 then - "https://elm-lang.org/images/mario/jump/" ++ mario.dir ++ ".gif" - - else if mario.vx /= 0 then - "https://elm-lang.org/images/mario/walk/" ++ mario.dir ++ ".gif" - - else - "https://elm-lang.org/images/mario/stand/" ++ mario.dir ++ ".gif" - - - --- UPDATE - - -update computer mario = - let - dt = - 1.666 - - vx = - toX computer.keyboard - - vy = - if mario.y == 0 then - if computer.keyboard.up then - 5 - - else - 0 - - else - mario.vy - dt / 8 - - x = - mario.x + dt * vx - - y = - mario.y + dt * vy - in - { x = x - , y = max 0 y - , vx = vx - , vy = vy - , dir = - if vx == 0 then - mario.dir - - else if vx < 0 then - "left" - - else - "right" - } diff --git a/public/example-files/mouse.guida b/public/example-files/mouse.guida deleted file mode 100644 index 24e9239..0000000 --- a/public/example-files/mouse.guida +++ /dev/null @@ -1,30 +0,0 @@ -module Main exposing (main) - --- Draw a cicle around the mouse. Change its color by pressing down. --- --- Learn more about the playground here: --- https://package.elm-lang.org/packages/evancz/elm-playground/latest/ - -import Playground exposing (..) - - -main = - game view update () - - -view computer memory = - [ circle lightPurple 30 - |> moveX computer.mouse.x - |> moveY computer.mouse.y - |> fade - (if computer.mouse.down then - 0.2 - - else - 1 - ) - ] - - -update computer memory = - memory diff --git a/public/example-files/numbers.guida b/public/example-files/numbers.guida deleted file mode 100644 index af58601..0000000 --- a/public/example-files/numbers.guida +++ /dev/null @@ -1,84 +0,0 @@ -module Main exposing (main) - --- Press a button to generate a random number between 1 and 6. --- --- Read how it works: --- https://guide.elm-lang.org/effects/random.html - -import Browser -import Html exposing (..) -import Html.Events exposing (..) -import Random - - - --- MAIN - - -main = - Browser.element - { init = init - , update = update - , subscriptions = subscriptions - , view = view - } - - - --- MODEL - - -type alias Model = - { dieFace : Int - } - - -init : () -> ( Model, Cmd Msg ) -init _ = - ( Model 1 - , Cmd.none - ) - - - --- UPDATE - - -type Msg - = Roll - | NewFace Int - - -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg model = - case msg of - Roll -> - ( model - , Random.generate NewFace (Random.int 1 6) - ) - - NewFace newFace -> - ( Model newFace - , Cmd.none - ) - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions model = - Sub.none - - - --- VIEW - - -view : Model -> Html Msg -view model = - div [] - [ h1 [] [ text (String.fromInt model.dieFace) ] - , button [ onClick Roll ] [ text "Roll" ] - ] diff --git a/public/example-files/picture.guida b/public/example-files/picture.guida deleted file mode 100644 index 1841450..0000000 --- a/public/example-files/picture.guida +++ /dev/null @@ -1,17 +0,0 @@ -module Main exposing (main) - --- Create pictures from simple shapes. Like a tree! --- --- Learn more about the playground here: --- https://package.elm-lang.org/packages/evancz/elm-playground/latest/ - -import Playground exposing (..) - - -main = - picture - [ rectangle brown 40 200 - |> moveDown 80 - , circle green 100 - |> moveUp 100 - ] diff --git a/public/example-files/positions.guida b/public/example-files/positions.guida deleted file mode 100644 index cec24a9..0000000 --- a/public/example-files/positions.guida +++ /dev/null @@ -1,96 +0,0 @@ -module Main exposing (main) - --- A button that moves to random positions when pressed. --- --- Dependencies: --- guida install elm/random - -import Browser -import Html exposing (..) -import Html.Attributes exposing (..) -import Html.Events exposing (..) -import Random - - - --- MAIN - - -main = - Browser.element - { init = init - , update = update - , subscriptions = subscriptions - , view = view - } - - - --- MODEL - - -type alias Model = - { x : Int - , y : Int - } - - -init : () -> ( Model, Cmd Msg ) -init _ = - ( Model 100 100 - , Cmd.none - ) - - - --- UPDATE - - -type Msg - = Clicked - | NewPosition ( Int, Int ) - - -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg model = - case msg of - Clicked -> - ( model - , Random.generate NewPosition positionGenerator - ) - - NewPosition ( x, y ) -> - ( Model x y - , Cmd.none - ) - - -positionGenerator : Random.Generator ( Int, Int ) -positionGenerator = - Random.map2 Tuple.pair - (Random.int 50 350) - (Random.int 50 350) - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions model = - Sub.none - - - --- VIEW - - -view : Model -> Html Msg -view model = - button - [ style "position" "absolute" - , style "top" (String.fromInt model.x ++ "px") - , style "left" (String.fromInt model.y ++ "px") - , onClick Clicked - ] - [ text "Click me!" ] diff --git a/public/example-files/quotes.guida b/public/example-files/quotes.guida deleted file mode 100644 index cbb09b7..0000000 --- a/public/example-files/quotes.guida +++ /dev/null @@ -1,139 +0,0 @@ -module Main exposing (main) - --- Press a button to send a GET request for random quotes. --- --- Read how it works: --- https://guide.elm-lang.org/effects/json.html - -import Browser -import Html exposing (..) -import Html.Attributes exposing (style) -import Html.Events exposing (..) -import Http -import Json.Decode exposing (Decoder, field, int, map4, string) - - - --- MAIN - - -main = - Browser.element - { init = init - , update = update - , subscriptions = subscriptions - , view = view - } - - - --- MODEL - - -type Model - = Failure - | Loading - | Success Quote - - -type alias Quote = - { quote : String - , source : String - , author : String - , year : Int - } - - -init : () -> ( Model, Cmd Msg ) -init _ = - ( Loading, getRandomQuote ) - - - --- UPDATE - - -type Msg - = MorePlease - | GotQuote (Result Http.Error Quote) - - -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg model = - case msg of - MorePlease -> - ( Loading, getRandomQuote ) - - GotQuote result -> - case result of - Ok quote -> - ( Success quote, Cmd.none ) - - Err _ -> - ( Failure, Cmd.none ) - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions model = - Sub.none - - - --- VIEW - - -view : Model -> Html Msg -view model = - div [] - [ h2 [] [ text "Random Quotes" ] - , viewQuote model - ] - - -viewQuote : Model -> Html Msg -viewQuote model = - case model of - Failure -> - div [] - [ text "I could not load a random quote for some reason. " - , button [ onClick MorePlease ] [ text "Try Again!" ] - ] - - Loading -> - text "Loading..." - - Success quote -> - div [] - [ button [ onClick MorePlease, style "display" "block" ] [ text "More Please!" ] - , blockquote [] [ text quote.quote ] - , p [ style "text-align" "right" ] - [ text "— " - , cite [] [ text quote.source ] - , text (" by " ++ quote.author ++ " (" ++ String.fromInt quote.year ++ ")") - ] - ] - - - --- HTTP - - -getRandomQuote : Cmd Msg -getRandomQuote = - Http.get - { url = "https://elm-lang.org/api/random-quotes" - , expect = Http.expectJson GotQuote quoteDecoder - } - - -quoteDecoder : Decoder Quote -quoteDecoder = - map4 Quote - (field "quote" string) - (field "source" string) - (field "author" string) - (field "year" int) diff --git a/public/example-files/shapes.guida b/public/example-files/shapes.guida deleted file mode 100644 index 2fc2ca1..0000000 --- a/public/example-files/shapes.guida +++ /dev/null @@ -1,79 +0,0 @@ -module Main exposing (main) - --- Scalable Vector Graphics (SVG) can be a nice way to draw things in 2D. --- Here are some common SVG shapes. --- --- Dependencies: --- guida install elm/svg - -import Html exposing (Html) -import Svg exposing (..) -import Svg.Attributes exposing (..) - - -main : Html msg -main = - svg - [ viewBox "0 0 400 400" - , width "400" - , height "400" - ] - [ circle - [ cx "50" - , cy "50" - , r "40" - , fill "red" - , stroke "black" - , strokeWidth "3" - ] - [] - , rect - [ x "100" - , y "10" - , width "40" - , height "40" - , fill "green" - , stroke "black" - , strokeWidth "2" - ] - [] - , line - [ x1 "20" - , y1 "200" - , x2 "200" - , y2 "20" - , stroke "blue" - , strokeWidth "10" - , strokeLinecap "round" - ] - [] - , polyline - [ points "200,40 240,40 240,80 280,80 280,120 320,120 320,160" - , fill "none" - , stroke "red" - , strokeWidth "4" - , strokeDasharray "20,2" - ] - [] - , text_ - [ x "130" - , y "130" - , fill "black" - , textAnchor "middle" - , dominantBaseline "central" - , transform "rotate(-45 130,130)" - ] - [ text "Welcome to Shapes Club" - ] - ] - - - --- There are a lot of odd things about SVG, so always try to find examples --- to help you understand the weird stuff. Like these: --- --- https://www.w3schools.com/graphics/svg_examples.asp --- https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d --- --- If you cannot find relevant examples, make an experiment. If you push --- through the weirdness, you can do a lot with SVG. diff --git a/public/example-files/text-fields.guida b/public/example-files/text-fields.guida deleted file mode 100644 index 9d75ea8..0000000 --- a/public/example-files/text-fields.guida +++ /dev/null @@ -1,60 +0,0 @@ -module Main exposing (main) - --- A text input for reversing text. Very useful! --- --- Read how it works: --- https://guide.elm-lang.org/architecture/text_fields.html - -import Browser -import Html exposing (Attribute, Html, div, input, text) -import Html.Attributes exposing (..) -import Html.Events exposing (onInput) - - - --- MAIN - - -main = - Browser.sandbox { init = init, update = update, view = view } - - - --- MODEL - - -type alias Model = - { content : String - } - - -init : Model -init = - { content = "" } - - - --- UPDATE - - -type Msg - = Change String - - -update : Msg -> Model -> Model -update msg model = - case msg of - Change newContent -> - { model | content = newContent } - - - --- VIEW - - -view : Model -> Html Msg -view model = - div [] - [ input [ placeholder "Text to reverse", value model.content, onInput Change ] [] - , div [] [ text (String.reverse model.content) ] - ] diff --git a/public/example-files/thwomp.guida b/public/example-files/thwomp.guida deleted file mode 100644 index 2701b44..0000000 --- a/public/example-files/thwomp.guida +++ /dev/null @@ -1,304 +0,0 @@ -module Main exposing (main) - --- Thwomp looks at your mouse. What is it up to? --- --- Dependencies: --- guida install elm/json --- guida install elm-explorations/linear-algebra --- guida install elm-explorations/webgl --- --- Thanks to The PaperNES Guy for the texture: --- https://the-papernes-guy.deviantart.com/art/Thwomps-Thwomps-Thwomps-186879685 - -import Browser -import Browser.Dom as Dom -import Browser.Events as E -import Html exposing (Html) -import Html.Attributes exposing (height, style, width) -import Json.Decode as D -import Math.Matrix4 as Mat4 exposing (Mat4) -import Math.Vector2 as Vec2 exposing (Vec2, vec2) -import Math.Vector3 as Vec3 exposing (Vec3, vec3) -import Result -import Task -import WebGL -import WebGL.Texture as Texture - - - --- MAIN - - -main : Program () Model Msg -main = - Browser.element - { init = init - , view = view - , update = \msg model -> ( update msg model, Cmd.none ) - , subscriptions = subscriptions - } - - - --- MODEL - - -type alias Model = - { width : Float - , height : Float - , x : Float - , y : Float - , side : Maybe Texture.Texture - , face : Maybe Texture.Texture - } - - -init : () -> ( Model, Cmd Msg ) -init _ = - ( { width = 0 - , height = 0 - , x = 0 - , y = 0 - , face = Nothing - , side = Nothing - } - , Cmd.batch - [ Task.perform GotViewport Dom.getViewport - , Task.attempt GotFace (Texture.loadWith options "https://elm-lang.org/images/thwomp-face.jpg") - , Task.attempt GotSide (Texture.loadWith options "https://elm-lang.org/images/thwomp-side.jpg") - ] - ) - - -options : Texture.Options -options = - { magnify = Texture.nearest - , minify = Texture.nearest - , horizontalWrap = Texture.repeat - , verticalWrap = Texture.repeat - , flipY = True - } - - - --- UPDATE - - -type Msg - = GotFace (Result Texture.Error Texture.Texture) - | GotSide (Result Texture.Error Texture.Texture) - | GotViewport Dom.Viewport - | Resized Int Int - | MouseMoved Float Float - - -update : Msg -> Model -> Model -update msg model = - case msg of - GotFace result -> - { model - | face = - Result.toMaybe result - } - - GotSide result -> - { model - | side = - Result.toMaybe result - } - - GotViewport { viewport } -> - { model - | width = - viewport.width - , height = - viewport.height - } - - Resized width height -> - { model - | width = - toFloat width - , height = - toFloat height - } - - MouseMoved x y -> - { model - | x = - x - , y = - y - } - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions _ = - Sub.batch - [ E.onResize Resized - , E.onMouseMove decodeMovement - ] - - -decodeMovement : D.Decoder Msg -decodeMovement = - D.map2 MouseMoved - (D.field "pageX" D.float) - (D.field "pageY" D.float) - - - --- VIEW - - -view : Model -> Html Msg -view model = - case Maybe.map2 Tuple.pair model.face model.side of - Nothing -> - Html.text "Loading textures..." - - Just ( face, side ) -> - let - perspective = - toPerspective model.x model.y model.width model.height - in - WebGL.toHtml - [ style "display" "block" - , style "position" "absolute" - , style "left" "0" - , style "top" "0" - , width (round model.width) - , height (round model.height) - ] - [ WebGL.entity vertexShader - fragmentShader - faceMesh - { perspective = perspective - , texture = face - } - , WebGL.entity vertexShader - fragmentShader - sidesMesh - { perspective = perspective - , texture = side - } - ] - - -toPerspective : Float -> Float -> Float -> Float -> Mat4 -toPerspective x y width height = - let - eye = - Vec3.scale 6 <| - Vec3.normalize <| - vec3 (0.5 - x / width) (y / height - 0.5) 1 - in - Mat4.mul - (Mat4.makePerspective 45 (width / height) 0.01 100) - (Mat4.makeLookAt eye (vec3 0 0 0) Vec3.j) - - - --- MESHES - - -type alias Vertex = - { position : Vec3 - , coord : Vec2 - } - - -faceMesh : WebGL.Mesh Vertex -faceMesh = - WebGL.triangles square - - -sidesMesh : WebGL.Mesh Vertex -sidesMesh = - WebGL.triangles <| - List.concatMap rotatedSquare <| - [ ( 90, 0 ) - , ( 180, 0 ) - , ( 270, 0 ) - , ( 0, 90 ) - , ( 0, 270 ) - ] - - -rotatedSquare : ( Float, Float ) -> List ( Vertex, Vertex, Vertex ) -rotatedSquare ( angleXZ, angleYZ ) = - let - transformMat = - Mat4.mul - (Mat4.makeRotate (degrees angleXZ) Vec3.j) - (Mat4.makeRotate (degrees angleYZ) Vec3.i) - - transform vertex = - { vertex | position = Mat4.transform transformMat vertex.position } - - transformTriangle ( a, b, c ) = - ( transform a, transform b, transform c ) - in - List.map transformTriangle square - - -square : List ( Vertex, Vertex, Vertex ) -square = - let - topLeft = - Vertex (vec3 -1 1 1) (vec2 0 1) - - topRight = - Vertex (vec3 1 1 1) (vec2 1 1) - - bottomLeft = - Vertex (vec3 -1 -1 1) (vec2 0 0) - - bottomRight = - Vertex (vec3 1 -1 1) (vec2 1 0) - in - [ ( topLeft, topRight, bottomLeft ) - , ( bottomLeft, topRight, bottomRight ) - ] - - - --- SHADERS - - -type alias Uniforms = - { perspective : Mat4 - , texture : Texture.Texture - } - - -vertexShader : WebGL.Shader Vertex Uniforms { vcoord : Vec2 } -vertexShader = - [glsl| - attribute vec3 position; - attribute vec2 coord; - uniform mat4 perspective; - varying vec2 vcoord; - - void main () { - gl_Position = perspective * vec4(position, 1.0); - vcoord = coord.xy; - } - |] - - -fragmentShader : WebGL.Shader {} Uniforms { vcoord : Vec2 } -fragmentShader = - [glsl| - precision mediump float; - uniform sampler2D texture; - varying vec2 vcoord; - - void main () { - gl_FragColor = texture2D(texture, vcoord); - } - |] diff --git a/public/example-files/time.guida b/public/example-files/time.guida deleted file mode 100644 index dc17c36..0000000 --- a/public/example-files/time.guida +++ /dev/null @@ -1,95 +0,0 @@ -module Main exposing (main) - --- Show the current time in your time zone. --- --- Read how it works: --- https://guide.elm-lang.org/effects/time.html --- --- For an analog clock, check out this SVG example: --- https://elm-lang.org/examples/clock - -import Browser -import Html exposing (..) -import Task -import Time - - - --- MAIN - - -main = - Browser.element - { init = init - , view = view - , update = update - , subscriptions = subscriptions - } - - - --- MODEL - - -type alias Model = - { zone : Time.Zone - , time : Time.Posix - } - - -init : () -> ( Model, Cmd Msg ) -init _ = - ( Model Time.utc (Time.millisToPosix 0) - , Task.perform AdjustTimeZone Time.here - ) - - - --- UPDATE - - -type Msg - = Tick Time.Posix - | AdjustTimeZone Time.Zone - - -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg model = - case msg of - Tick newTime -> - ( { model | time = newTime } - , Cmd.none - ) - - AdjustTimeZone newZone -> - ( { model | zone = newZone } - , Cmd.none - ) - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions model = - Time.every 1000 Tick - - - --- VIEW - - -view : Model -> Html Msg -view model = - let - hour = - String.fromInt (Time.toHour model.zone model.time) - - minute = - String.fromInt (Time.toMinute model.zone model.time) - - second = - String.fromInt (Time.toSecond model.zone model.time) - in - h1 [] [ text (hour ++ ":" ++ minute ++ ":" ++ second) ] diff --git a/public/example-files/triangle.guida b/public/example-files/triangle.guida deleted file mode 100644 index 0a3d4f8..0000000 --- a/public/example-files/triangle.guida +++ /dev/null @@ -1,140 +0,0 @@ -module Main exposing (main) - --- guida install elm-explorations/linear-algebra --- guida install elm-explorations/webgl - -import Browser -import Browser.Events as E -import Html exposing (Html) -import Html.Attributes exposing (height, style, width) -import Math.Matrix4 as Mat4 exposing (Mat4) -import Math.Vector3 as Vec3 exposing (Vec3, vec3) -import WebGL - - - --- MAIN - - -main = - Browser.element - { init = init - , view = view - , update = update - , subscriptions = subscriptions - } - - - --- MODEL - - -type alias Model = - Float - - -init : () -> ( Model, Cmd Msg ) -init () = - ( 0, Cmd.none ) - - - --- UPDATE - - -type Msg - = TimeDelta Float - - -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg currentTime = - case msg of - TimeDelta delta -> - ( delta + currentTime, Cmd.none ) - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions _ = - E.onAnimationFrameDelta TimeDelta - - - --- VIEW - - -view : Model -> Html msg -view t = - WebGL.toHtml - [ width 400 - , height 400 - , style "display" "block" - ] - [ WebGL.entity vertexShader fragmentShader mesh { perspective = perspective (t / 1000) } - ] - - -perspective : Float -> Mat4 -perspective t = - Mat4.mul - (Mat4.makePerspective 45 1 0.01 100) - (Mat4.makeLookAt (vec3 (4 * cos t) 0 (4 * sin t)) (vec3 0 0 0) (vec3 0 1 0)) - - - --- MESH - - -type alias Vertex = - { position : Vec3 - , color : Vec3 - } - - -mesh : WebGL.Mesh Vertex -mesh = - WebGL.triangles - [ ( Vertex (vec3 0 0 0) (vec3 1 0 0) - , Vertex (vec3 1 1 0) (vec3 0 1 0) - , Vertex (vec3 1 -1 0) (vec3 0 0 1) - ) - ] - - - --- SHADERS - - -type alias Uniforms = - { perspective : Mat4 - } - - -vertexShader : WebGL.Shader Vertex Uniforms { vcolor : Vec3 } -vertexShader = - [glsl| - attribute vec3 position; - attribute vec3 color; - uniform mat4 perspective; - varying vec3 vcolor; - - void main () { - gl_Position = perspective * vec4(position, 1.0); - vcolor = color; - } - |] - - -fragmentShader : WebGL.Shader {} Uniforms { vcolor : Vec3 } -fragmentShader = - [glsl| - precision mediump float; - varying vec3 vcolor; - - void main () { - gl_FragColor = vec4(vcolor, 1.0); - } - |] diff --git a/public/example-files/turtle.guida b/public/example-files/turtle.guida deleted file mode 100644 index 413da1d..0000000 --- a/public/example-files/turtle.guida +++ /dev/null @@ -1,34 +0,0 @@ -module Main exposing (main) - --- Use arrow keys to move the turtle around. --- --- Forward with UP and turn with LEFT and RIGHT. --- --- Learn more about the playground here: --- https://package.elm-lang.org/packages/evancz/elm-playground/latest/ - -import Playground exposing (..) - - -main = - game view - update - { x = 0 - , y = 0 - , angle = 0 - } - - -view computer turtle = - [ rectangle blue computer.screen.width computer.screen.height - , image 96 96 "https://elm-lang.org/images/turtle.gif" - |> move turtle.x turtle.y - |> rotate turtle.angle - ] - - -update computer turtle = - { x = turtle.x + toY computer.keyboard * cos (degrees turtle.angle) - , y = turtle.y + toY computer.keyboard * sin (degrees turtle.angle) - , angle = turtle.angle - toX computer.keyboard - } diff --git a/public/example-files/upload.guida b/public/example-files/upload.guida deleted file mode 100644 index 7f13da0..0000000 --- a/public/example-files/upload.guida +++ /dev/null @@ -1,86 +0,0 @@ -module Main exposing (main) - --- File upload with the node. --- --- Dependencies: --- guida install elm/file --- guida install elm/json - -import Browser -import File exposing (File) -import Html exposing (..) -import Html.Attributes exposing (..) -import Html.Events exposing (..) -import Json.Decode as D - - - --- MAIN - - -main = - Browser.element - { init = init - , view = view - , update = update - , subscriptions = subscriptions - } - - - --- MODEL - - -type alias Model = - List File - - -init : () -> ( Model, Cmd Msg ) -init _ = - ( [], Cmd.none ) - - - --- UPDATE - - -type Msg - = GotFiles (List File) - - -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg model = - case msg of - GotFiles files -> - ( files, Cmd.none ) - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions model = - Sub.none - - - --- VIEW - - -view : Model -> Html Msg -view model = - div [] - [ input - [ type_ "file" - , multiple True - , on "change" (D.map GotFiles filesDecoder) - ] - [] - , div [] [ text (Debug.toString model) ] - ] - - -filesDecoder : D.Decoder (List File) -filesDecoder = - D.at [ "target", "files" ] (D.list File.decoder) diff --git a/public/images/mario/jump/left.gif b/public/images/mario/jump/left.gif new file mode 100755 index 0000000000000000000000000000000000000000..063f2ae588f96c9c8d8a082ce846853aabcd3fb6 GIT binary patch literal 287 zcmV+)0pR{eNk%w1VIu$|0P_F<0000I2?Q<;0CoTZQ7sb#C1Sh)1FL5``1UW@jb#u; zp%jg`NB?(_Ed`1vFu06R^$a>@Vz literal 0 HcmV?d00001 diff --git a/public/images/mario/jump/right.gif b/public/images/mario/jump/right.gif new file mode 100755 index 0000000000000000000000000000000000000000..b9c7c766b475dcace0c47ee42d1b7b49a27cea7c GIT binary patch literal 283 zcmV+$0p$KiNk%w1VIu$|0P_$40000I2?Q<;0CoTZQ7sb#C1Sh)1FL5``1UW@jb#u; zp%jX4zE}Rp-BusuQ^`@xJEq+3J3)R2e2#vz`+B=$H>WF1O(03(FfCw h)z?tc$}0c|)dapGInN literal 0 HcmV?d00001 diff --git a/public/images/mario/stand/left.gif b/public/images/mario/stand/left.gif new file mode 100755 index 0000000000000000000000000000000000000000..9e8ec007257c2654754f3dc2a9701fd30135ca37 GIT binary patch literal 357 zcmZ?wbhEHbRAx|S_`(1JLYypmybNUwOo6(>%&JLy7?@Y5TmAT^f1@{5z;&U>^xKpA z+Wv0pICgyNm#@$N|M(BqL@c5BlZBIkL4ZN=KewN2NU*bGfUA+70W%{|zYfR@kTV!q z&nu|(rDV>_ShX(a^}Yh0GsS0^njiBx=Pqt&KdA78u}*DP(GBN5hQluk(lV?H_q<7n zvW+Y5YdLqLw5zH6*rSOa94yyfGf&_!yVJDoyLrZWyR0WclgM*t$e4?oHIXm;^dh_%LI;|Sj=<#&b`G?uO4A}$?)>kWA^t9?>{}) R_WEXb=jX5AfBX~~tO24mgmnM_ literal 0 HcmV?d00001 diff --git a/public/images/mario/stand/right.gif b/public/images/mario/stand/right.gif new file mode 100755 index 0000000000000000000000000000000000000000..56fbc9140b44538772efc5994201103a4de693ce GIT binary patch literal 364 zcmZ?wbhEHbRAx|S_`(1JLYypmybNUwOo6(>%&JLy7?@Y5TmAT^f1@{5z;&U>^xKpA z+Wv0pICgyNm#@$N|M(BqL@c5BpWDwhB-q(8z|~04fSD1fPw^)UCj)~3gAT|HkTV!q zuPLbXrDV>_ShX(a^}YfhCKKJ&UkohHG2BsaIQPLop#H$k0}mztY#K$v02&OR{{snYt2@GQvDs@kkxD1xHjl*TD661?+#rC9-CEb zIQDYv*J0q}=eOCrhHHJ=nX`O*Z=5`J=-iDpm!}@&S#sgQ9EK$?m)zRP@{Wb!{hL$l YU%xVZ`)uL+=hvM749r|6f=mq703E4(iU0rr literal 0 HcmV?d00001 diff --git a/public/images/mario/walk/left.gif b/public/images/mario/walk/left.gif new file mode 100755 index 0000000000000000000000000000000000000000..c2143a282fcc8177c3669f4cdeea0e1bea52d520 GIT binary patch literal 1732 zcmdUvYfuwc7={m=$1d0lKl|73|99qj-}Am-$dbhi zd^hp{4-9btJ(Jj|Z~{mG$O^$^hV!Quz$l9p4?p*~q1nQb72D_EJg80o^HhfQf_doo z2XBVojO_gHae6a8T(GMbD zWAM(nP9*gX9u#d*?{DJXwNtr#z2ioZ7PJh+L@30umdEms7p{-frgh!enVFJhy{!vn zp}mh7xh(I#l-d^|+lMEswj+s2-tp1VcF7t0JneFFa}u{{ymgs}kUV9^kwPVuz~aRb zkuV4=UK*>!O3PMMq+@2IZW-=fYefga;KMu2C*aAm2mG+-{(Ue1c=bBm=|7EZ0BNZ+XpLoDDllM;c7TOz zz3@mkBxhrf2e;5N!2k+vtLDCrsJxq{=y6(mV3mojbf1wGGRukM7)b`C^!8)5;jx>J ztWJdYh7xARTt1R)@!xP?EKAz?1+v#IZvQ?v=mE2QT>`-1{KBgB4iuMB5 zQSv)tW#;h{MoOc{2$H}>Q7Ec`I`dgn44CzelulFM#Jtqz)yj-&wsqBC4KPS}WB6HM z9GD2Ezva)Q`R8Dx{NlI#K@r^;Q#EZo9fy@$&E!fy zi-&!)J-QXu4(pTGEWE#i5gotBe&E%sh)=f8+Pf zpJe$M{&(Gf44{An4z&M9E`?C8`Z=NnU7~`4;c3>#NNnlVCv025j1F7siFNJCxfxz2 zM|Q7fFzN67*lWlD5N7+h z!g*M>L^hJ206ITk3X7Hjmi}0tJ8ppba?G7>g1X>nW!0&ga?@$Ts55EthDNP56QCD6 zE~4px*=_5-;wrwb{>T1_z#aQH{?+nPd<8lZp9ryky0k&P?$`O*h*_@L{8Y2P{rbrQ zzuHLx$KPJ8o;NioIFKvq0_;XZ)f7cxf4aE%KGv?Nkk}6UFoq?1OLXQ?yb@8(mhVVN zqZoAlvqKde0?x;>1N{L9%gdKZMF7W24liLW^aZSva;;V%Fv60WV~`-vRM!9$JzRH& zRA>RS0j25*6@V9*?M6>t19c^BU&qC3u6{qx8KZ+q*T82iOT?u#hzIF%ecOs;#T%voPm#=k(5`ZX2YVg6cc?s>;i@D?JiE^M{*#KiTH~ Ua=Z85U9V9+5YT;o*Z;-uFR_hlqyPW_ literal 0 HcmV?d00001 diff --git a/public/images/mario/walk/right.gif b/public/images/mario/walk/right.gif new file mode 100755 index 0000000000000000000000000000000000000000..4e69804873ad25edf6d1dea8ec81e893f32f8f5f GIT binary patch literal 1763 zcmds$YfMvT7=XXF=k&D4<7pX66gMNomI`gTScF1JtJtCF{M8&D7jalJ^oko$$>%h z^oz%{GqcW_fBn2>=fz6nK8swvSsKI-ppb|`uU8Nq04Cr%KXI-B;M>cVYj-ABC~E5W z{Ceq|jbs7OG#wCX1^0c_)@K3CtnTsdZA!Lu4P|uQE_NlTOf^MLG=_bZ^Ed;AbJjj= zQgD|}GNYWMPHZip+S%swH&8Lp#N!EnH zaOF8oB$WcAGWf@UEpetgD%!|zu4zHL+IRpH)zs3uXqUMFXD~!vwP?fcYomCVV|b+3 z>Zlzn?!l_APo4!;Ppa-(pr;V{ZE6I0@dCVj9vbuKA7^I)1r@lF-=U%aCWt)=)C&YE zQ*PLqg6BTMB);U@-xnRK#b=%r3UcXDQ=@6%W99G-IGGpK3oo%lb8zhi9%Tx(J7_fL zEfjeE5|VyCrfo@f`zmZmPf=&9C91tSq#%%fFkx#n40B-(E7-{uS||~%Ej3Wl%nB-> zj~X?raYj9Eq?tr@4aT!u1z26<#Wn!t!ySda5ER(mlg}A}kYQwSXsZpmzVgOk>>ZB9 z$KvhvJ_i-JfyD%eagJjK|io<0vOQ-qMGxr@{+b?uVfT~7!l#pt*iYG*1bOYg9zb!Bs4$;3tUyS6C~aQ$p(X~ zit`dBQH3qB{Ci1-t;p|nT;G?<@w;`@# S8Gkh5>%xHnQTKmeO!(gq#cT-x literal 0 HcmV?d00001 diff --git a/public/images/thwomp-face.jpg b/public/images/thwomp-face.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c5a8961107b6dc5c4825521a5da812b867bc7ced GIT binary patch literal 12683 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 literal 0 HcmV?d00001 diff --git a/public/images/thwomp-side.jpg b/public/images/thwomp-side.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a775982aa6e9841e0960eadfe867d759155755fd GIT binary patch literal 6783 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 literal 0 HcmV?d00001 diff --git a/public/images/wood-crate.jpg b/public/images/wood-crate.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b9ba73cf0766bc3a8a59b8d68b6e9d091801dab4 GIT binary patch literal 180105 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

OCK)wW(5?6%{}vaNF8Iz}j>#zNH* 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�ITst}bO6QJzw#BEUBL|p)W zr!pzGc-F+E6)(IA+Mlm!8$&PuYbX7|aGNOA zzTYJSfm`FS)4A@DIsG#{a1R)13`1u=z;oO zt4Dy&|MCd4q9Zx&(C1~rDI3pE@ehY2H0|sHkHbG3iHQtMLr;shWa{l|z1H@m%h&XS z`mVikv!5;r)8kE+&@dl za(ur+ftMpGFYZh77TA>P)j85O)?pOVC3{AJY~{Gsu?&kK)Q$DCi}&bh}p3sizTVGB~< z99<4?RJZgC)(qgg98g<8a_HB|*>xTM%`jc%q&DnX3~ew3Nppwaavj4z*@!v{1nGw; zK1p;&z((SnxZi$k_qdbkWO%MQ0o;JA@x?IKALSWAh_g+|aR!!vJc=(A=P}=ibbcCR z25>V9cC(dN_wIOYY4N)G4jZPrnu<4si$9znrOlb2U8<70{%1Bca|H$2uwZ`mHwfc= zy`U6=;t7W!s?G~%x|FJ>?JC<6D>nYdc-;e>(qIzwr-SSCY0iz~7Sp|zH92e=;J{~{ zDmvA89&7=j!_~l=u<^g&3kc4Q1vyu~eb_KWOWQm%qKrYI;WxjqdP_bbFT&t zzNROcs*A)fNFW!aq69=AKvOV(r*p5OC zGH(LQP^(YnxA=KJW@7TRE9yRCDt@pnYPb0|9>Fw4O>BM~ zDsY(ruCvsOnXKW7D(>ydex_7#_SIDM|1q8k0PW zwv=LP=I`TU{LmrC88h1vhaa2pwHt}~y`p`cSCn)MG{9pNQg<6n)EH@3xF<&Mn{iAsrb@-whqay#S zb~9=g`eO)~LMAlD!A33uUx=XmPV%Eo(1(kdWzCN0l_pxUrX^%o(^nvVSJn6?BGct3 znRXPNQLPf7U>D~u=47RjV)waoGDXSr-I}(dyKot&I>P1wN=LT${_~?svMud3vBNMx zeYY|FhC)W>sQfw*A*!jv2qW=xXL$i!~ zOoQE6UA$0r2s`?<`CRwTWI?Bwa03ZW2)RIvk({P{C=h3-jgbg{Q^@?}#bKAGY#_)E zB5nZA-fLeT?r^`7;tt2onadRyU^8661H;92p@-Rud+xmmW~F5wBbJrlww#3m*tdQf z(v5IE)w_Ctee>8!cV7Qn{!CDv(YJSQj)GD?4^0N80g}u9334>l&rC0J`LhIkj|tJ>DR}+@5{MoUw)mbW0k*(Mv+6+_0W^z zPy?$*WmWj*@K8l&5`%*UPFSvAgMG@mWp9*Z;c#&t z3Neq8c9BbcOU1yLPhFW>Xaj;hPa%D=FNKk)Y?RfcHEQTAC=8;Up_7japEL~a(5__# z6jdZ0zyDsv2L;7eradNz$(HF?*12n?KQDNl1Mq9YT;3eQ{L+XgTD)Fh?IFtww->?q zD_MN5X+b^T=O0aX_Pz9lYeO3=@&t{menqT zGSS*oUwtelppV7R-lvkkbDp3iE4A^IB&$O5|H*e>QEYJoBnXoM3(83a7 z(ATf}*Nqd?qgIH4p}T7Tq1gi7IzLKao8QpJ|H`QFpsw>a{TmopJYOP zP~8#}Px_9lLvhGgNS}slRQ?Q%t&;w`k*!#wm$j3%)=~ZLwP5FCV907 zMV*aO{0xDd-y7DO)N{|NK>eKOmzx$*93%v3sSu2qjz$~mD=>(TsX1aH9u=GMN>O6y z_jV?XjWaNExgnXXq7JinLF4P1oj%ee7S-m2Gwr$L-%Q2wPsIVinACukJ+DDt1EO=Z zX2dfLf-2;*4fZF)cKsn`&z|FJXVc4m#lim8GSRKp3==wgRN#(%sjMXwz9y^TRndMc zuagBl1xEXKH1Nd(`gya3*_Rv8fs==hLBpRznjXJ@--Md~rXj{p};cQ7EbU3A<&cr(5m+)i&2%dbiFi^F&%pLD7iM zWOLy`t7Mp=`7GM+9F$LQM3%GyOUk^^gxtD9T`{)rnY~kAmzg@GrI->EbB*&91+&T! zF6}P)bTG{dDb;0M-`^pHOd7DKPAIOpv&|qU>r_j!D@YfXNo(2OBtp6Q(DXkVt!6yv zGjtIBa@|7tsnPch(Bm)4b`tK%|Di9d-PsYIA*K*4Q|uIa{Rj}ji5&wyJ52Vi+j!fk)X9zv(m8M(I+!Naj(P_4~u|}t1LCTd^*d+`xbw*}v_l44CKB@&!dKSC zIZm|%K=)7eTogL38b@oN!*WuoyE)6Q1-DY;|IrBTp2gRy*Ng_2Q}KB%E?Lms!Te*0 zi8<|<)Z%0uF6UA+)Xv8igSKbpu&=H>jZOPZk+kuf9`bIISm&1)A3N}y)ON+YcbE-?QfxSQ_HoCR?Cuh^~_hrPDP;Y%R8t!kTHu5H9+CW5La?MS0_T~-~ zNh&k@2uhO^BfAkJTm{c=9INI?d`nmH`cLT(&uE3NyQ%+ZvXR`$Y_suANP4hHJ>R0C zN)Cf;)3uHp6x&fT-7blMEX}2f)D>Jz$Sm=a-55}od5M5XSzw-)=C#HuW_28s9DV``3E{Y_r{aXs~sLN{pU2W`%t z-FYVA%|0cMd~CJ!v8=3`2;)`^$T@%@;?-Cw(Q(%$=P8;f3mu;YtYmC zN_IcN?~K+c4{sI!tN!B5ieap3Vi$A|U=MnCE&N>3ANSlz9)8|?RGQ6i@lcrRw_6vp z`BuMne6lp4dEiS!h+>DbvvnGelV}JtPxfNe80J`c+u|wHQ!Fcck}t~~YXjJ#Pf;*# zA^}xrlk-yDmu(|tTy;1WxRLR&sES>r&98RZqs82 z?d3bkA->xfe16E}PU@+Dud53kPq38g5Ku>bxNZKAhH@uc?k0P~`OnxLMK;@2iiDDQ zR@2QlSD2Q0pER+pB;{v6Vt#P|aR1ht_eqv#t|82)-hh3>=c)h)x!a7>Qd61kC`t%|K-4iI7#K=^%a_P7oVcV8hib#A2GSCu;JE* z)5>_ny-G)50E?JSBnY%44;Ojne59Yp$+W)Hp;0&%n4QSQ$9M1_=XF{!2+vSsf(G$v zI%F6CbMNg@+ejbO0GN6$O*4q&vOZ5V5Klg^I+VA>a$Bhx7i99l@}m87p3td}x$nQ` zHR@B-yGGW~5_5C$&-UF-Z;x(;vF>V~$w#U1DukYM1j1VzwJbxE8<1~QOAbRg-WxE3GPK*is0^5FXhmht^PX(ls^L5;q?sUOGS(TR`Mv_<4j z6<0SV5V*72)}-0qH4bf#?>y(rG=FwrJw?`}K!U!g?_gXdzHMc=2dT6XcB~53G~bi> z^AI0IpykzCjf7G-0W8Q=G5^H@yj(UT-beW@ri|fZI2~EUY^i$EEj#D{|J?q%aie{& zh$kwR%0Jn&QY=sI_P0dy z9f@?`os8;5dr`;D?motMyI#pxA%7m6OQt--r;zWPdqS(aZ2qX7 z>2hFJz$6M>fLWplrRZAyPG=#~E?#L^Kk%_kR^=DW_@q0J=tc0H?AM}#Zej;xiQ4Yn zZ0J*y3+8}2#Y(+_;bb0?zL?AF%A&7k&OBcrIXBer3mOI=faIZy5P%M_3g4Y#0J%9qOO|Kk=$7DrMjj>BIm^?N@$rc zOl4~_^CqS?tz5eRHFsISDivmy8YC3?(-RR~1)Ye_e>Brc%gTn)FEDB%0d^R~YJG!Q zf4~2r8Sy!3?<}zT%c-F_{B^I@H<>u!+XrJw=ct)(u8-;I?U#qjRxDnb`NEtp*MjN0 zB6JTT7Vf=?Ufa6*I@r~++P=6KL8>NNr}x&B3~Ih60gf60vkb(J@9KE1^Av!TbQYB zb2-5ySqjGx6S(#VXSzTG{g|_8v~$NMWR?LIYISMf(#uM!Ec^%gV}QdrZ#HN}m^LUM zcJn=$qhvzKu-11m_93^05>!tC#rx^zoOyR)p0VlFC*Trr|5H_ei3*(Uf}ME7OzgDy zAS4p9&5<^)XZnw3W+C@a(%Ne?5Q&bJU<4zId=-B+d`tcez1z90tF&(XJZs~}Z&IZP zRB?Ytrk{ydb9k}=mwxo!<8*o4Bbrn4T}5{{BJxwXmy||u7gnc>rJ6LWqWe;Q7yGb2>bJv?>z0BUR%dd|@)53q`O-YPUp1MxD z3=g64Z)Voj(I_FlmTt7Ur*o>WDWGL^p9y~siB(s`1K zk!+1&ou#r>6rW6;=sU6U>%gVq88HZI;2-y~^G|fn+urr2suX%sQQPbFn3iAmJCmZA z5dhEw@_q3Dy9aWg|3@=E{NX$kB?duaB{G2i3Hd}{#=W5NA~P%-icOL8^bKX;x)1*g zGK&A|6zMC#uoqF7ct@*%`8k$)O2aQt4A1tVvEKJskv;uNR_lL&r=}M=N>0a(9JMmU z9Cka8qIyPZ9w74B!HRV@p+;p9An9YzjZRnZ;jS^T*wBG&X=D5$miqq#`W(}x2!|^(s8~~ z4$i3V=>_*-@rFD88)%ZDQTqbtHrMN%mjsFG-wx?EgFxkz@R%KYbiwBYnlVqk&}lyp zQ{w%jqQcJIl(pENoi`<`EYcxA3>8#0dAn~feURpe>`(i23YjU3x0GssF(ln&h&iLS zT-OI(#I>_@7T*q8%rrM=OtB8pdgWVGPLVCd5vxtL^KeQ_S-Rup1!+|+?9JYmUVbad z*hcmcQP;=Fr=Oa$gPFgPnVkuxh#Svs7!5%LX_f(KsMm^Omz1ybhA{>ab>eG>UP9&u zBtqpc_iw(ztjNs=&K0atBd94cpVIh*y|MlmPB>doIJNPJvTf77A3P_G;T0;+f7MP- zm!al-7G}JD9yS8X=ldpSexJkH35{HL*}5!+it&jDXG>*oKdbg`tncQOy{abg=2jh+ zBgC#3b!_aKn#pFcer_PT?H;Xe#6eIGP30vZXQx(Y4V@1g1FrevfXLh~`s)#?{M|wM zPbsGvCnP%^z;pe5jj463JL?&Z9y?_rzhJbL<_V*~JXB?J;^ z`6cOUKbfDN*-eFw$+5F062s(>??SVupM`H=LbDZq$Cy9VD1G?i_PxDyFMA+`vWXd& z{f+x@o-f2{Q2hKwW=gsP4&LbhX(H8s_B0uoYlnDJdo1rmHBBl=+)G{!nn}b1$qNB5 zNr~|GuiD}~e7ABsg-Y)jBo#9?(+96Oe6Dd}8`oS$$`{ zWvkPzn1}aYF<(StRvvr%Qm65TDKKfebK&{J9p(C=m0=a{W57ANWJI|ICZ+(8cU^&2 zB}>E-80ap1Qi&X`(E+oQ3`eQ^dZVQ)H%#q|OYIFzEayx+S>8mNo4*|CioW@%BQknR z4lpsjUBEt&FPIEdW0`Fi{2UM5Ic{UkJNcwnXUyAIoab`ye5~r3KgTTx@Grmc{QX-$ zH#$OQE&cb(gOWKpICrrswn-Z|aN@4nVIJa9Onz>~ESIu~E7p+I$zPsZF8jEX)hP0d znv+ME{NkLSeqeQl12ppa%8CK|gq71V&`IQd#zmD^Q>7@8%8G+V_-~bYGDn~u@`(_< zJNCIDsit~4KNl9{ihx`S>y}@p%w_}_nWU=U_PmklR^6cB%P+SbuY(-X){--p8^rJ9 zDw<2GX00rneA4*&c=pH}kD*!%_Gd$h#2ydwc2Qg<|Bu2!XJ6lUWj9h`($3EWiZx8h zdqUY%0E^*5WI6(NX7S@~Rkx_D4O z&_W1W(xT9j-r_sDX`1otVV4UFyycpfKJ#SXn{aY!R$sQi`*xB+sFHBKtoVC4!`0~v z{YHP;+3ECxwFOZxN6cgHkKaJfd$fIVD~EqRNK#x(9P|-1R5Qw=BRcJ?&79v`*+D~F zRXO+_8&)wHzGr|BYNtDnQP0heef>_4`@33k;Kf(Vmd##evfKCCcG8W zcbZuLwz?+LG_SiE2zUkXW1rp^wI`)q?Yd#ql6NcbHuArC&A2n)oakrne&DmOd#z%| zj4WXD)pzMTpd}7M`FARqa__JeDpb(~^dK4N*jSIT{9KE7QKHIjXm)s~*#@Tb#uA*I zbX)4wXQdOsug#lTO^vsN36C9znTD8JYeaV1Rj*&yI^OSeb+eGIIz>-{$Bu5*8S3H! z(Y@Jy))VDVGM@gmOYJewwAvC`d!77eDriq@Q!Qm=^DGE%?+-WW|2e(oJ^7DjyS%cv zF4oTf{71Xh=PvIRICq5X%EZF$D``%`2S-ITjbwSsOIXdmzW_#;Qw&JK!-jaZlAx}CAcU}71(~`6acmRxhdrnM|=>V zzU;f}K`sd`{&2Q8+H)aiXHivV7_67F;X31!3`8TcBwG!h-ww}&QZe3zH{N^S9s4t{ zR|OujUb&Nu%)QsbZi*f|1~D&~I`}!I70eGhiaFhpYt%O}4clz=wQ8JGa(m>e|1SYSngc;Za><2_-IClaTTJXa90+CFBG%49EIT$}KoUAMRwefYx~)fn>{- zPCAl(!VkIPD#akG-Pn`bevXkRr*Q)MGjzW~Tof)%Ci?{78sEg)EUW&vS$9@}nZJhJ zhQ(o%PmG=@8mwhqx&b3q(#yBn7^d)$*=HXhB z)eUtjh^gNh1}P@B_4Uzu7S5lCv_B<0Uy?tQ zIgMNkj|iSQ__O!N$=kau*-zObNL5*KyT`&?2fusnttzlYAuo7h`hBh8v?b=9j1XVn zVr#*vmZ;63M7h6i3&iJAFXc>sdEQddR&a*c6K zQ#~?uaebAC)k*cq`*?HWdaZ#~P5kTn3^LeAYB;AlFnv;6=tT+8?vj%-tU`gS9;SFN zK^aWgUTyv*VcV8qBgXUdK6a*YiwKLgpw@doRkdy^(5(1-9+J0nNY9V8Yv%NJWB*vF zP3eC4T^B-rGurhmR(I-w(U{A;nUrD7u5~cvZExhfJOn=btl87gB)ivjIhLl&-G8|W zH!Sy;wZOQnh-2%mn>Qq!{$=$z1`unxz7$b^7ku22nksf}6U0O{;!f<(@4QaMsX{S-am|+kyo;w6 z+Lu;T!(;tHSf~A=;(T1))9n|3MB%Y^{7z4q^)d}bqK+hQ+NZe>7QLKgceng-5k2jF z7B-l;8qf%GAG1S@!pAA#dFG5OOCN#whao$sR0+wyKh zx<>51&^KfY>L+$`Cmuu0E~WRavF*$z76yZfZ?t(=o!1}Pybd;k!WGjvX+@Xm{>>PYgp!tUBJecA;W4tG2`yBO|@m-VOt6Yas60iZO>7Q zkGVGASLT`A8jx*$LjxCVdM2(i#CMf&=y%co`1d!-TjPK-qs&2`7*+C>eAZt|F)Ooo zqh+gJR%i^nvhT9-bM-*+9AXHHQj`1oo@|&${7lTLiuTWV@1L^uNb{-tWUXXmfQT=G zNkWvLX~DRxEPH#j$(To<93_NUT(XI<_|^v7I(Cop3H`!q#l2eV?ob)ve1{ z>7>n7mOf`)Csi%b`To;WH5R(0_+l;qI$q@{51K)KGVJFS-s2>0{@@&JYPK&}) zF-#A>?N0_?n!Pgijbg+V5d+c1IV}ejJ8z<^)r}N`ed42^z{^^(1cM8@`qwT!(7%&7 znU)_|@^XX%Qp=QZs>l6y5Iw{df2P(1Of52w@A7vAiH?+QM1Y&|nQR8wEpbP&m-5&I z`YYZJTWaG$Pd@gNgX{db@{_k=+ka6|FXT~k=NLt@)Zr|=#2YBL=@*v)H;(O<)vR3- zpZ|Pb1gntFez?{zn7LfM`iA z=wICkLqo-J%G1B|Y}clCn&4F`H{UNxI;%kdV;2(Rw-DX_J08Wc**fJBEl zHj}0NB>PVy`2Nuh#XBRLNRkqTmhJR=r`RAw&#FLEwzlw>EpHhXw|p$`pSW`cRna^Q z^sZ6JmRW-UZmY6-l4b4{^VKxvH!ZrCb@DH#Ww{8baKE ziwxP??+I1p-^}@-ul?t^XVI``ZSbM9o#$y@b^)W9?>NHa7~H=PQe4&XLGfA4E1o{y z$J$Zp-yF`~RG$Gtc{z*Ci51k%4tF`&6d>?x6gEML;#TI_K6hm7HM_J`mfTmF_=#2H z$AY@(j%!js`L;rfZu3WptpHlPH6r@M^L>gWjP#&pQ9YGxIUHjsEz(ctV-?>x)D zsQdMDlZEnY0&`U7fSM6Dz?;DO0<;vX-ISUL zk#$Sv%eEV>rb0i0<+05MWN9a41^0jVVAp&kJ}zWSLz`eK($LvMQ+Bxt_<#6$_>F@P z{A|wixY)X4J7P)4dBTxEkzBrONJ!UXV0fb+N=4*TuW=1DTEt}p%EpFuN7TZmbKcgo zO*1-rAg+D#`&g2?TFGTvZI|3EHDP0-$<$;@Q^%y562Zs7zu&CH2i&T!S-o{= z6KHavwBD#nYhD>)bspoXXsIq49bmBZhXymT4Nf`(O|`EuhkE+=j|=*yt(cCj3cg$E zQ{=hH!@C8WNdXkrxf(R5+uehM{K zbhsBG$FEBFA8`E5TUH(42x5p`I?qPiXr@}z8VB`)7z4ihDf90x@Ih?CuV@YyPv?|j z{A4|#Oqs4*sju5*cf|84&qe_&SOj)YV}a8giiL%)nMh;kx#w#;kpcf@qniPoaBoxQ z(M@PUOW8>+^&{*iqIUZn+}*ekoxZ5bN|8Tn1EmL zUzN<|l$<-EAEXQPkbDZH5pTHZRIJ#-%yT&e9<2{O?^8<@)PgmCWaxD?dMn#(M3u*{ zo){)9JOehKz;Z1{-Fw`V$$Dv__i=S47hhV;Wcg`?1AhsS z1Vzc_uy00N+g1(}nju7O+Z^kfXQenRreh0TXHVwcft<|Q;3hR9AR%l-P*Ai2RH1kxn>4OLCJuTcEr9PKR( zsI0KWSk3seYSeR$zKc#E>l}q&1!j19ucqv=6EDx=_WSta%NI7j(M6Hu7jr@+K^0== z?SVH!7Sx`jj4MA(fp%}w09MKEVP?_?;#8|@S$4w(vfT4dl%t-8YpSL8q6dJzy)@vm zCHq^#7V!WiPTW7)KIQ+pC~}+L!sgSlzlnqYddAZEV<7jrWg@{u0ByGK0H=qW`=1Lv z?wPL1$Ev!#%*K;PuCDAEe(xMJGhF(7c>mEv`X!n?QeSCd)0Hr;1>IJ6RxKXGVi~gZ zDDN0|T{LE`i*TmJ%T_maT$!`{?8xe?4t}osdM@sF6%Drh;%b*`vUg?A0w+|%9zHac zF!Lk>^G@EbGy)f=ZM{-l79;*xm?7`elVKzMAZHg?iZ@p*Dywv6dOQD_+z(eM{FKvi z*Dqh>4Rvj z-16q7+OcOQrpMe*!|tTwg0CuWswmh7Xz8K|-L6v3)Pbs8oLO9ZGPZW%_4pA*5&^4Ye~7EEec z*dYB-dhIr0s7s0Kv${rdnAEASjW@vTdFnnpxl}o}za#D#!M(%12l$X!W02ssDxfdw zWx7tlmg$>|^Q+;HOsgmj)JOf9ej9MV&h)*j!G|@G??U%!ojrdW*XilGya|~m3aCad z7a+%*6#+NXbgyp+BO6y*6jM};Hx$dK)m;MINBkS|YyBFmFvpKaOmPyQe}BS4^Vu6? z+%_k%Vr)~{|7f0%#h%xqat{-b+cCINcbm^!X7eby^HP{C;u3Xq;rd6+=vKED>GRU& zo^!xu#Ngd@2xOXt^T^^=SHU{|d_Vc7PK`|Kz2}}>S|g8T0u%j9Kb>{B;F6~QtH?mq z)B&)+#5~Vev@CgK?eNak&wX*q3@o1v915AM>=T~8Z&>zC`=r81qpB*3mKbm~(UWKX z{;s51P4t@3eQl4baam%XkWZh))@!J2-;jzE1M~OyqKgENJS*nNc(J?%JCCF4=<5G* zbRPax{_h_rN+qkZXAzYZ+3O@l5-MdKGi9BOV;=Jqp|S~?hh&dqAA8Tl!NI|?4^GCh z9fvbMzw`b51^0dKb3d;8dSBP;^?XHJ7A9Nf6FU!VBQ$iDp&Z*Z4XaIx?=MJ{c3W!C z8lC0pAw??gK5}};HTIb4Z)Sm z^(Du#0HcZH!4o*gHoPbRIu~aH`e(mSsi`XwkRMF*5Y12iSj`v!bpbfb&Olc>@U%0& zD}Y@?w&LI7?b=wk3bNDk@UXSA1R{3-ncvT_gh1LRi8J)Cj_IV9;LL*cc1G`&0+5i{ zfl0PMuyuT5MP_0=>9SodEnMius}||{av_io@6Vz2ezVP=t$HQ;o{yCF4Zxl%D4z;> zq~xw#s#Pf9Tg6~*O+!MuFP@dzJCTx2u*EZ@k~`N-chf3dGNcDEZoeX_1T2MbZYI9! znx_G*zeBVid!QO8UieV(Th2pH(|J^h#65Mfq?TE+^3)+ovBYyO?khSMpYwc_=2VWhf4<2XGOznjeQKW^Kk17P1xg-^3LX* zEAGF-?DFwuN=I4W`k$S#oj#&Srax86L=>bYHv5~_?`^s317dupMn+3QoxHmmr+k4Z z4_M1-2W#v1GTLU&|LfUkmBbCoiZ_U`I78LS@J%w`p@Gdk^Y~5d=*#d$Sw5d08 z@&%a!n-}Ge95R^a=M{&}^k&Z_5>y4*ChFFw|4dNR;2YLVJ`?Ur)E4;9e`Jr6o{POz zl|QvUGH1pz7+mA=?Z33#b3~>gCCO5n&hg@>Ha}@!@asoj(ry>xE-!!VAK#A4F0+EZ z+x+A*VTrufV8%^AUz%HHcR$a+_|K$LJ=D zpGRK9ei&?Qxz)F;D;r6T3P(3>Al{8vyi4P1Zumw&`9Qk&^b0@y)xbQiXu9@l(9M(*DSmxj&8)DaEfjB&eDB3Pt4KyaJ*{(olCwx1W%D+P$#b(A0!7 z+%#hliyRmX&Hs+Uh@(7;-i4N@oY+G1!UgkCIM_pDg_(g_BUE53tBOkJi2!)OvcnBj z8sOt0^`_aEWljBJl3@Bgu7nsL2-xTlK_ zHX+0>%>APZG)^2VF@gyTIQ?sOP_ z*~+?1ZwBA}xXzyG_WbbAyI;$g=$BHLH6tO+NTaOtS0CKeXseqGG)p}4MDsGFtaIPO z805Vy%i-dWTG%pe2#hBbMQ8vCkK}!NphZzARoVyrf#9i+V$-TEFZscNP)FtV^@ZRU zC`D^06O@cQO?k}%dHj!)Pa(QQJhOE8z^>edQbJ&tk9fTM$Bt9Wp}=Bx>pCUws5ebi zIjhT!lFBw1aOdYbcq>3~+rPJip8ZV;2-?Y~@ zGQ2sX)1;q8G4T*8sKuH3`nFX5A~FM;hm(89{-PejH6yCW%}}FQQ8t>Z!4LgzaTQ~+ z-|=OyLh&}Mw(tRRrhtx!F_-lE%H>Gzf8rlBEvPu&0&tYBB( z7{X1H<;`x=CIz72&#eq#^PxQMHxE|EVuqz{`Q4o+$hU_bg9INr&ta_-Q~J@Si>%pxdr|o&&bSfLFzpuiO{7Zgm1zub-Rl?WegcE~(!G;qq>@}5yBodFsjz~*#*~Mz3jS= ztnh<>4r15}0tX_bd;%Q8Nw2XMVe>x_)_Qv`JCCf3Ub37{UNjD-$6Fljm7>mfo%R5= zMZ<=z9fN0KA$(v$kO4)-o%HNB`<&a%u$SO7^)yXwVP&kD-9!cB&+k{3@gq29n}9$iR3f`;CjOl4{k*YinNGBi`Vr zMbAI9CU<36)9fl%k7Q0H=dYbGdNMVxCTC75dQG;O(Cn5Pq{rVBRgaIr{{98gz_usA zug|!lvuiSnSFZ44Ca)dYps{Y^qokK^e#0iU)W0PArm)#vK#;FP66*nlj1@uu+@@a6 zeBId4h$6?TyVY7m2RTmNz^l5`X>y;j9&g72UZX~u7tE0m>3#{W&P6wEvS_H1Keir> z{*wK}!NF2m!tqpIapbXVL-)IByU0<2jqdr4Q%0PW{+iE!bmB!#2SV8)0$i_bY=>Ej zXTfWBSm90`0r~-*ftzP{_NR{hZ&EL92>9N$}&&~E!a?ec?W z?^aRfZW8p;vhv=vnA4x_9|;iTh^VtHqPUgNH6?xQ;#|PDF1e82)@`5+?c;TADmC>? zPAcJg$uFyw#Lt}-^@nJCp726ut$`ATYas(BX6_o?0i0Pk%!k4~K-6T#zG05UMgcnc zM|Woj0QUsTSdQD?&EC4cJ@;N^NBZRP+6j-HT$cZ4f8bk_SiSo$Pu1%~6c>VrfBec< z@5W0^`Dt;}e0QrZD{Z%@w|M^YMdA5r`bVbf4LXa+vV%i)$NM{54=hh6-xpIxnOTYi zWj_O|>Z3`TPQB-R#Hbh4VPXk3M7JLYoKyhPf_YXpq7Qxm2$3-E)XuGQ8L*VU^J9Ay z-S@t_hQ_F#cDgU4GDQeV0pMe_?uCEFN@_^FN!jhC6#Eo$&8w708u)K|PkFlc$TmH7K=+ z4V4HA`o?wd8aQ3dLR|hvo>SphU*H3goQpKAWCWL)cKnQXtdOe$lq#)-`+-^}+8&@u57KyM{waXUaWq9|C&T6GWEQj4_0)I`Zgj(< zz z^r#L%FW#qQYuy4REMBftr{qq%qv#1-TQ3Q4Q%{{zSEHET{5#ke!}N2 zqs!_8`!@IT5gR{Z^U0^6hM>uYx%aiy5m+tD;4?S-NvQo^L_@=2Z^42u3%4%uM>pXq zRh-?mfeiIhpt9FL8*w5S-z@LuY7T#MP)c76^r-h#fX*=igQft7%QGm4^RwKX*1c#@ zGJCrOQkHV}`<2qTb4@n4gZK!Af2~?9lL)jdHVDdxSuie!I&y%-zU`aJZv}j;v35f) zO0o;=sg5^}+t;1rOUZ{+nvNt6=IagEuTsD^v{d{maVsL)gK^x@oJJI`}{Cg8<$zWia5NE z>BLC^PL$C-+L%d$C1>i4wg$MEy-&z434Te*Y!{uI`^vrQ5kl7oANm=Nl|cCBX1F@&*9_AkWC zjA=vcJ#3t094^Gae|*HS4_`j^){d>+Tx8uB$~BKURx|w~kmZnitIfy@ba-uKU2D-b zt!^VZ`T5HjQ+Pq)LNoqQEBeC0x7TK^QXO~PDm!jW9?t)z-N!1EU&7-Nvu6=Eh_Zc;F%pMBZiN`~1W3h;};p`Q=sU z)lGBt+L*hKZ`vIk1_rm4g$}Hk^=yMIel-vSmYwYzhngNh%=8@x#_Jdow0;LB+dlke zZtI_7WBC!520IfrHe5|fj;+9LuAdUr;Rt8$+YrZERPWgPki8w!-;hV7R^M{Tp9)uO z?id7N0-6pbswevnaS2bpz5$1M(NHNM3$hgPt#>jF*AZp%6Yw3|nZwEI$!dhny$w(+}`nHfDi|K!@AI3(O;VnX7GJMy^>C*?Jm2oiw8G zdKh5!Ja;iJEAYUg#9k_=!@mf2vgR*C%UEj9c?!TwKi`!T9KlCWehB~TX3<@MLp1sQ zDn{jU%yJ-Q94@BN?L3rE%k4%6u3Yo^EF6Q!Wv?Jl^|zZEiu+4=ZVq!I_lrZVN%QK4 z$)4+8R~!W{Ehhy!CRsCgdI#JFFd8sQdU<D=`gmku-P({T{oyjN=|-$g=zt#J7BOpQSLwTKo za$ICKR$`NU5$UY2=~Eln8@@$hGj1cEmRCIHjrsfwRi4W=bNkA1o0OBl+J)b^oJ&`* zJera+M=MU@*Mx89Ku3Q1*sNMfyeaHUTCE4j&vGwB)>WGYibIOpIX)8rULT za?liB)&yNHrsRiyZ>*{WbLJL(xV#p|Nhh3ees!ppD7#-a{5R)1BD_d)HU6t;$3Fhx zvLuE_B(7Ho^coPhbV(+`ZND;)ZFXN5DIEMZwh~^WYAZXC?|j_2a?EcXnjJew{2>e2T@OAyE&-r z^>5$752EWT8sBkPqUe5T|1Ol6K&=$NCney_`H{nfEA zLGzcUdeniUE!<^ABUy9;7W*-ol3oi#17b(Wqw+JUcNydM@kTX~5NYUmm3Pj7hi2fo zL>D|F(I#NJiny^#gF~O8{{MonxPs$a6cIsxAPF zWsLoOZ6&Bu{q33buSjMUnk4C*$L*;3val$61Jy!-@^u94$1i8;oO%X7$uf2$c+_Ho z-w}U3veTkYAJFp*k$xx6Y}D@3@@MZf>ldXWS1-18XT=2T6QeR?Tdy?}!#!1YwcIJL z>n0w4lw;eCJtA+hVp)={GVN|+lpNP3K^y4c#JNX)&qoqjFy)7y1Rr9{IQvkp!>vu0spklWjhu|W*{qcMlSsHQs}Fgcg^V2p~XA3W=PK1xs?C;hTnhoCGoeLZL_}+@DqQ@^3iJ=VAwz0;VbRB z%o3|+*>pJn=##fBIl8Yh8DjRm<6y5*gwXvL;Q7>p_+DTvTJ=lUYM2FF>qVO5JDQ0K zJQnEv9HKlmdlm=%ak3M;|;dcJjP?PMc)9@M=OXMd*r~SA5*7bW&lyV3}lGh+5KkS5rjP zTlq-O9j{^cC-l|D<}e}I(;5~u^4CvzK_1T+5Q5R*$|)~*k%=3<+s)Ftn(JQ^4?dyZ16BzrjWOLHiGnz-B}Hp?T@Mb@4YLmaELk zZZtgh$zt|uMDuH%>&S|#4=+wsnX=Y|Kz~0{`Ls)+&IYu91sBUmTT43aHB7e3mYG|? z@H&xP7wcFpf=J)1CAtlUe?0t;uG?)@{qA+N5^+p1Xd9C}G{l_y9xA7QXTlJOc2oNZ z9vWCdw}=%Y8{NjLtqw zRik+!vFKVO#sgeVba>{D*)g&JZd5qXK8q!$rvDogcf z>fEQw2lT5Pmmya|-c4)htS<3jj4naOg5UgFvvWw@C)^By2yT6Nu3Vc;f~A#!fB4(N z^0o4EVAPOK85tN72YQ|Uo){(m{txf1DPt@x_%M7 zVMyZa5EM2RSvy_}-_qW+z1#j7KJ4+eU2G(Xh2SP#sc=+gE5gVzKz?S#t@R(>w{4S- zFq)_!V7>03btYUX4HyZ3C-Y3qK8kR8l$Cxh@cql;K54;npBEX!?cj;3kk2AE~+jK#o?;n!T% z0+?W}X1%O=k!oSsxNX)fu^i;mK8Bvu@*qSGZ?>Z_U25yKV_Dv!j4&e#Tkx~6-e6QV z^+(qprlN)CJnRWdSc*zmZl8Zig91{YS_7w3D5<&n(*A1uErVC@Q|s%kRP% zzv7-12C3AG@_z+6Ida~Nea@$RsR3g&fR?amG(Ka4xX+*AqQ5E%-i;JsOXp9ubr>o{ z%6{>U_H79_ihACW5CwC3exR&!YI8;k0bSxDyeFW&;mNx3yX#~32 z8aRZHDsA2OA!j+zagVA<1ulkn!30->zJ54xOlCFvOalni;QKuLD-IedmBDIfyQBKwUFbjngX3K+V56J&d+TzT1G#uAt^nvF~6$f4MW(mYVE4@ zK_c_OSiu{;euqRSYbNuiW+2D~@80#AM?jI9Jyz0%Yusp!-0SM#y^U(cba2dc8=uS0 zN#lKAURJMpMqS*Dq%8v=qzSzhz_-*w7As=>bJ(C-0N-b86Deb>T{Lh5n-xxjq8MLo zT9Nqqv`L|UvlM^I`MTUzX;bXj667#apj=Hf=|(7fhleQl3(rZ~PJXkJ9)*I0V=ohN z>lepxlzBh6*8bB;gk@C3^fL_-L~$wNo4>#%xhB@cU(T=l7d`&a$LX_2o`LVN944`S zSVzi!BGbhOvGn!l9|#k^)^SU^n~u)^`;V?cA_i!Jx#bWgM{H2y6^rOPO~32!c>ga3A^6@w zUmeq0yGeeqHt|FGCFDN#=@cfXM61A+Eib`bp-Ev_C8TfNlEjh(M?UTTQyVY{ZA(2eM*zm$HUw^QOqO?^Wh zZOCAMSqi?*OBO8=pQ0*>^1?ritsT;$dIteT$3kF)b-I!h~3z4Y=QrhJ!?$8~p*XN$~ zp0O$s^2&OW?`?8Ds>q~>qMmkg`g8TygrxafN3fx!*c}yFxYL@pJtj=C zvc%OIvJ`E@+_}SATlRNxO>V&QDBZn?`-59SBz@TeWXK)&6DL_l)WiBEFd5N|icg%1 zGK{@*nx)?f0+y9`Y8k8yIa3fjnfrA-?qCgr)Z<&)y&HV*d9vF9smK+w46@ z)w5j>$v4h?8r?S+6*)4}M&zDWb}~L_D6DD73EhFto{JafoqIf{pO*u^lfjwJc|Ql9 zXN`rPz<*V50oeEUf}gHLM-LX!7maJl-|$fEQl5s>s^4jkp!kKGl#SM}a@uHTm1R?@ zP*Ic{S^40c8HRt-a)9Vzy9lY(=2~l>8_FA59#Xvh05EoSAy{p4MyTCsq=yfSSKO3& zKWW!)u@{Unt9{5ps6h<$tMMjUu$`9=|CU*y>^~gjWQ$n`c`yk+p0ci3C3?fnjX$Pe z*UU*-h4V?1?e)}WH7qa08wk)n1>ZNk`&6r=G$e#g7v9FBe-^4A3fGwoW`in|C2Yh} z)d#QL07BIM&25Q26u!H)DY6is{^QKm_#NFb90?`fpKc2&)+b350}Y-S$g+X}+?76d z@=PjDJycA6|$n})-{JEE{VZ7+OPft8%+!p8pD?^J7y`*cGMT_TRk;! z=Qk!lk7_Uxn%evj=pcjcGAf{Xq0`6=X^Th!FR zZQ3wRWTfv7M=sI@(Mkh}?ZtlD_h8Itf9aGTBd+dWo0=H2u*`_2E>iu0ZI=hfHv7K} z@l3@9d@mOv)9q6WPd(i+Q`KsF)%c0QYy}`7VEuZd_%`K1;wE(v-dB6|XOo%hgXbi- zAll+2tbQ6dZI>7MCNJN0xIHtps1ZPG-}!-O94<%Z^O5ycs@%T@fr+ui;6sS27I|t! zvb$Zbu2Bxu>|XAr^|zvkq4EOjvrturWK@<7*@tJqZ3Ums8QrJarsGY% zh)qOe9QxkHn>WOHgPlsxXmHJ6@Hw9fqXDrPhJQhR11r{7#o?UQ4|(lUJ!NWU-@YAC zPjBDCR)SDuWoQXVNVE;rQKMM=(f+Jb=CYC!7v=QA5(HZru+j1lW>hQI))!k#pS57) zB{%BN(1kptIqjUduzv0}gj?wXQ; z-I}a-X&sTm&s(3u+@vYjwqMHwI4j0&M-`J^;7EK0FE;hPq9i#rP?rFQ{&dSwRT)NW7=`w2538UJ0wkd zNfEzP^bf3@s>@655p1<}7LyrMb$mxhCv=4d&OX-n$Fc1c=OdI;1?FdPFG%)`pA(Fw zG}UQdLA9tzGDv!(LOgsG&XRqv9{ zEc|LXT<$~dD9eTtG7N{eFd;mFlS%uGb-smZtnrFq95=M zs^;*J&&}6%D)!+@bfdPiW!zT@F`%&6mJU<3zbJj1@r$)_t_bzBhXw(^D%s`?t46R^ z$%&uJy^4x#yuUBBw(J|V<;vvK&Q=yAILPObK z;Orsu%H)}H0HcrC@>&dTe)2SwC*!Vt#o)c z5DtykMAKbcwOz9?tP0mOmcpjGEO3g2d7Ese@@c%Y|9Oj~s}vO6rj0a`{M3X}aQ^jE zm_wV9$XL=Oy3^^)V~-Zm@_nofZG%qLQCnlq z3P1{#9m_&#bf|a1LnXiw{tQ;o82X5*~|8{qrQgo(oWZD^~Be~DNXZH-XDTK9I}u9;M&=j}BdFaaVT~d93u>!w3Xy zYP(2?_YHj4Jn8fxEAoNmT7#Y{f6#O`+0rMm=VJU<97$_+dKyPz+o2d?x0bM0bXC(I z9gaSHp^Y=He8kex#_&mk#(+&Z9(zHYpe~yT1)8mLchTHsB~E(xNa* zc#~T!?8?8;kF;~tbV>O}hB^83HK9;w=+kMIArUVo2XpZD!zcE&N#2ZNUS?-un&%;! zca=>H1Vwd=;>(35yaH--6ke zE3+9V#Y@$s9;%T4IU=HVRFuWS3=u7EyrNn;_0Z=N3ZT1tPR*10hi=CteY{(}rqRSG zzOH;TKC~?6A;%HN7*3PL&9!+n=KX^{t+HQTPzcwN5vtR~Pk#U~tG8CI6^|meOfiS( z^nf={*$wh->%?4@c@VY9czEO9%v|#O1S5VIUR9(GDDR!yp4;K^nE>9xdG{O&JInqM zm%dEIg&ThviLYn=WsvroH9f||-NOG=(^xYQZ{_?jngMJViX=D?TAXufD2(`Mk%y^t zzDU>U{opyDG{9_)3!i(sf_Y0r^CT_OiT5YF?v@)OMZX4D>c#ueB-D!JRg@r46Q91kc|T@@-sy z5iFt=@r*7|D5w7M;@+$879A5B z(%}?UV`pUl9rx&#{vcgq%7>MJO5V!l@eLNhAHzz|wn+J#=e~5L_sh=J);EPW$8f=w zi}+DdgXRbG{4GEEPuHW)AyX-5>QBCviG;vK2gF&Sm%bwb#UV7L^ zAN-qYR^zA8?W2E+@6(zP3g5w-JDW9ZZA)mLibLm`I;r^N7ud*oOZIDX{MU$F~| zix^kw%D4Vbkv3}YYTi$M%-C9-t>>SyYidME%V>>4v9$YijmJwrtx>%sb*!mb=FmeG z7=u%Lr0bf?Y?8F=m7orc_hz)2(yRv{LQ=_~cs=sE(G_@gE&59W9`s0D8N$=1n-An6+MFxT`NRb@@h?aD%zsH6c}`YQ=E8 zkdD)up2x~8IUO?;DM_mfG7##pOQze0sRn}`WxAGh9VsK5PH z!|I9AiE{qR9q;WpkP(og zh3{Q9>2HfR-ptV{ zFh5(m+|HBvGdDvTfJNY0r85D?T$I9-S>G4Q7`3P2)Q{>J2g0Y&_}31qiF?gwew>;oXh4&oUh~RBnj;AN{XK?NXs*bGbG;?jCVD z3eKH$l-2Phxv6|?qOGR2tKYfibjr6U%Bg%sk-j7=0h^GP7s*}CbuNt|Qvt^Y7j4ZR zU$QU)yzjCpymaGrv)|G><>672;t85_m$DFV5^tM|USaDgelo0(;SsJO?BpW@sM))) z_)oZ+sjD&jQmFGD>g|-`x6yz{<*b)o{lneiz6%mq-NX%J4I2?k29g#Ktu?`QcJivQ z&c>1sk~9jvG$TGf==}V|idFqNdbMdBty33yS%ijDa4fD`xZ6rwtq5NY1}q2FGzrTn zRNTJ!!siyJ=i|>mZbj+VjVrP=V`Tk1|Ix*zy>3>n8Yv?xzi{wDKR$b&ZKiA2 z%P>|V@z2=xM3&piIp;Ppk#ra0zN)QMvH}who#F;7HdEsC>jVu5dPGEp%WEg1wd2?? z-kmqBJ;DlPl@2EB{W41%3{s7#F>B2qlnWM*JD21HQ^;j&zJIa~CCBrpUjXWrckmt| zpquD140WRUrx3V~WStZ`*db&er@7SKMJP=iHR>Ab^LWhSRe`=^OHowZ`tUWaHPg%x znyjK}3jP%=O!-;i{zV??M3NwDoe81wsrStYF5|voh~HR{R9r69_v=4UuLmtc?pu7X z!P8U%Lr^|(;{1I2XdSgHF@6k~3x9=DyvrUk@SxMdb`^8QDEjj@a}c;Zn3)2rtXEl1 zT7}M*^|Z`{@h#Htt9PA?G1>nsBanya-{%I`cPnH%z%#~DT<1&iA0V=P z)+$TZu7v4N=7m4?@QSN!7js{*rTaN2ef~Vo<6~m7Wf_uMy`TTGv3)W($(hV_z}<^x z_NC=h--(DTtJsb%xZWAQaSeFXXqo&8{MZibW^&?A3)OUS!#3%K$p+Ut9{sbZ&^4V4eIW#pt`Y z>_=n1(09#7h+IQYYRMjaoC|?PNV~1@*0`x1&%@G{-Dmz9Ll-Yx*@b8v*j}a1+DJ{3 zgAPChpUHZKuOZ

xih^LEf;sY{05=oHz*AMcSj}@!y#Ia;Z(@;{EF0=N?W*HSGxM z&5BKUQP9rYz{>sg*tz!15ZLyS)*ul8x?EKg&hp9pJyw+QL@W2?1l~$!S&^0p{*?c< zda1sz_pfqz7C0PTDMvKxrb)iF?VVVSvyoK{5*kUGRM?+(_?VW&*7?)6*qrn{=Y(Z9 zWxk01V#6T~;pFEk4oe`{oOrA z8LK=rObO9XlAr&zTcti%%u3_C9^#N-2&fVz;}a`1!J>Ar{9UzD;{zbMGdXTYRWp#P zc=URaY^=qfW;;crrx;YfbN?}i8FC3C2*ljy)pEp=mXuXB&|DeQZBSAfB@pyaYkYcR zd!3e4q_+T1Ec?9S<(28TI7%Q(Po84Gy;`@*(3kenL&vVlvg#?w4VO441OOgt7rR=% zP5kK3bM}+k5&N>`03G)0Q^>E~KcpQTMa1&sr22bCK^`2>fN(@`dn(sw4N@6KK9G{b z49^rQ(sT_PwpP=+omSj+V;(209S4GK`6#;@A<5s*dl$#`g=nrgfjUT~G@iSAu4rbX zR^h#sCuH+AaB)XIqZ2;1Ge*wFXfNitv~E=-?*MCY5n$`fFElDuZA!J&1Yxy?HHNx z>M!p{TX}N$?Q2$d;w>{|dqr1<;dtr0F86|d)YGb3Y)ShiWIQ4f$yB)n=ftUfX}%c> zoe`gixfpN>$IG+kP_=0j0;Xg*z8Fn;ckNjpascTg-tcFEmJoY4sixY@BOYQnYc z_b^uW?V$Ce<&$`x6V9WXsS`Y3s)sz}2I%I)=3_`3xnci+M^cUKk=JxD---h=`Z6Ad zJgZFk)YYP>T;!1?v8^bSYI<}gY=nrFmXVc6>du(6}SEs%%^?4f8r7T0v(#gEV0O8(1&Z{!2*iA#@$f|4jo7GkHAx zuYPyv477<`W8;*r`*_YeO0f5|e`tRBB0{y3p-AAEMvXEp4{SB9uH!oO^NbEp27@Mr zh{vvHMbzv1kB}JgWjhB!&8jxZ$4j;~sq={eqyscw+1CCly=v!xhS?jhKVG2IZ#I~E z^oeSYjJ~EUMRZAFkKb}eACO(#qvXmX1ZiT4fWIYw^!`0EPo30(Y${eDQ-+!vT3ojv z`+Uy$T!UNrw0}K-)Dzn2smicUmgG*ca|kk2>M#h%7~uVuYzgu>+^*hMvXLCS_Lx@z z6Lc?^_6k44!Wjho6@wuI-5rhyU#9{@;3Vln@nLnE1^_gbH3I>{#Me{7 z+iYwzeE}Y$k7)5&qD(wHOa;Dvw%k71UqWD}HSQ2ZbJqfOS2Icwgrhn5pf_{O65r|L zJSUDDM)h&9HZxi<@wFsqk#jqLV;WwCq(^Wp`VeSVj3mg-nF+Oe8!lI|Sy;o{&cgci zKUSW93>?Ark@VlI)U>rVM4|wjX0&aip@ zn{ahpY2&P!Qx(u1XqM0ghuFTPOi@3x{ILBn)YLGg2)eZBsjZGt?^wj4Gs?iS5f$1T zcX}V3eB7!CmK#`{4|D)bCk-EB6#ddFS_1MYPB%i_pvFeHW*~8()9e80`3|j+tNNEJ zGI7E`T)AACWds{&k_j;odO@&J*nf%?c0Lf}0k|MYZ&OoZ!sFfsKN{fOQ5Jp#>JRP8 zPx&&;LLMIlyMZqvFwzfE8=BhZ0`V2Xv-{6KJ+9TcG$teGQgS1_^J-po451%zwa?yD z^9F+aKoHl$rw?3S{tiA027mqs@VjG`AM{4J@AM^$Xpoiu5?n zO!hC`)IIS8*J^eWDjJt`>xCW>lb09XSTT4?=T0e(pQb1ng0=R)WvCD>+$IfSn(az5A=A!VGsiEHuHUH58Zok#{Nu_q6 zPv%egm$)I7K=mWH!WoF^B9`=g#u~|*UJhz?v1~%6OBu^$Q*Duxf)!bxtz5>AC5!Q2 z{8LYrPYJM1zJ@&Ual%0tkmS3<&9F=V`4q&u7-H1@kT7DjxI8a_31HP`yhqo9Rqd%7 z+H5?Z;#D-huj6~KI*f45Y3CI1anw`(&9Mp+_x*O>MdzoR)BmFGIb+5#g>72TPDX?u z<;>0Wfr5U|X%k+);M2+^JatrAhY72wY|jf!%)3}Bmd(2Y4kSsg7$>A=CORj+b7Nihb$VYI5^xg zjXR`16f_7fK6Z0kTRO|BcDh$r{ori+#@VUek^kq9X>pizvLjTo~)s z7|wIBRZV^y_7or%_^L!ux}zxX7#&3p_)viK(3~`x*LEO~AIa26_XFz9gz^~}FyGsX z$$I;hWdx}DtEkLM>0RazjPi&fJpZ4Z4tyYZ_FY z48sb3T9(};z8>I?3Pvt`y%o8Xqoeg~RNtmN@Bm$J(J)@e@6xX*=c2w`&dn{sE|k9w z+qSH6A*5&i$$Xx5T`s!(#sCiw?(_NNeff#Qnj1gN4666_SB4zI-1vfo)p;5}Z8jJD zWXcjKK8tE3tG}hGwk~g@PuUHNi1A&kmu1>Vyo%nCIEiPEh75tpn}h*%C?oA4jP3!9lU1$k+Y*xu}O~{U;ve?VJiycwg-pU2-+gZm#R}P}mH4a?^U+l-x zaDrcFmwoK~5|NcEt7Y3-ms`-sN3ffM_X*d%DKKzB)#j+Ss-5?sM$XA}Nc-J*a)9v_ z9G=c{4JW}P7P2#iaeMTH?oa}OB$I)5LO~qp3SY4#)0B$!%`Jl#OZ?HH21cJKTRCTC zKH`Q|kKR`IlBvXxrWM}s4uiP1L}x|Eh}bceG7N;OnN`SC zdk8_Nm*cKR@bZkRoljOn;4et;+2{2h^5D7y%3d}Aj5DGnFI=KFiqI2xgIj`qACv_DunAPk@ zDQdO-HRSUj9S<3?CrEu0U_6?x^R>Rh>FPp87%`5gm)On%ub+5CgBj*zd-wq{F#GM( zD&KWcK-)jEVO0;_Thr+h&g;_2KF`*NpxeLkJE_Tfu3r1G?KgI{{h2iD)_VmlN#9iN zxyV&-nTHH2ELgF7{gENNENa#~b*B^-%t4j)fH478)h6>e*{Jq~9H*!O)oQm=rw-1& zypE2?lmI5Kn~b+4i^?n9^xRHz_}=;UHe~HJo{IfP_YfK3e7hZWR5s4u+twd^tZ%%l zy}Wa0{#=)&i`VP*@4(_z;Bo(WeR*#AanSGghVSJs(uUeKAoO^Qpzo4+R>1|_Zadg$ z+)JzJ=G9=1+mJpp7l_v847uH#(DfmDxU-olE;7>aNz3eDUz=~BGCI$X{vqheT9HPV!xLt% z4^MUNiiK9ie$)3uIW_x4;g>t7BJk=XZR?R{^GiaqX6Tn&KuUgC^2AzWs~$m8o8Y@k zf6gYL7dxCsaw87aPncb!ucc0r*w_TrIAU8ebM)6*iczj@Rlndyl!>DM=mfJ{ILd!5 zr5OniN>bUVLj7NK2#4Jo+%$MTpc}{K)gCRxOc0I;;u>6N1HPG}3OR-LZ5`_Wo~&~b zG1)Q;VDPi2q!c(VHh)92(51WqfpY2r9g-H3J=KE*mpVU%c6L%%A2R}P+|%?L6M>K? z9oU9xe-D@2@}G8g6|ji>%l6(^E!e#P>9b<-{++7#IYbv1+Oq?GP< zsPpeDIP%8qKTlDvo*o9Z#|g!t|iRdmNuq zALL_kX1y$a2h+aj(wcKTd&aixu&aKqM8bAiY9x2%aLWtAdNuJ8PW)D1Txc>h3;9#c zgp2~aEpe7KNl-PqMWcOLxW(HS%}FiYjiS38tjg2#){n~wRm3PtrkZ=z#ylTBtzViI zDsH==9<={{QUb5jdz6lSldS)*U>thO*A+p>v`?P9mCO8C9|8RAETpz3%jkHB;Uu0 z{lp@`$ycOjW_tLspkSe6qnn?q(olA5>A6M>O!{5C+~!c_>APJTOmbuA*g|2JFraY{ zszT6CDR;v3_eutG1ND`<4>udq6T01EPH5q|0X$JhZ%a*z@(Lh)h)!Xm$b>7iZ;7yk zvH}6!$0-Q0wIYQt2pV>!=R@}TyA>ar;`A4@BzvZ8zE14HFZ4-+pZ)&fFe)KckbFn? zqX*0c8D8gCwIe<$4`UR3wY2~F+~^yAw8{J6#yLmd*#1tvVYRt+vnySR{a}BQM0hKc zU4u*M#@UVATyA&f*Sl3>SzX(@mA>45@gt!5cH;*l7KWNEsJbGq=6kZf_-Mm-bMZR2 z+5oe8`}c)W%`S6Wjk-?hz9^OXoQ&~2NZe6)@s-IX+Q2b7i&e@?5LbrI!k;A1B|6O5-m_S?Er$uojeqs3>o=4%& zS#0b`;88#{$^0w=rT4bpnMax6(a{R9$n>bjefwgz#Ae{TC#(3s5Zl4hhMd~Cyf`>WTNh<4le|XLc`wHC($@kDw zRkFJDHx$eAY+o})F@S9~y_@-a?QTB6AU>oYmZ>l2^5kzgF$4y&@!h$*4_n zS2Wh8(v-}KheV_bgjh?|YuckKf^Ki}Gir%uP#%L-y6F&IiZ=;-=LLQ&Y;~C78{V7B ze*S3`c*dLrjp?(D9344-4`rClzrNuQ@8RT3a_eXy^mpO-&72}V_Kf&_!@RO|0G#pk z$?smwO?C|On(^#{@Z2xU(S|J~G<%n#%t9?m(l3yE!Tr23qv!tGLmj>+(_6U&D~p_n z_DhSXx)wkkIIZy)dY9+JnPyJ{-L`S)Koa{=yclXIU`a>)q|tC_jwHMo{(XwLod;&V z*1pLfj*jokk-)ll%5B7zus>fpV!Rw{cWLBv|7EcA%^Zb_b0igA`Go>|tgq?fSS7x? z0x71HP#O4cY^JaGlIS%zxev(~&~{?kbhb=?GXUx}kYzQ)LP|b@b+(@+Yf1C#H)Qg+ zASS4`ShmN5X1&XCaUBu@05#*^It#E!kEiZ8-po@X=yPflzMc(R&RZ_4M&LM^e*?ND zwvDTx3=5K3j9U$~I)d=rKNHf@-^p($EmF@{E#jTom!dL5;SeFyTFz zJy?lIb5n#W^igfPg;qS;*xNMiQcVn1c0iL6(2q zW||RI6`#__H5=x!^qwF%fbdCAb>Si}f2`iZtv}wUPZ^i#;_K4dIm=yK+!Q2{H%Ok{ z#l9I9kAMW<1pcng&ziLpdif-X=w)S7!tng1 zka35wfI%c>&F^8DGLkW5`WutK^j=Ip?kOd!fQ0TGo1dMF4DEkTidmX^V6u=}%Y!1p z6j>NEHN^t=JYVd0<5+0qoYjqKD|sFr6-UCSic%}#e8q?&R~?HX;CZ()54Fn9m&#;C z=3~PG&b%CMx+MVu`w%*rue@M7;! z0KXz3@g4_5k18Ov08Ht>a8RgQsOGg}Myixck|2AZ-(zetl5Ux6Gtp6qzq$btu>cw~ ziFwd$@uktsz9`=}aK?6G8BfiFeVlv1JklS`Q`ejpR_Dgij+OGt`I>~C#emS++7-p5 z?OuQT$|r-mL!c74PV8dzjgG;0kMFJU-F!zG(QBmL8Eu1dj<(PC^WbQ?GZ&iaL00x) z!!wy|M`CqeQnmV;EfO)$d|?`iH(tJ|fLw#yQuYshXKl}O5I2xI7@Aj0yh$hNQJTfkYq!C4{mFZA;Ry^{wb;vIu^$4D~+*%-;e*JgIE7Yx9V(#`LSyy zGqus$+8o$x(8sTV0_n03I@Nr>jS)lcS4>f6I;>WqIe@^Mh@bdZ* z*DkEp#J=VH$y=179#l&ZZkKM$BJ6EC@OZGYrvJIy)!C<%47a5i=E(}OvNt{8%2xBz z?2MDfjeqU+YICx>p;kM+O;e_|2)h!&t!*rK@`4funGt16$ z@CFxM37XA+j`mswYAbhKnjh*8UH)cwM(g-UbBduYm*_2up2^?9DzB>i8J|Ok(MV!2 z3CMXJ`ewW${_!~=m@4nuWYx^U(xt&{ppidSFG?cdoz#C>#iYMxs!phbuq~ScYkKJG z623eZT{pKaw%NAqE&_je>oiuGMo{{Y@uI##1yLf{pK$H0OI3E)(pi3$!18rq;=z}o z#p&3pjNQ z7vEp}N7pha;j&hRI!~u^qdYz+3GJ?KKnrMTONh(Z7(bn!EJa7ovp3Bs+E_L3y@NYd z_DeK<-{S#|<%SCQ?$vXR7lEH>PqU9FeF)rg)kg%c)CXo828)Xg6ls%!ZiJ96(o<*4WfEjdVNxM|UNNYkCG4symzROO_IRbs9M`VG@QDL1$i+m2ue@ zuS2^ci3VldImM!oeqF_H7hot^k@om2re8baxcnYp%wqaV8&>&N51}8?+5M{$od{l%;B)rpmsCSd)D~VI4C*l+!!FV-uYMPe9 zVn+}>dIR;u&;3AR)J6bZFfQpEIeQtJq$hrZ$Prw{Cnp~j`&pD$0S?l2#z{)EWNDq! ztP@q6v%In1bwYeNAFobd`&YYj_8WHbuFzg-&aj<;HjoOx&<& zU7!ycU?}`Z^1PdD9R9q$uj=3a@F{$HW-Z0{O~QS>oio4u+nSi`M91j8wZqqxJTjl{ zL)XS;IS}l|c-KO(E{K72@jeH$))nT@FKJc+YAEkU=5c(I6kZ`Qaq0J=Jq{cjD z;8q8CbK&29?#0MIaTtxPrp&xip(K>1{I#liiOp?gJA5E z+lAwsi1oYe^{}osg*&OZCEDM*~v~pF% zeL8n7m5Te`PLPz4urjYN8Fp_}Mqa2l>>LsA?fgwZ$1Y%acBWOb;^GDvLmJ=11b27* zD_Oj^?$b)g%2S*+kp>5rIfE1s)`U9QcN7XhqsXz_08G_lU7E&=_5PeNpF^5^w_7uz z_v@R-w<5dA%SA8GqBr(5LBbKn_bzMVQSG&}jI?rMtsUHreKRzl*fkB|MIbM6IX1el zm3C8zzxByPmJBF;*f=A-J^|H%TbfCRVTEqV#ARObFt_&5w+BA0npST$_J>DTbWBW* zudTl(Ao4G^Siai1w=>C`f|`8#ohcf1vq^#Z9Ot-z(gCxPL9!ZZ7=5rT(F7sXbht+uB0eIf5r z=lI<@ttX}pss+~@OZqu6s*BG5Xs;T+4RP6AU-tj8#6A2CI?c5t--M5Wer3&WyK;C+ zLCqDYB}r^0aYY)J@FM1Es_4}yc`l~m%Q9=0FNZl^Vq5W%Jp!cuX{a#arU@35vq=kX zxHb9B;sKv(Yd^2r&6TizG4Em*wY&e!oN6qPOT#oTCU)2AQ+2Zs^{?h!;PdS!+r$BE zcDoFu7%G>DC3(|+6Ff1ny@>)HHtn;qck&@?%DZf8);D$xBD&nDw@4iK`pJP$C1Rm6 z|B}bPPhf%X4EZ|l7lei8=N<)$5-TVf*VIM`fW@;>G$SQjn2%%{Vs3XxY4t7U8>L6) zjX=yXOskW9#bRxQw*!AqX|c)Wg{l$)gD^&g#jS?Gk?y#-bHgzGx~OK`BO&7a8k6US^u-Cu5Z+v!7I z+JrJKUncyfV8lqke478^#LPTM`^Rw0=%2>PovMcH)2TWinS6CP}MpXDJ=gXm*AdiSMaG~b*3V=ATRCuF;vQ0d&LjOIxr|ATXFuV!xO^93{ zU|}S`z*o;cqD-MfB$O9|1H<1Y5GzP9u#_gj2g>12+se)t~?ZZ0Yi@dc163zTo<<1R>S2K)OKABFStPBqk^J&`DJ7CI@(t zE)x>d;cFK0vkY*VpSG)17zn()8fCQ;K}iz+U>r;CaUF|zy7I$s6>^{tCIa!9nE2$P z8ImV(>^Kha#&4IVM+x1Z@B-`#%qXx-ntYWxwzVm0Bo@Slt(J(bz{L%mb81ps79b+6 zjRdz{<;4sEfr8Mr09Z`iCA0FLf)~tjV2PjC5g7&HeXFo)5p#0=(Keg5{OpZ_wC71P zzO9pIzG^FLtAEi*uYEUh)Y<;7&|UmO%%WR({U5WnJ}XZ0Z4z=x`W;(Lpg}J!D55SQ zGzb40ls@e9Stn;ny=NT+wx*q1FXk;}X}~T+rAdzpXS7OTt`Ky$ie+BD8HS~;6#?TS z8MRszTR!Jm6BnBpptJv;M|XkUZzLyJ0bFFoIY&026d%JltsK+%MaC0$Y5mP;f3D-4 zFnQ!P6H2HRYd96zHcq~`m^)U>WV5*adFS~%-ebc>+j$(sV9PCQNC-ZlgBgw&?Nw15r>gD%xUAfMuf=|Rg)IXSpd8U@H^2+?y#aTudq}0!! z;Phs$^C43XwNfqLA<`_&jaS#vfIDpy3hZv(nCE6b85LVe5Ll;!(S)lAs$O!8IaYHC zRBUJemW5B#EAU>BfW?UH?yYmB={onG2HkUWgE+6t;2UOXJIj}2r2EKza#b_y)+WJ4 z+{&j0>Ys!|s4V!BXcx`?_PP&=GWiZ3Y3X%V!u7w==^A;-#nS_4M!A}DACaxlo;_M> z$YOga&F4_ZDM<2MdXG8j%@Np6|CF;tQ5gb61>GOh5Q7BL6shpzgo`*ca}yGR%P-GD z_FZh*y~}ZzQo8Rdd`=Zg(H%5+=lUqxyn(c|2#e7(pHdqv@z@6YIhB)lntcx*vr!%y zI_mG073njW$AYH1&|>?V2UjF0`M5UYe@C3x=CtgqhI6^&WQ_J)>WkCWHMRjL?>D7= zCZijPlM}91KOzM@+%4POa_1qny+J&+Ep-IumO@Lx$MY7OzV5VYdgVuy(i8T=tm4!^ zjQgiANB15XlB#;r@{K3eu7is8Dj`rHDQVn=yKiHz+gmJMYXjqBkgg?OU-?_A(xl}A z(B1PS?(>~0S68?uAZ&qXI zhlkDH)F0_J83UiF6Qf2!8)~1K{J8USw}N$w>9wF=4GRo&mTnh1*&EZ4k zL6%SW1SL_)rb?*o^!oT(p%9M5#YMOizx3QXMkYsp^2%nI9x7em^TeI~4NJG|06aj3 z$;ZEnEY{4kv%P4#e-t37^(L&(%&s}v3T);i;N?aSwv@4WII7f&kfBvZjA?`(kpsK~BocAr`XxDrYoCJGykfnD6O=&avQ!gTS%Eo)3oR_LWB&l5VV7TH+09-+x3Wx7x&aJ$#A9w0dff_b0QC z?>oCX+l*tUrc&2WPBa8bwM*b( zt~WfdFMItsu7AE!5pMLh?h4nFem!_^KIUmTt=v?QvB8)>JKLLXUc#NIuXY9KIO)B# z;=r=PR6F*mk@!ttRgA(zzRtwgCL3@%?82#%)|-}Yl=;AfEh>6WeR&p37Q<&(QHric zBeJ1?y#8G(8F;MqJCMpEa%mMkr^u6Kh3JO4J_&>x$xX>>g5L zRL$&pGQd}QN5iFUeBNw!#Jd>mYd!dlBfpOEgK9xPy+0v(0UEHp&$7Y);@g;4V>VbH z+$CqZjXwJxihfC3yn&&riW9b$hWda0R5#XwB4-1QZY=(Mdb;1MS>OIOKThqDV#1>A zj-cCX3-{~Y>9dhWwY5L%>KF`@-=9`HpEPK(4nAnE8j1C~FSA)ln~GaaA{C}Oi+9d^ zsdZaOd(<7&r;MSdi7zN~rI|`s*nr-+nqP>U}2rU4Xu@ghKh8lNq8O8m3G-nW9? z*TPm~J$fZKD;T;{jm>DczyIhiwce^U2UZTpq*76BbG%e;xy#lkYzO}@;qt3@m(z!H zryn%BY5jv=Y-q9|l3h-+AGs~YxYhIRkahEWWZG+Ynd*@}40Z;df2fC{2K}RZ4>|TC zIsV{%zt-a#K?i!}ICGF<2UanB*%LCg7rBaE|Bo&=b$OK*aCc$fa?+i|Ya>mbez(Ye+>{I_OMVB8Z3#A$`TD<@P&$C_}VbAbj~XccUVILB*G}spH#*f#Zs}643VPP0g z_-X^Z7l)2Gl4M5pW4tfQ(7fsUiKQ-GQ5r@ENaXRf}BR01Yw1#xw)H+A!t%ov`bcKQ*S zCyoVeE&fb0uw*c?stU&QlF0a%3S+C0H-S!LBt-;ak=Qv(iuEEw2ZD2NzjL zEr*^u2E(Y~#x*LT4r@$-S#1xjduvAc7v+?1dd>lzWPLS%r1_MUg3=o2dB%ptM*NEV z1`3qjq_8DMF1@;Mru!L|b1pf}OHN7L#`X=U6OD!8GrXv?gr8u1?G#ii|iZ)wi{XO+S|1_6;SVfJd zbMIc^15n(G@bp!g4L6&(h zF3yO#i2nx&Ug_M#6*|7K&9iX44nm`^j|lzhtmK~?N&B0go2IAXsw`?qPa9OkR29_QKpFB_ye#R zq5yGms#S84P!8%+=hV|nT%#0SsJTWc&GrWh3y_8_(#O}fwQC8Qk-Ic9XF11$pwoRW zdu;<_5qs;od;gLZbHZh(M3sGuGT*2-7`i96(TlChrI4-e9wC;gUrpC7Nm{^meIC=_ zDVG1yJsT$*7w9drzpy<0`8>K=yH@$4iR|BH$yCuyjjP#Rm{HAZoAZK%8~nj`VLdA1 zYiR|f{AU;ekF91fFL@hp+8i}F4;TV)LF%v|@y&chiOVW_;B8z*Wf{5#*KQ@c0bnOT zMdwfM%gaVmB?*GW6bFroaSb-=7>$pj3Rz*xoSd7Z=NyBS38NK77zUxzPTOh1pLZ>M zN(_@uK{ABCoSP8<0qVpD&6(@dv!+^vfW?Gxs?sLT$@w&23ov*{iQ}G=#5G4!c8M-fmi=_ zUGFH?g8c22xXXN%>l@FRaz#_@P&9La_uj+%<1sC5xb${fP|S)~@$llL#JbxsnYz5a zoe-OD#|lYDM+XUm%`%DY1i8uj_8FuJKD$wqSb`+_J2TmwG&al^P9H!5eV1M! zeiFZy%X5aLmp_Fec(62vEP#YbaT`6K*<4K(WZu2R`A-V zxj~Ol1n1IXN3j{$hIUzZ9}OfNXtwF2|ILHGLjujqGA)!cH4Ozc)g zcCFm6+mFldO02OxzgwA|uVn+j#n|w$`{uy|aEFY7z83P0Od9WH32uycX!t0PkXce$ z!V8J9>%Cq<0%<#qCp?Y2zpS{{=C=(AfCMiA$%hr~?m{g8UabA{CC|R(QQK!&t9bw3 zXcxBWFA8u@H^8MO9jdRPmn32o9A&%$A9efF?gzoIv_os1Qg6=9Eftqq&QlN^ z!n4Tjn|$)aNuX(9a4l*7*;Iwssr>})^L692vND%W0`sqxof~cum5iI#NO5l6eUG0G zj2c@b=lZ!;&tenJPv7ZQ(riFIX#T)%pwGtcPfXj0X*?CIqg4Qdnd<4q$sdMVAF3bKkzjF3xJ2<@n zKNuLlDBl95Bv7v+Lp3y~A{Hm&jQi%*T-s`Us)gs&C1qcZdG$Aq@8lC4a^$+TevyES z{-9m34f-8e(6BF^U@?pL**)Urt&CppkQYmRnk))TGKik*cNS22Fe@ECAn$(+?D z^%~7DgpK!$%lqCOzFQp=-~V~*Wl}#e2KbU@Yu#x~abIj_b5bnGWlyv%*zkK8F1F`@ z6!_VkzEM}9*=t5ND_kvaUO9W8OA}Ibmb4??{7OoY{1I7;L{$EFP9k4obBRcpp2Gq* zYFAc4ou?}`=+>W_bBMlrkZ66a#g;5hS^Gl-h3lq7ou*OLQbk4hbt~$q6?1277Lz#K zibr!CSxdtwq`>^DCpAg!=>q$`E7o2{pZ@?-{-}PYXW?CHOC!pUPdh1Da*-TZ&vom$ zhZk+_?Y-cP8OLMC$5Ncavl7!s)#VVqzT!iL{%*l{^B;9E(rBysHigfMSC^RQm19^k z$!#7FVO_OT9MGyuq{{h#0O;>*0s!+PlXl*!UkPlfo^4PE-i$U+oyVzC3);{v?NwX8 z4LM(VER??eXzXmU*>Mw=^T_SN^Ff723Z@2ywoX zz3(zKv#Dpz6x=`5{39`fZi7Jy(CVMfj9k>|weWu|zx#;h^^C6493u}GtLYE~x|%Q0 ze^U}xG9Su?Uand3HD57m(_3)s3B0y<5vucx|^i<j>!~GS{d0mNm3T*gTRZ_2HzkJ=TMqpZ^fqQDz$ZVLB z${&-djC?YC)0&XJXiKIWE3aNrjA442I^oNcVityPg!m@mDA9>2V4OJ^ zb6YVZ2WI827Xc{!-8 zfn0@$;y!4VgZX1^8bt2=yY7_m-Tl@09BuY)^~lxOtE&Cf>I@wOf6ClcWTjmup7rJ2 zRe{Yy=QX0Scl8%D^`BPINLPPbC<97Uw%)GS_p*OyYXcQEUa=6yJ%!OOEOAy0-c^lA zQF|rj8LXuzY|eLmpOZ7pf)LcWr0Z~4ziR*tkjEpV3a7NwZ`J)y68t1TDZ8xII@gfD zXvCKAqp#3+g_GHXGgg)TlV*S@FPCRp_I~9MAHQ^gqsGQoU3tAjCoSII_+MxB z=T5cIieU8HCXCMa*5Z1owOsYfs`8y1ptGM%|MsDVpDsksZtWMa81tR}`0Xu`B+#DV zlr&G$`+$e?*C_%PkvtGRfT@gvYIth@OM(uuq0$~mpP#}XxKDigtn=$T_dmhfD%)*0 z1T|(_HA|gR4Yy2z5h?+?@T3BUpE(xd-xVLex;~qy%r%y5AamBfjXD;u#G$t-j+Tw% zWKHqDys}M|zNo;DGH&(HgnRfQ6VBujb1`t-XOov~Uc!NcYAmjo&tTC|!C&3XAvfml z|7p+iIu}$~!YuOl@6HV0i&uV?GB=oFUZO6LZ=B|oK3cojGA)8YL9bSvZgnkF#e=w7 zy{W^u)#Ve*9SsJ0{-Z0nsd`OYNp1yCAZroseLW|JC@&eyJ5+v+9ROZygpFG{;#aN>KVzS-IXy9Y^FC_PM%LTF*-dWGWHodG`hf zEd6(PEnSwPTJO447S&8@1{RymVCQ36tlukshkvN~BxS(Lv9*%?tw&BIlhAD0xCCV6 z$tAsmm~G#l$Ll#xO|J;Phez`3>ZpAyrvZNWK*hs;vo`AYhHn6Tw=ROaacyl4+pTmq znKH<4FmYQ4PtExpt8ziFk9BL<@KZ&_RL)e#Qj(eZA!!$?;Vk9g#;0%HZ`3=Jj@0{& z>W$yOhv+V;9-Z6b3fWW422Vg0zzdsDMXv#>97JXzC(Xb_LI2)b!-736<`cL6wqMyM z{?{Kp7#HhKZiX2lty|!2(b5*y;16E6NiyzD??J_v=#Qr?*^GZLGC0R7R{&+XNdRMR zqQ6p{Ta~PIKZe%lNlS2iaTd({jnDASZ;Pis3G?m`-cG#ak=D}3RkXG*T-t6dnzytU z{IIQG*F;kpTK0GM({5EAM%wU4@TLF|XXZYTD+nIHpvfOY12 zait=<6QfR9s|Ne|l|3bvWu1BVr7+C1xzq3fC4Gh>4Z`+RtJ$?KY`XvO5sJ$4bNfTV zc%2u3DvUgvJ+Qg~z6xnE6%w7=IRf&2HFS3*);N8za*YQlJY%8HWHnfL$>RI>i8C5j z4ng-`H`&x+v5}9VWr+CprCSuGTlT32y6H};*L zYYqzHojfwBauu1-GPl@Jd2JR)lesg1OZVimVPCRUKA0n}5hNnq5ZHq5lC61>FVk#Q zSHb@AiGFab$(5~Ty@Z#K8Kdlmvgs1P6Nou12RUbYB2yzwLtRY zk5@<53c>jm$DY@q;k*On0?D8>6VG96dQ!i8I zsix8#QvP%~kgJN~t-_^zP>|7{I-iXQp(HG}UjzBWVs6u_02M627VYEpf((m0Z)Z$5 z(pKxf^JkbkwY;qrrFkuDFoju!x|{57H%WS?_*kN7*Sy@M_psks#rWJz+{3d;x!+~X zXm?2AMPA8}u}Wsi)&^3V{lYG%9zNFpaeKzIwmSm$O(+?(zRwHo_K9PWg34T5Bi@zp zH-8@YdYI+()KeF1|7`#FWd-DGjE7>QRtKM1Wfz2c=qji;=`bacp+8MFNrU)f?mz{N?0 zpXISUnj43hxvl3{1Vr2qmVHHqWecUr7_Xsrf--1rR&#l6BYA>jT_XAyl>y~747IS- zf}%eCMHY+Hf(4mQa=SzMzl@->=lDmbx@X}%>RrJa;K2M-w+vUH)g+SAfa|H{<%nS)K+lS?&Q-+nu+ zUXm1g|2DF?DQmT`&H_iio^fOS)u2{7#aO@oJRY@V+^r}5K{&yzoQM!j=u*LT?}~Od zTVyS7X7eki*Hg<1NZD7{bbh(|IV%9zX9fyyzGOn(|8vLzyVdFkTgcF*`CIk$Bn8d3 zklGW+`Zi?!(k(N~pIj@~N2hpfp2SoUg{-6 zN6Yx0O?6XR7skV4GrD<+wObjVr`p*HM4X z!o$lEQuwd$V;u4vI9Ciba%xL4ii?a5Vn>?EJ;QV?jl~NM(S&-AqjQ6dN1)laj>E|roBsdu>oiy-|maJ5aHHNM&uqzU`8GaqzNn1U^@NZw+J zz)oK5H<*(gA^JB423hLl2Xbfl|3{aBUhyc&-{LRNv5kC}t-HJT0O#1d$a}xv_ch&H z-J`qe%!<2po9%VEVSs822h%4~=z(fe*H0}oe~EVabaCG;Jp?8+O{_ZM1j3rmL)4!0%ghq2-o`)9Z&p?CeIUC6b?2{PVzsn zmFn2SNuFK8QVpu6@6}IOAak9)R-s)(ZkJ3QgKpr1;J*rt{q09vmQ>rcfxs!X9d4d8 z9i&|ot%TTijNJG8D1qHcgAZMKG-AV7gpwaQAtG)ihVK-nr0IG6sFr%Q8Fy%hUl&_z z+4b4igI(Bss}DJguZ3?TiESFVSML*O;#EkCfpm$G0%Hx|fExWb5KdpwO13*s9f}SN z@D8B*KT1#PeoJeZMU}hWsvoOs$skTv$yO=o!FGYDk}|Q{t+PHg%HwCHzP?5S%Un@n zX5L6zH{Gz?6^-_*E&S?jdpZ8zY`_>nQ5BO`RP=ETRYoQ8kFf(2@{58j$JP`kZa&YZ z!VG6(v59)&S;{Vaeq%GpIo+qE5Cr8@y3o_kH|YA%sUfn^Qp--}h?T4qz{nlTgovq{ z7TA8%)5M_neIG@&=8ZTVTp>?PrEXRY#QCdJDI89RT`I@OJ$~f-^vgj@v!+``2kX3p z_c<;t5RZC2$4N40njEHJecMV?v)Q{z^T4b}Mzvr&orkX0xIo6tJvG;L_3haeQq|J1 z(qU!!(Q*2N9-IZJSQgIK@>!+IT2axxV;W!^FVSno3zaUhqBP^WeH5F=1)s5%?2H=xhIuBKFYb2nPTf(ZY$eP25yhhs=5x}Hr4)^?F}q6!2#o%vqoy! zZkA}Xr>Y4ja`?yPkA0|$C0{L?BcK56t6lbF{bKY=4Zqj{>K5WeR z-cru>dGLcyI9R2xUM9_rngVulucFj+gG88AKpT(xHNHb^m-@*f2UZE3-341YPS8&G zirFpqEa`X#jm+kEyBq*vO}@vj$NzYu-1WCtachz``@ddIvHJ|2bn^($@E1IqnP;=~ zqG;oG^dk8B$eE!kLZ>62Y31<#od8-D)L*ZG%HG0}fx0;d`j0HrJa4HIQV>BTS9!J~ zco@5lB^fjpo6GU=Tt7`CK1oix{`a)eL*ry*dlc*mm|Sm(X4f%n!7HXMVxtaE?GTjBg1%9iW<|M!y zf=k)!61tEBt)I?;8a*E;aXi zv-yFjz3yG#5L1-ep>D9Y!bwaxtPIzF^bY|nqw1)F{LTb>5jh}`=L#13JaXr}dL4s> zl&MDbP0Be!8;hn+Sn^7&RqT{dNQIoQP*WRcu{(3e`X9O^+ezum2D7=+8wtas3yN_C zM_k4_B=4oh*bGsabj|Fwh0TwLJgs5LwB*2!|q5TZ4VlfZ5wE=Btqiy1|ScSgC>u*c*ST&)%hX?9^ z)@95o1ph&~xDYL8XJmQVbG{{5=2ysu9aZSPZ~6l?X(d?;RQftufe}hGqf3#w3oAaq zsQ!{cq0t~3Ef(~JC!430|8wLkLR>^ipdw1W;RmL0f4VDS_bDZJJzNBuOB5BF49nm2 z4Uvd>PjHAP|E#2rP_mrZ=dmjcx&F1j zSlqof4Jj2sUU7Q<7(tjSgTXh!V_7ehKnq0y*S7raBOYyAauc3wa!Tzi5UQdSVuT;Z zl({j3f0NRv*Ys*j2bOm?;aeD<)T>@|ulhmkb-aTgmPL`rR=gvnkoyWVxqWd_(nC$Pzi%IAZzFgRs z2bS%Vz3S*OJNM1mOJB;#5!T8u{LATn2dw^f*ktDAdM8L4@kJ_HuXaKTp$RE_U!zz6 zfxSi@t3P^CtvORr8)xcOd}De`YF$jtx?0(8@ODcTvl;mgBBdU{d8){7e!?Fk&+pPu zXlv`>|ImZRg0|E??hc%PG~T8-48TJa`RgMRs?1YtOU*GN<43~+zx+$LT$|$JS=a}V zx0|Rx_(P&Uf7TzsxOOI22sB^YejBRU@W9;Rv(?UJ3BKj}!1esC^b~4d zAWx)*!1U%bwp0{z{ZfKp%xv77Dx!C+zd?L~3yr}gGO~AnJ`U><;vpS+=38a3jc9g< zjvoov0$CCjByVLd8wOsq^IFW1Xz{JmwUxHp!8U9+ak9yJ-)%W+$V#}3M|WEBnQwms zRFd}ljS;*Jjhv+d`atScB274U`W18a_J&Ad{3V+j4qoecIRY$d5v68DsGwelsuBX{ z;`+k?U*)r`oy>3v-Xov%PG{sRv<&oh=;RINP3i82ATSS9j5q3MvlScg5UYaw;r{@LdKQG2-YB({`FC~`X*UaSB7l(1ZPmdAy&Sr3R59}A z_h6rID}Te-@v|CF+Cr7Nd7Alc=jHue@Q>OjU-)z2PlkRw_!Hsp8O42Z;=hdl01v(- z{6X#zBYrvzCQ5p zgY{iw#a<=&YvTJI3h%_wLfh>xExsganiPHt@f@03z8h4v*5thn5#{4xxyu56j{g9H zgM1M9MW%cU@O`g_ygjE`_~XM`2g937FA(Xve3rHzJbw{rnnsu7OY5t-et2~#yjiF< z#HirBDI2SS<>J3Mei(QMTeZ3Ho~k}5_!D0j{werf;rTRMPZO9tU8h><8orsN*cQ`d zTnj5LL6|;C!tVs^=Bp2grCS8t?(E&0TDN4|R@-FW+gn$mHSpDN_^c(WT5;v~Q@YmI zUR`@=v_E?z)Vv8lh2%dCAfLnk0N59LcZhY55VfGuFQop*))QD7ex0Rf{REcMV$9=` zNbicTs9kD$&w=zfmqu&bJCE$!VWr$VUO2z_c)DJqlM4v>)ESpiv|Zzp5y0p`rKnjQ zPf5A(2AFK^FHV`L*~4TA|w@NMslJPoJlT5a^Y2a0r? z$Go1${v{VW%q=R$nyrcl`BXYX4`1PlLE%6jKP+7+#mkpV?%nj$`uS{q5}|2Zy7_dv zK2c33_D>MadE(u2;_iE`Ho^4`G8>4aH&3O$w)%aA>JRj-HOuQdna3k9&j!1DKL)|# z&kWxAlWG15)~~!}r`~v5P>)o95MF3D8cu{Q&Y^pyJAzfOb!|#MNG%`8PTKTccj4`w z)8E=%{{U}UUq$}_FFKcpw6kR){{V?MKiWDpY-B$^sq&R$mc|JrA0yO$AKFQ%LfSux zr_%LHg*NQp+S;~{fs)MSm{@&Yo(+Hy!4gainNIIq>pb>K5s(9T}3s;jpAM^$xR5i&dFAhnejiI= zeP!XDH&yV5i0#4~%Y7eO7kVRJ{{U)##Kh_PHnPY2Fck!-1%^0MKo#uT7leK^LuGLS z>-HAVXHdHS*}B#=y>9TF6&aIPl($TL!OLLeHhDgC;_rnzHm9x&--w?cd=d7)o$hV? zGmD*i*lA7s%Pm0an(Elb?EP6WhUwT=u*GvpC#R+V0789|wbHk*`S1Dqo*}JjT8wD~ zv1j(!8e0^W8)2W<3DLPHy$1Ern0uV>IR_}ynM0O3Yo`69V^eStRNHI816M$ z;KqK{b!#4-fHHIPvnu`ssK-(()P&PY+pqWsD~7M3#wL^|lI9z+sh*|2)v5i8^qz+* zN6pixy=30$eog6`KePxr$!ops&;kn&y^wk5oY$)hT?$J>H};K>pDsgNpA>1g*Y_CU zgQby``gIjbUkhosC;(f54Cf#5>w+=c3NfAsPfvbjPpiGZtDMq?*42E!$l)xc)8UqR zUrdJig|G>2_EO(C04P1l!sGG9drV$C@urKVjV2lXz_2a*{f61T!lgbj+KkV%To}YI-`g2RFSVmd{bExX`+}vJg?7@k$ z`ynTg*!h^AGt~8|a)g>|PrvRGO)K8Z{ut*X@sEjh;w zNf`AMTthTc8_TOWqM!agu5)p#jl-OV16~$mIOG5iJpt)GwT7D|+!k-9Tj_GbyVp_c zck9{{UZj(r)eDwf_JE z$k)7!NV%5A?_7t?FkNZ)Hls?{Mu~lOjtIR{aC(jp9COA^Zbz*P+i+*rLc{i<4Y9t} zbi3=@K7d&(o=aGUz|rUZW0M z0TrAYoJJdiajX5C?AJD?#{TWIkL@7s5+AVf)S@3%UW4aV?47?(-`CuIE2|W~6tDjP z0Nc2e#E~1pr+A}Uf*IEn%WbLYO1D>pV5ScN4^K~8N8b!UihRO-_+cGdi zYy{(i7)kPh*YA9zzZKaFY0~~)ng*L&`t&ULelHDa7HZbB_;W=N-7&e-^j$Mabu7!I zmt(3vh4$CnyU8W_1+v)5tNMnu1kxWjUhyB1FgCW!r-E#5axzKWfP#I(liUMb4zp=4 znWz}8r)x2C4ckElrkQ?Q{v}F&W2EJSd5RCu&Op!1D?Z;)h9d;uY?=rb>p$r@umJj> zF@RD2@dF2vD*c;o>P_GN2az1nR#vr+=StT61*IF?OHBt=n&VL-=Ha$(YrFG-j!sGT z#dMdRJ+zqpio?S?t@Oj@GwK%uPp}pxa%@8sd~x4t^#Jrfe2%hRMX6j}=yTi3wgwL) z;!lOH?`-dXiJwiET?b1;gOcmV@h@uK(e=GIN0#6L9A+iCtz$q zZ~1@GCIPu4kFt)K^9SCo*Z7I7+eLjB_CLcR7utcixYM-FTT(WwkKuiHR~ybqaU>Tx zw>KJnoM!d`(2}3nfqyawILI8+S4-owzn_<{<|Nlezx)sD z(}Q)3k}lATU|11=I3owQUeqYXKaWrNZ|RGki{GVS=OzzEljxZzxJpra;H($ZtUy^^16g( z9S>=}{{W91Y;Cv)Yh#-5eOpwL+U)6*W^wk6n|qUx6Y6*)ABPo{r|UP3Ih#g_eE$Ho zeU$yDfzHrKJ=cZ*0KNyHt@OIHYen+@zpjBTZk4b1{pWYCd^h-Q;mvE}l(!b%8S$^f zPl>+{q=#7W&aXbN;hDA17x+)b3#xc-Me!tSy312&A5m5S6C%FC4*XZjULyEWpm>J+ z;#Kv&h2akeiF`NU9}#>AwD1kLi#$NO4~hH7vVA)og8k({HG-`%bDXY+abPFaY_Ohi|E4>s$kP(q9hS&1vD= z-BV16UA^Dh^zRU>*dH{r$};1S%t>s54oM)^)T>S|(0Znw-^=l~n{3ZMcm$)Y_wr4r zzpq~QM`8Om_$R|34zvw*S4y&*S=Ad&KWx?auAy+trUyP*byh$p)v_`at65}#YwLY4 z##;0?R`6Qb+W-RGU|I{Y$i~2N@{0J+Nce^08|g8oHI?a>?LiuiuYmJ%82NFLsp}f$ zgXEqD71*8tW5M+OLtXfTC6)fCZEK)S2AnkT))w0LgzPU-yCC8%yy#+@G>R1mgzfu*-wVQ-E^6bl?t~ zPC8QRn!clR7>7&o7N{kj=vv&Sj&(Ic0O)%K>`zYpE5W=!sCe?@MA3CC-vArSOVOyc z?f(Gw7m4pJ6cMa{WtTn*_=zRjXA$%EDHI%h(hL(0kEmVi5sRHu;r6!&kl$(%cyq-6 z02OrW=59yJrFe_LTAy%E8Nc2=O?m$SS6bRvx2O6407%tVlS|&U`aAi0o~<0Md2f3s z_8Z=r{%ebioiasxQ-#Y7Oc24oW$DgXcf~^;_Md+C;_FP+G^MspL;G&x*5!5E3zP)1 z?b3C4wD2!+$lH4B#|Yd5;}%ypOD*=Qd>!#R?g%wDg4hiU#rKw0x@wIhEvV|P;q5BZ zj6Wm-2qfUfJqnt z6M$;&o#OoxT~k(``(KtBVQ;pv(yhEztBb2GFH_PmY@b$i5oJ8g#H_d*h-Cwf{-~eY zyZL^aYpKlZuJ&D<^xFF#lcQ_8jM59g5qML=e-ZWDxb*!j-(LJr(yjG)ZlcnEvWZ~M{9p?cEaoXGsNHUl>L>L?*&)TvIxQ9n~ZYZ z>&`AOEntG)Ej6E1H@DaCa|Pv=siezgWE*lxr>IRs?Q-V_fSX9*E;#M%WxTw%FkEXe ziy7^O*Y6SW%mRBGu>N>x|9YF5D7*NQ20;Ze7TkR$NFY4M7rKiWgbf0f8^E0&# z2f{ZO+IFF3Y4OWV)HHj&Kx+3oPk^+|LekBn#y-}i0g$QGxcPJYq4MNnx+(los4kIk zZ+<6yS=6qiwbTu@uZ#ZxXI)H3Fe=WQHGL06!hY=>t8W}fo!|mTF>o|{+sLl2blY{d zxU<7*8in?iW31fX>f1&`#o^s6DmE?|Rd5s)0{{$5Z8FnJiuTgm#aGLwUs`Gw#@|ub zwJl!BFLb>ukif3Lh;rJA!59F=$K9;{t!*7mw%gZxH}@S0w3AOySLNo6IP^v~D|Hs3 z@c#f@kL*3 zvg$e~g}i%dU{q>@oa;I;R$YXD%TT!g0G62f>(wM#V{vn%c#di9um1qzWK&Pn-s<;_ zR2GP!*Yh5oupgHjm+C5|?XI1xSzKA#{ie=;wQ0-^sBgF3klVFZ{?gTT#vtw)`2oTE zqcysANpszNzY{BKd#N+bbc?SK8+(YZwBHJAI-wtAFkJY9#QG4mzdR{AW}9vkZ@b+D z{`6pFYZF<~tSzIsf>8?JHZI7~b?Y5M%OSLKH0D{Eq&Plj4$S$^Shh*^`#%6`l0khu zQCQDnbwW}Li_Jve+mbhFhfsATPDeQeuQ|zF8or(w(k70^T~k3@g8>&eHoAH>_$TGy zs*T6)rvv8sM{p|Rh)UL4FZcuY9L~v}0Gc%VghoFi7-oF#Hy2B0Sh)Fy;lq*$2eIi| zmVOY|v?wRmC9&}ym!I@s?F(C7Q|t$x2-P&BgPiql5Ak=eR&6gvH#Sz0cyb&c^s@$~ zof68}V7_yzIWNkFTX9SvL*4k7sTK%=}ZLblM*Pco8VhYR2=H~P% z&&r@+SARNI>2zf5t$qCu6^r|6V7#=`+HpVG+U*|J@vlzw)?OUnw7)QL@w)unf`k$X zBE46`e-*q*d!>J7YI^pAE$y+?t#wOnZ%5MF^pbQm($1Tx5)1s_+Cv?FU8j@9=zLLe ztlDY1WyB4Cr)mze!L3_sU+|6EX?8~c09V$v3)jBUJU^=W@AmO_L}WhXRRA*Wu2Ow# z!Md)Oq-hsAj-Pv}z`AX|wc@LZw3#ezlWvHhncu*gZS1%I0NTe=9${t-yp~~-d7W0% z_jK=X>+}~gX>Po}@9WdCXHe3jzn5Ou?WefChWu?m+4{znqu8zbmE5hSpTCXEpIyT} zYbwja)=PV(Yu8tP9k63}_FuQ^8vdDN>`5U`5M`e@0FFr-wC&AIEJokZ!cB3S%~`xLDz9SXEk2R@4~R^8iHs)52NtzlV>H2`ag+$ zZQ+jyd`b9)uUe+8R$uI&3{E`14S|pK*WV}@40iEZr|#r~WLtcMsu9}aRj3)eUtR<);wFCsy%i7)N^KPs(8eW}#st3xWO89mDho&w^ z0#1Lw-nflAVRL%}5pOcfbDMv(>kTrk;a@Sa44*WK<^07@EWLWr-=E?C01R%E(@kH? z^11I;-YByBWv06(tiC7K?bR-&zSZuvFCSh`qBz7_Y$H<8_S>L}7=rn2wH6f-wiQUp zy4Gj9g4|x-Txf9W1nGW$qv1_aG&@pyk$oITZ(c&7$3Uk5S3PN|-sl&)J=VUr{{Umv z?OOLpzH1-sdmBp^(qnxv*;#+fOo?MAL-)ME{O+#fNY}jP9$n_S61GUW(se{cx4!6E z!=r^#Gn}gd+#ZLQ1vkZ;Cn21Z-OpN~ABybt z2KdM#WF$b_+)BvBiNVIxwGBH@dA#ot-fL6N zKFw|C_)}9Tqq$v34ZAqtbil_->0{S)3#g`m>fQ{w{{Tl8*ZO72Z5uC~0jLwne!Px< z8uf6G6GBq<61O(`Y3TMm+)f)0icwYc?C;y}K6U-Qd{^T8FWML5{{X|^4A@+2e+v9F zbjhvPWSUfdF2B?YG8Rwta`BOZPhapG;IPuXM!Nq1 zi~K95>pmUurK;J@;NJ`D+NGb1t#8(4)}z;(N(y(#w+NgNL6I?y!o3#9!@eCQt~@*8 zeOtp3Y}R++&QB&UP=L#UpF>+c!Qk&~!`t=x%;{j}drR z#J&^pUFiPQ+w=IN!H7_wB!TlOUAz!fV!bG0o5MULTBfwUmaW-c>9cKLeJ*qTK`c!? zQi(CAwbeXGYdV_M9>5nc!tJkzYGWe+{jV*))l~eWGcb zzSe9s?-Xg8{-2(Q{zLjF+t_3t8Mhqr*(BEHm;IaegEjr`hvFz9xUjmn{@w7#t7m_v z4y>)JXsV&T$pCT49V^0>D_vi2o9Wv_+EtzP)&3uT{{WYv>^i@SFLaAfwqAI>^j$I= zU@fgC)irwuwmkfyHE_hqm23gL+7;uk7?c6whUCbfr$4qumCvH>b-R7;F3$Kk2y*#{+eB zU)n8qb@q*GPdeQ5H;`h~91Ae{{{UDY!dxtw=X3p7k($XSy8i%LbbUnI^Vj|dU-$^( zEYE~3ZZAxBUKAf_&)RhRj|fGerJ|e`+hM9I#uKk70%MQ>#aYvJ_@%YeZ7g(e2Mspf z?6K4|Yi|kZdWYC`eN*P%&Hn%&tQ26A_Xl1puNus@nT9E~y*&M|CuP&#i$liT3BG01 zurLGiG4pq>cT$J#qsh6N<5`B-rOJ&$&rIQy=1{A+6Ji?#071`id9H-zckf=`@IS50 zsykUFtL>q{>zak)$7Kco0EZzW;?&�Ho`>WMp7u0-8pAv+R0fkT7eBFFnoNEHo<% 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 a9>S{AnhxLJhyMU13cPgR?_OTNng7`$6>C}m literal 0 HcmV?d00001 diff --git a/src/Components/Button.elm b/src/Components/Button.elm index 14384d0..2943259 100644 --- a/src/Components/Button.elm +++ b/src/Components/Button.elm @@ -9,6 +9,7 @@ import Components.Link as Link import Html exposing (Html) import Html.Attributes as Attr import Html.Attributes.Aria as Aria +import Html.Events as Events import Svg import Svg.Attributes as SvgAttr @@ -31,8 +32,8 @@ arrowIcon attrs = ] -type Type - = Button +type Type msg + = Button msg | Link String @@ -68,7 +69,7 @@ type Arrow | RightArrow -view : Type -> Variant -> Maybe Arrow -> List (Html.Attribute msg) -> List (Html msg) -> Html msg +view : Type msg -> Variant -> Maybe Arrow -> List (Html.Attribute msg) -> List (Html msg) -> Html msg view type_ variant maybeArrow attrs children = let classAttrs : List (Html.Attribute msg) @@ -122,8 +123,8 @@ view type_ variant maybeArrow attrs children = children in case type_ of - Button -> - Html.button (classAttrs ++ attrs) + Button onClick -> + Html.button (classAttrs ++ Events.onClick onClick :: attrs) inner Link href -> diff --git a/src/Components/ResourcePattern.elm b/src/Components/ResourcePattern.elm index 896bddd..67075af 100644 --- a/src/Components/ResourcePattern.elm +++ b/src/Components/ResourcePattern.elm @@ -21,10 +21,7 @@ view { mouseX, mouseY } = , height = 56 , x = 28 , y = 16 - , squares = - [ ( 0, 1 ) - , ( 1, 3 ) - ] + , squares = [] } ] , Html.div @@ -41,10 +38,7 @@ view { mouseX, mouseY } = , height = 56 , x = 28 , y = 16 - , squares = - [ ( 0, 1 ) - , ( 1, 3 ) - ] + , squares = [] } ] ] diff --git a/src/Constant.elm b/src/Constant.elm new file mode 100644 index 0000000..24f563e --- /dev/null +++ b/src/Constant.elm @@ -0,0 +1,6 @@ +module Constant exposing (server) + + +server : String +server = + "https://guida-package-registry.fly.dev" diff --git a/src/Data/Deps.elm b/src/Data/Deps.elm new file mode 100644 index 0000000..2a12686 --- /dev/null +++ b/src/Data/Deps.elm @@ -0,0 +1,50 @@ +module Data.Deps exposing + ( Info + , ModuleInfo + , decoder + ) + +import Dict exposing (Dict) +import Json.Decode as D +import Set exposing (Set) + + + +-- DOCS + + +type alias Info = + Dict String ModuleInfo + + +type alias ModuleInfo = + { pkg : String + , ops : Set String + , values : Set String + , aliases : Set String + , unions : Dict String (List String) + } + + + +-- DECODER + + +decoder : D.Decoder Info +decoder = + D.dict moduleInfoDecoder + + +moduleInfoDecoder : D.Decoder ModuleInfo +moduleInfoDecoder = + D.map5 ModuleInfo + (D.field "pkg" D.string) + (D.field "ops" stringSet) + (D.field "values" stringSet) + (D.field "aliases" stringSet) + (D.field "types" (D.dict (D.list D.string))) + + +stringSet : D.Decoder (Set String) +stringSet = + D.map Set.fromList (D.list D.string) diff --git a/src/Data/Examples.elm b/src/Data/Examples.elm new file mode 100644 index 0000000..9249464 --- /dev/null +++ b/src/Data/Examples.elm @@ -0,0 +1,3502 @@ +module Data.Examples exposing + ( Example + , animation + , book + , buttons + , cards + , clock + , crate + , cube + , dragAndDrop + , firstPerson + , forms + , groceries + , hello + , imagePreviews + , keyboard + , mario + , mouse + , numbers + , picture + , positions + , quotes + , shapes + , textFields + , thwomp + , time + , triangle + , turtle + , upload + ) + +import Data.Registry.Defaults as Defaults +import Data.Registry.Package exposing (Package) +import Data.Version as V + + +type alias Example = + { direct : List Package + , indirect : List Package + , content : String + } + + +animation : Example +animation = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "evancz" "elm-playground" (V.Version 1 0 3) + ] + , indirect = + [ Package "elm" "json" (V.Version 1 1 3) + , Package "elm" "svg" (V.Version 1 0 1) + , Package "elm" "time" (V.Version 1 0 0) + , Package "elm" "url" (V.Version 1 0 0) + , Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + , content = String.trim """ +module Main exposing (main) + +-- Create animations that spin, wave, and zig-zag. +-- This one is a little red wagon bumping along a dirt road. +-- +-- Learn more about the playground here: +-- https://package.elm-lang.org/packages/evancz/elm-playground/latest/ + +import Playground exposing (..) + + +main = + animation view + + +view time = + [ octagon darkGray 36 + |> moveLeft 100 + |> rotate (spin 3 time) + , octagon darkGray 36 + |> moveRight 100 + |> rotate (spin 3 time) + , rectangle red 300 80 + |> moveUp (wave 50 54 2 time) + |> rotate (zigzag -2 2 8 time) + ] + """ + } + + +book : Example +book = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "elm" "http" (V.Version 2 0 0) + ] + , indirect = + [ Package "elm" "bytes" (V.Version 1 0 8) + , Package "elm" "file" (V.Version 1 0 5) + , Package "elm" "json" (V.Version 1 1 3) + , Package "elm" "time" (V.Version 1 0 0) + , Package "elm" "url" (V.Version 1 0 0) + , Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + , content = String.trim """ +module Main exposing (main) + +-- Make a GET request to load a book called "Public Opinion" +-- +-- Read how it works: +-- https://guide.elm-lang.org/effects/http.html + +import Browser +import Html exposing (Html, pre, text) +import Http + + + +-- MAIN + + +main = + Browser.element + { init = init + , update = update + , subscriptions = subscriptions + , view = view + } + + + +-- MODEL + + +type Model + = Failure + | Loading + | Success String + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( Loading + , Http.get + { url = "/assets/public-opinion.txt" + , expect = Http.expectString GotText + } + ) + + + +-- UPDATE + + +type Msg + = GotText (Result Http.Error String) + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + GotText result -> + case result of + Ok fullText -> + ( Success fullText, Cmd.none ) + + Err _ -> + ( Failure, Cmd.none ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : Model -> Html Msg +view model = + case model of + Failure -> + text "I was unable to load your book." + + Loading -> + text "Loading..." + + Success fullText -> + pre [] [ text fullText ] + """ + } + + +buttons : Example +buttons = + { direct = Defaults.direct + , indirect = Defaults.indirect + , content = String.trim """ +module Main exposing (main) + +-- Press buttons to increment and decrement a counter. +-- +-- Read how it works: +-- https://guide.elm-lang.org/architecture/buttons.html + +import Browser +import Html exposing (Html, button, div, text) +import Html.Events exposing (onClick) + + + +-- MAIN + + +main = + Browser.sandbox { init = init, update = update, view = view } + + + +-- MODEL + + +type alias Model = + Int + + +init : Model +init = + 0 + + + +-- UPDATE + + +type Msg + = Increment + | Decrement + + +update : Msg -> Model -> Model +update msg model = + case msg of + Increment -> + model + 1 + + Decrement -> + model - 1 + + + +-- VIEW + + +view : Model -> Html Msg +view model = + div [] + [ button [ onClick Decrement ] [ text "-" ] + , div [] [ text (String.fromInt model) ] + , button [ onClick Increment ] [ text "+" ] + ] + """ + } + + +cards : Example +cards = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "elm" "random" (V.Version 1 0 0) + ] + , indirect = Defaults.indirect + , content = String.trim """ +module Main exposing (main) + +-- Press a button to draw a random card. +-- +-- Dependencies: +-- guida install elm/random + +import Browser +import Html exposing (..) +import Html.Attributes exposing (style) +import Html.Events exposing (..) +import Random + + + +-- MAIN + + +main = + Browser.element + { init = init + , update = update + , subscriptions = subscriptions + , view = view + } + + + +-- MODEL + + +type alias Model = + { card : Card + } + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( Model Three + , Cmd.none + ) + + +type Card + = Ace + | Two + | Three + | Four + | Five + | Six + | Seven + | Eight + | Nine + | Ten + | Jack + | Queen + | King + + + +-- UPDATE + + +type Msg + = Draw + | NewCard Card + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + Draw -> + ( model + , Random.generate NewCard cardGenerator + ) + + NewCard newCard -> + ( Model newCard + , Cmd.none + ) + + +cardGenerator : Random.Generator Card +cardGenerator = + Random.uniform Ace + [ Two + , Three + , Four + , Five + , Six + , Seven + , Eight + , Nine + , Ten + , Jack + , Queen + , King + ] + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : Model -> Html Msg +view model = + div [] + [ button [ onClick Draw ] [ text "Draw" ] + , div [ style "font-size" "12em" ] [ text (viewCard model.card) ] + ] + + +viewCard : Card -> String +viewCard card = + case card of + Ace -> + "🂡" + + Two -> + "🂢" + + Three -> + "🂣" + + Four -> + "🂤" + + Five -> + "🂥" + + Six -> + "🂦" + + Seven -> + "🂧" + + Eight -> + "🂨" + + Nine -> + "🂩" + + Ten -> + "🂪" + + Jack -> + "🂫" + + Queen -> + "🂭" + + King -> + "🂮" + """ + } + + +clock : Example +clock = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "elm" "svg" (V.Version 1 0 1) + , Package "elm" "time" (V.Version 1 0 0) + ] + , indirect = + [ Package "elm" "json" (V.Version 1 1 3) + , Package "elm" "url" (V.Version 1 0 0) + , Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + , content = String.trim """ +module Main exposing (main) + +-- Show an analog clock for your time zone. +-- +-- Dependencies: +-- guida install elm/svg +-- guida install elm/time +-- +-- For a simpler version, check out: +-- https://guida-lang.org/examples/time + +import Browser +import Html exposing (Html) +import Svg exposing (..) +import Svg.Attributes exposing (..) +import Task +import Time + + + +-- MAIN + + +main = + Browser.element + { init = init + , view = view + , update = update + , subscriptions = subscriptions + } + + + +-- MODEL + + +type alias Model = + { zone : Time.Zone + , time : Time.Posix + } + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( Model Time.utc (Time.millisToPosix 0) + , Cmd.batch + [ Task.perform AdjustTimeZone Time.here + , Task.perform Tick Time.now + ] + ) + + + +-- UPDATE + + +type Msg + = Tick Time.Posix + | AdjustTimeZone Time.Zone + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + Tick newTime -> + ( { model | time = newTime } + , Cmd.none + ) + + AdjustTimeZone newZone -> + ( { model | zone = newZone } + , Cmd.none + ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Time.every 1000 Tick + + + +-- VIEW + + +view : Model -> Html Msg +view model = + let + hour = + toFloat (Time.toHour model.zone model.time) + + minute = + toFloat (Time.toMinute model.zone model.time) + + second = + toFloat (Time.toSecond model.zone model.time) + in + svg + [ viewBox "0 0 400 400" + , width "400" + , height "400" + ] + [ circle [ cx "200", cy "200", r "120", fill "#1293D8" ] [] + , viewHand 6 60 (hour / 12) + , viewHand 6 90 (minute / 60) + , viewHand 3 90 (second / 60) + ] + + +viewHand : Int -> Float -> Float -> Svg msg +viewHand width length turns = + let + t = + 2 * pi * (turns - 0.25) + + x = + 200 + length * cos t + + y = + 200 + length * sin t + in + line + [ x1 "200" + , y1 "200" + , x2 (String.fromFloat x) + , y2 (String.fromFloat y) + , stroke "white" + , strokeWidth (String.fromInt width) + , strokeLinecap "round" + ] + [] + """ + } + + +crate : Example +crate = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "elm-explorations" "linear-algebra" (V.Version 1 0 3) + , Package "elm-explorations" "webgl" (V.Version 1 1 3) + ] + , indirect = + [ Package "elm" "json" (V.Version 1 1 3) + , Package "elm" "time" (V.Version 1 0 0) + , Package "elm" "url" (V.Version 1 0 0) + , Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + , content = String.trim """ +module Main exposing (main) + +-- Demonstrate how to load textures and put them on a cube. +-- +-- Dependencies: +-- guida install elm-explorations/linear-algebra +-- guida install elm-explorations/webgl + +import Browser +import Browser.Events as E +import Html exposing (Html) +import Html.Attributes exposing (height, style, width) +import Math.Matrix4 as Mat4 exposing (Mat4) +import Math.Vector2 as Vec2 exposing (Vec2, vec2) +import Math.Vector3 as Vec3 exposing (Vec3, vec3) +import Result +import Task +import WebGL +import WebGL.Texture as Texture + + + +-- MAIN + + +main = + Browser.element + { init = init + , view = view + , update = \\msg model -> ( update msg model, Cmd.none ) + , subscriptions = subscriptions + } + + + +-- MODEL + + +type alias Model = + { angle : Float + , texture : Maybe Texture.Texture + } + + +init : () -> ( Model, Cmd Msg ) +init () = + ( { angle = 0 + , texture = Nothing + } + , Task.attempt GotTexture (Texture.load "/images/wood-crate.jpg") + ) + + + +-- UPDATE + + +type Msg + = TimeDelta Float + | GotTexture (Result Texture.Error Texture.Texture) + + +update : Msg -> Model -> Model +update msg model = + case msg of + TimeDelta dt -> + { model | angle = model.angle + dt / 5000 } + + GotTexture result -> + { model | texture = Result.toMaybe result } + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions _ = + E.onAnimationFrameDelta TimeDelta + + + +-- VIEW + + +view : Model -> Html Msg +view model = + case model.texture of + Nothing -> + Html.text "Loading texture..." + + Just texture -> + WebGL.toHtml + [ width 400 + , height 400 + , style "display" "block" + ] + [ WebGL.entity vertexShader fragmentShader crateMesh (toUniforms model.angle texture) + ] + + + +-- UNIFORMS + + +type alias Uniforms = + { rotation : Mat4 + , perspective : Mat4 + , camera : Mat4 + , texture : Texture.Texture + } + + +toUniforms : Float -> Texture.Texture -> Uniforms +toUniforms angle texture = + { rotation = + Mat4.mul + (Mat4.makeRotate (3 * angle) (vec3 0 1 0)) + (Mat4.makeRotate (2 * angle) (vec3 1 0 0)) + , perspective = perspective + , camera = camera + , texture = texture + } + + +perspective : Mat4 +perspective = + Mat4.makePerspective 45 1 0.01 100 + + +camera : Mat4 +camera = + Mat4.makeLookAt (vec3 0 0 5) (vec3 0 0 0) (vec3 0 1 0) + + + +-- MESH + + +type alias Vertex = + { position : Vec3 + , coord : Vec2 + } + + +crateMesh : WebGL.Mesh Vertex +crateMesh = + WebGL.triangles <| + List.concatMap rotatedSquare <| + [ ( 0, 0 ) + , ( 90, 0 ) + , ( 180, 0 ) + , ( 270, 0 ) + , ( 0, 90 ) + , ( 0, 270 ) + ] + + +rotatedSquare : ( Float, Float ) -> List ( Vertex, Vertex, Vertex ) +rotatedSquare ( angleXZ, angleYZ ) = + let + transformMat = + Mat4.mul + (Mat4.makeRotate (degrees angleXZ) Vec3.j) + (Mat4.makeRotate (degrees angleYZ) Vec3.i) + + transform vertex = + { vertex | position = Mat4.transform transformMat vertex.position } + + transformTriangle ( a, b, c ) = + ( transform a, transform b, transform c ) + in + List.map transformTriangle square + + +square : List ( Vertex, Vertex, Vertex ) +square = + let + topLeft = + Vertex (vec3 -1 1 1) (vec2 0 1) + + topRight = + Vertex (vec3 1 1 1) (vec2 1 1) + + bottomLeft = + Vertex (vec3 -1 -1 1) (vec2 0 0) + + bottomRight = + Vertex (vec3 1 -1 1) (vec2 1 0) + in + [ ( topLeft, topRight, bottomLeft ) + , ( bottomLeft, topRight, bottomRight ) + ] + + + +-- SHADERS + + +vertexShader : WebGL.Shader Vertex Uniforms { vcoord : Vec2 } +vertexShader = + [glsl| + attribute vec3 position; + attribute vec2 coord; + uniform mat4 perspective; + uniform mat4 camera; + uniform mat4 rotation; + varying vec2 vcoord; + + void main () { + gl_Position = perspective * camera * rotation * vec4(position, 1.0); + vcoord = coord; + } + |] + + +fragmentShader : WebGL.Shader {} Uniforms { vcoord : Vec2 } +fragmentShader = + [glsl| + precision mediump float; + uniform sampler2D texture; + varying vec2 vcoord; + + void main () { + gl_FragColor = texture2D(texture, vcoord); + } + |] + """ + } + + +cube : Example +cube = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "elm-explorations" "linear-algebra" (V.Version 1 0 3) + , Package "elm-explorations" "webgl" (V.Version 1 1 3) + ] + , indirect = + [ Package "elm" "json" (V.Version 1 1 3) + , Package "elm" "time" (V.Version 1 0 0) + , Package "elm" "url" (V.Version 1 0 0) + , Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + , content = String.trim """ +module Main exposing (main) + +-- Render a spinning cube. +-- +-- Dependencies: +-- guida install elm-explorations/linear-algebra +-- guida install elm-explorations/webgl + +import Browser +import Browser.Events as E +import Html exposing (Html) +import Html.Attributes exposing (height, style, width) +import Math.Matrix4 as Mat4 exposing (Mat4) +import Math.Vector3 as Vec3 exposing (Vec3, vec3) +import WebGL + + + +-- MAIN + + +main = + Browser.element + { init = init + , view = view + , update = update + , subscriptions = subscriptions + } + + + +-- MODEL + + +type alias Model = + Float + + +init : () -> ( Model, Cmd Msg ) +init () = + ( 0, Cmd.none ) + + + +-- UPDATE + + +type Msg + = TimeDelta Float + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg angle = + case msg of + TimeDelta dt -> + ( angle + dt / 5000, Cmd.none ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions _ = + E.onAnimationFrameDelta TimeDelta + + + +-- VIEW + + +view : Model -> Html Msg +view angle = + WebGL.toHtml + [ width 400 + , height 400 + , style "display" "block" + ] + [ WebGL.entity vertexShader fragmentShader cubeMesh (uniforms angle) + ] + + +type alias Uniforms = + { rotation : Mat4 + , perspective : Mat4 + , camera : Mat4 + } + + +uniforms : Float -> Uniforms +uniforms angle = + { rotation = + Mat4.mul + (Mat4.makeRotate (3 * angle) (vec3 0 1 0)) + (Mat4.makeRotate (2 * angle) (vec3 1 0 0)) + , perspective = Mat4.makePerspective 45 1 0.01 100 + , camera = Mat4.makeLookAt (vec3 0 0 5) (vec3 0 0 0) (vec3 0 1 0) + } + + + +-- MESH + + +type alias Vertex = + { color : Vec3 + , position : Vec3 + } + + +cubeMesh : WebGL.Mesh Vertex +cubeMesh = + let + rft = + vec3 1 1 1 + + lft = + vec3 -1 1 1 + + lbt = + vec3 -1 -1 1 + + rbt = + vec3 1 -1 1 + + rbb = + vec3 1 -1 -1 + + rfb = + vec3 1 1 -1 + + lfb = + vec3 -1 1 -1 + + lbb = + vec3 -1 -1 -1 + in + WebGL.triangles <| + List.concat <| + [ face (vec3 115 210 22) rft rfb rbb rbt + + -- green + , face (vec3 52 101 164) rft rfb lfb lft + + -- blue + , face (vec3 237 212 0) rft lft lbt rbt + + -- yellow + , face (vec3 204 0 0) rfb lfb lbb rbb + + -- red + , face (vec3 117 80 123) lft lfb lbb lbt + + -- purple + , face (vec3 245 121 0) rbt rbb lbb lbt + + -- orange + ] + + +face : Vec3 -> Vec3 -> Vec3 -> Vec3 -> Vec3 -> List ( Vertex, Vertex, Vertex ) +face color a b c d = + let + vertex position = + Vertex (Vec3.scale (1 / 255) color) position + in + [ ( vertex a, vertex b, vertex c ) + , ( vertex c, vertex d, vertex a ) + ] + + + +-- SHADERS + + +vertexShader : WebGL.Shader Vertex Uniforms { vcolor : Vec3 } +vertexShader = + [glsl| + attribute vec3 position; + attribute vec3 color; + uniform mat4 perspective; + uniform mat4 camera; + uniform mat4 rotation; + varying vec3 vcolor; + void main () { + gl_Position = perspective * camera * rotation * vec4(position, 1.0); + vcolor = color; + } + |] + + +fragmentShader : WebGL.Shader {} Uniforms { vcolor : Vec3 } +fragmentShader = + [glsl| + precision mediump float; + varying vec3 vcolor; + void main () { + gl_FragColor = 0.8 * vec4(vcolor, 1.0); + } + |] + """ + } + + +dragAndDrop : Example +dragAndDrop = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "file" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "elm" "json" (V.Version 1 1 3) + ] + , indirect = + [ Package "elm" "bytes" (V.Version 1 0 8) + , Package "elm" "time" (V.Version 1 0 0) + , Package "elm" "url" (V.Version 1 0 0) + , Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + , content = String.trim """ +module Main exposing (main) + +-- Image upload with a drag and drop zone. +-- +-- Dependencies: +-- guida install elm/file +-- guida install elm/json + +import Browser +import File exposing (File) +import File.Select as Select +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Json.Decode as D + + + +-- MAIN + + +main = + Browser.element + { init = init + , view = view + , update = update + , subscriptions = subscriptions + } + + + +-- MODEL + + +type alias Model = + { hover : Bool + , files : List File + } + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( Model False [], Cmd.none ) + + + +-- UPDATE + + +type Msg + = Pick + | DragEnter + | DragLeave + | GotFiles File (List File) + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + Pick -> + ( model + , Select.files [ "image/*" ] GotFiles + ) + + DragEnter -> + ( { model | hover = True } + , Cmd.none + ) + + DragLeave -> + ( { model | hover = False } + , Cmd.none + ) + + GotFiles file files -> + ( { model + | files = + file :: files + , hover = + False + } + , Cmd.none + ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : Model -> Html Msg +view model = + div + [ style "border" + (if model.hover then + "6px dashed purple" + + else + "6px dashed #ccc" + ) + , style "border-radius" "20px" + , style "width" "480px" + , style "height" "100px" + , style "margin" "100px auto" + , style "padding" "20px" + , style "display" "flex" + , style "flex-direction" "column" + , style "justify-content" "center" + , style "align-items" "center" + , hijackOn "dragenter" (D.succeed DragEnter) + , hijackOn "dragover" (D.succeed DragEnter) + , hijackOn "dragleave" (D.succeed DragLeave) + , hijackOn "drop" dropDecoder + ] + [ button [ onClick Pick ] [ text "Upload Images" ] + , span [ style "color" "#ccc" ] [ text (Debug.toString model) ] + ] + + +dropDecoder : D.Decoder Msg +dropDecoder = + D.at [ "dataTransfer", "files" ] (D.oneOrMore GotFiles File.decoder) + + +hijackOn : String -> D.Decoder msg -> Attribute msg +hijackOn event decoder = + preventDefaultOn event (D.map hijack decoder) + + +hijack : msg -> ( msg, Bool ) +hijack msg = + ( msg, True ) + """ + } + + +firstPerson : Example +firstPerson = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "elm" "json" (V.Version 1 1 3) + , Package "elm-explorations" "linear-algebra" (V.Version 1 0 3) + , Package "elm-explorations" "webgl" (V.Version 1 1 3) + ] + , indirect = + [ Package "elm" "time" (V.Version 1 0 0) + , Package "elm" "url" (V.Version 1 0 0) + , Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + , content = String.trim """ +module Main exposing (main) + +-- Walk around in 3D space using the keyboard. +-- +-- Dependencies: +-- guida install elm-explorations/linear-algebra +-- guida install elm-explorations/webgl +-- +-- Try adding the ability to crouch or to land on top of the crate! + +import Browser +import Browser.Dom as Dom +import Browser.Events as E +import Html exposing (Html, div, p, text) +import Html.Attributes exposing (height, style, width) +import Json.Decode as D +import Math.Matrix4 as Mat4 exposing (Mat4) +import Math.Vector2 as Vec2 exposing (Vec2, vec2) +import Math.Vector3 as Vec3 exposing (Vec3, vec3) +import Task +import WebGL +import WebGL.Texture as Texture + + + +-- MAIN + + +main : Program () Model Msg +main = + Browser.element + { init = init + , view = view + , update = \\msg model -> ( update msg model, Cmd.none ) + , subscriptions = subscriptions + } + + + +-- MODEL + + +type alias Model = + { keys : Keys + , width : Float + , height : Float + , person : Person + , texture : Maybe Texture.Texture + } + + +type alias Keys = + { up : Bool + , left : Bool + , down : Bool + , right : Bool + , space : Bool + } + + +type alias Person = + { position : Vec3 + , velocity : Vec3 + } + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( { keys = noKeys + , width = 400 + , height = 400 + , person = Person (vec3 0 eyeLevel -10) (vec3 0 0 0) + , texture = Nothing + } + , Cmd.batch + [ Task.attempt GotTexture (Texture.load "/images/wood-crate.jpg") + , Task.perform (\\{ viewport } -> Resized viewport.width viewport.height) Dom.getViewport + ] + ) + + +eyeLevel : Float +eyeLevel = + 2 + + +noKeys : Keys +noKeys = + Keys False False False False False + + + +-- UPDATE + + +type Msg + = GotTexture (Result Texture.Error Texture.Texture) + | KeyChanged Bool String + | TimeDelta Float + | Resized Float Float + | VisibilityChanged E.Visibility + + +update : Msg -> Model -> Model +update msg model = + case msg of + GotTexture result -> + { model | texture = Result.toMaybe result } + + KeyChanged isDown key -> + { model | keys = updateKeys isDown key model.keys } + + TimeDelta dt -> + { model | person = updatePerson dt model.keys model.person } + + Resized width height -> + { model + | width = + width + , height = + height + } + + VisibilityChanged _ -> + { model | keys = noKeys } + + +updateKeys : Bool -> String -> Keys -> Keys +updateKeys isDown key keys = + case key of + " " -> + { keys | space = isDown } + + "ArrowUp" -> + { keys | up = isDown } + + "ArrowLeft" -> + { keys | left = isDown } + + "ArrowDown" -> + { keys | down = isDown } + + "ArrowRight" -> + { keys | right = isDown } + + _ -> + keys + + +updatePerson : Float -> Keys -> Person -> Person +updatePerson dt keys person = + let + velocity = + stepVelocity dt keys person + + position = + Vec3.add person.position (Vec3.scale (dt / 500) velocity) + in + if Vec3.getY position < eyeLevel then + { position = Vec3.setY eyeLevel position + , velocity = Vec3.setY 0 velocity + } + + else + { position = position + , velocity = velocity + } + + +stepVelocity : Float -> Keys -> Person -> Vec3 +stepVelocity dt { left, right, up, down, space } person = + if Vec3.getY person.position > eyeLevel then + Vec3.setY (Vec3.getY person.velocity - dt / 250) person.velocity + + else + let + toV positive negative = + (if positive then + 1 + + else + 0 + ) + - (if negative then + 1 + + else + 0 + ) + in + vec3 (toV left right) + (if space then + 2 + + else + 0 + ) + (toV up down) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.batch + [ E.onResize (\\w h -> Resized (toFloat w) (toFloat h)) + , E.onKeyUp (D.map (KeyChanged False) (D.field "key" D.string)) + , E.onKeyDown (D.map (KeyChanged True) (D.field "key" D.string)) + , E.onAnimationFrameDelta TimeDelta + , E.onVisibilityChange VisibilityChanged + ] + + + +-- VIEW + + +view : Model -> Html Msg +view model = + let + entities = + case model.texture of + Nothing -> + [] + + Just texture -> + [ viewCrate model.width model.height model.person texture ] + in + div + [ style "position" "absolute" + , style "left" "0" + , style "top" "0" + , style "width" (String.fromFloat model.width ++ "px") + , style "height" (String.fromFloat model.height ++ "px") + ] + [ WebGL.toHtmlWith [ WebGL.depth 1, WebGL.clearColor 1 1 1 1 ] + [ style "display" "block" + , width (round model.width) + , height (round model.height) + ] + entities + , keyboardInstructions model.keys + ] + + +viewCrate : Float -> Float -> Person -> Texture.Texture -> WebGL.Entity +viewCrate width height person texture = + let + perspective = + Mat4.mul + (Mat4.makePerspective 45 (width / height) 0.01 100) + (Mat4.makeLookAt person.position (Vec3.add person.position Vec3.k) Vec3.j) + in + WebGL.entity vertexShader + fragmentShader + crate + { texture = texture + , perspective = perspective + } + + +keyboardInstructions : Keys -> Html msg +keyboardInstructions keys = + div + [ style "position" "absolute" + , style "font-family" "monospace" + , style "text-align" "center" + , style "left" "20px" + , style "right" "20px" + , style "top" "20px" + ] + [ p [] [ text "Walk around with a first person perspective." ] + , p [] [ text "Arrows keys to move, space bar to jump." ] + ] + + + +-- MESH + + +type alias Vertex = + { position : Vec3 + , coord : Vec2 + } + + +crate : WebGL.Mesh Vertex +crate = + WebGL.triangles <| + List.concatMap rotatedSquare <| + [ ( 0, 0 ) + , ( 90, 0 ) + , ( 180, 0 ) + , ( 270, 0 ) + , ( 0, 90 ) + , ( 0, -90 ) + ] + + +rotatedSquare : ( Float, Float ) -> List ( Vertex, Vertex, Vertex ) +rotatedSquare ( angleXZ, angleYZ ) = + let + transformMat = + Mat4.mul + (Mat4.makeRotate (degrees angleXZ) Vec3.j) + (Mat4.makeRotate (degrees angleYZ) Vec3.i) + + transform vertex = + { vertex + | position = + Mat4.transform transformMat vertex.position + } + + transformTriangle ( a, b, c ) = + ( transform a, transform b, transform c ) + in + List.map transformTriangle square + + +square : List ( Vertex, Vertex, Vertex ) +square = + let + topLeft = + Vertex (vec3 -1 1 1) (vec2 0 1) + + topRight = + Vertex (vec3 1 1 1) (vec2 1 1) + + bottomLeft = + Vertex (vec3 -1 -1 1) (vec2 0 0) + + bottomRight = + Vertex (vec3 1 -1 1) (vec2 1 0) + in + [ ( topLeft, topRight, bottomLeft ) + , ( bottomLeft, topRight, bottomRight ) + ] + + + +-- SHADERS + + +type alias Uniforms = + { texture : Texture.Texture + , perspective : Mat4 + } + + +vertexShader : WebGL.Shader Vertex Uniforms { vcoord : Vec2 } +vertexShader = + [glsl| + attribute vec3 position; + attribute vec2 coord; + uniform mat4 perspective; + varying vec2 vcoord; + + void main () { + gl_Position = perspective * vec4(position, 1.0); + vcoord = coord; + } + |] + + +fragmentShader : WebGL.Shader {} Uniforms { vcoord : Vec2 } +fragmentShader = + [glsl| + precision mediump float; + uniform sampler2D texture; + varying vec2 vcoord; + + void main () { + gl_FragColor = texture2D(texture, vcoord); + } + |] + """ + } + + +forms : Example +forms = + { direct = Defaults.direct + , indirect = Defaults.indirect + , content = String.trim """ +module Main exposing (main) + +-- Input a user name and password. Make sure the password matches. +-- +-- Read how it works: +-- https://guide.elm-lang.org/architecture/forms.html + +import Browser +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (onInput) + + + +-- MAIN + + +main = + Browser.sandbox { init = init, update = update, view = view } + + + +-- MODEL + + +type alias Model = + { name : String + , password : String + , passwordAgain : String + } + + +init : Model +init = + Model "" "" "" + + + +-- UPDATE + + +type Msg + = Name String + | Password String + | PasswordAgain String + + +update : Msg -> Model -> Model +update msg model = + case msg of + Name name -> + { model | name = name } + + Password password -> + { model | password = password } + + PasswordAgain password -> + { model | passwordAgain = password } + + + +-- VIEW + + +view : Model -> Html Msg +view model = + div [] + [ viewInput "text" "Name" model.name Name + , viewInput "password" "Password" model.password Password + , viewInput "password" "Re-enter Password" model.passwordAgain PasswordAgain + , viewValidation model + ] + + +viewInput : String -> String -> String -> (String -> msg) -> Html msg +viewInput t p v toMsg = + input [ type_ t, placeholder p, value v, onInput toMsg ] [] + + +viewValidation : Model -> Html msg +viewValidation model = + if model.password == model.passwordAgain then + div [ style "color" "green" ] [ text "OK" ] + + else + div [ style "color" "red" ] [ text "Passwords do not match!" ] + """ + } + + +groceries : Example +groceries = + { direct = Defaults.direct + , indirect = Defaults.indirect + , content = String.trim """ +module Main exposing (main) + +-- Show a list of items I need to buy at the grocery store. + +import Html exposing (..) + + +main = + div [] + [ h1 [] [ text "My Grocery List" ] + , ul [] + [ li [] [ text "Black Beans" ] + , li [] [ text "Limes" ] + , li [] [ text "Greek Yogurt" ] + , li [] [ text "Cilantro" ] + , li [] [ text "Honey" ] + , li [] [ text "Sweet Potatoes" ] + , li [] [ text "Cumin" ] + , li [] [ text "Chili Powder" ] + , li [] [ text "Quinoa" ] + ] + ] + """ + } + + +hello : Example +hello = + { direct = Defaults.direct + , indirect = Defaults.indirect + , content = String.trim """ +module Main exposing (main) + +import Html exposing (text) + + +main = + text "Hello!" + """ + } + + +imagePreviews : Example +imagePreviews = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "file" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "elm" "json" (V.Version 1 1 3) + ] + , indirect = + [ Package "elm" "bytes" (V.Version 1 0 8) + , Package "elm" "time" (V.Version 1 0 0) + , Package "elm" "url" (V.Version 1 0 0) + , Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + , content = String.trim """ +module Main exposing (main) + +-- Image upload with a drag and drop zone. See image previews! +-- +-- Dependencies: +-- guida install elm/file +-- guida install elm/json + +import Browser +import File exposing (File) +import File.Select as Select +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Json.Decode as D +import Task + + + +-- MAIN + + +main = + Browser.element + { init = init + , view = view + , update = update + , subscriptions = subscriptions + } + + + +-- MODEL + + +type alias Model = + { hover : Bool + , previews : List String + } + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( Model False [], Cmd.none ) + + + +-- UPDATE + + +type Msg + = Pick + | DragEnter + | DragLeave + | GotFiles File (List File) + | GotPreviews (List String) + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + Pick -> + ( model + , Select.files [ "image/*" ] GotFiles + ) + + DragEnter -> + ( { model | hover = True } + , Cmd.none + ) + + DragLeave -> + ( { model | hover = False } + , Cmd.none + ) + + GotFiles file files -> + ( { model | hover = False } + , Task.perform GotPreviews <| + Task.sequence <| + List.map File.toUrl (file :: files) + ) + + GotPreviews urls -> + ( { model | previews = urls } + , Cmd.none + ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : Model -> Html Msg +view model = + div + [ style "border" + (if model.hover then + "6px dashed purple" + + else + "6px dashed #ccc" + ) + , style "border-radius" "20px" + , style "width" "480px" + , style "margin" "100px auto" + , style "padding" "40px" + , style "display" "flex" + , style "flex-direction" "column" + , style "justify-content" "center" + , style "align-items" "center" + , hijackOn "dragenter" (D.succeed DragEnter) + , hijackOn "dragover" (D.succeed DragEnter) + , hijackOn "dragleave" (D.succeed DragLeave) + , hijackOn "drop" dropDecoder + ] + [ button [ onClick Pick ] [ text "Upload Images" ] + , div + [ style "display" "flex" + , style "align-items" "center" + , style "height" "60px" + , style "padding" "20px" + ] + (List.map viewPreview model.previews) + ] + + +viewPreview : String -> Html msg +viewPreview url = + div + [ style "width" "60px" + , style "height" "60px" + , style "background-image" ("url('" ++ url ++ "')") + , style "background-position" "center" + , style "background-repeat" "no-repeat" + , style "background-size" "contain" + ] + [] + + +dropDecoder : D.Decoder Msg +dropDecoder = + D.at [ "dataTransfer", "files" ] (D.oneOrMore GotFiles File.decoder) + + +hijackOn : String -> D.Decoder msg -> Attribute msg +hijackOn event decoder = + preventDefaultOn event (D.map hijack decoder) + + +hijack : msg -> ( msg, Bool ) +hijack msg = + ( msg, True ) + """ + } + + +keyboard : Example +keyboard = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "evancz" "elm-playground" (V.Version 1 0 3) + ] + , indirect = + [ Package "elm" "json" (V.Version 1 1 3) + , Package "elm" "svg" (V.Version 1 0 1) + , Package "elm" "time" (V.Version 1 0 0) + , Package "elm" "url" (V.Version 1 0 0) + , Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + , content = String.trim """ +module Main exposing (main) + +-- Move a square around with the arrow keys: UP, DOWN, LEFT, RIGHT +-- Try making it move around more quickly! +-- +-- Learn more about the playground here: +-- https://package.elm-lang.org/packages/evancz/elm-playground/latest/ + +import Playground exposing (..) + + +main = + game view update ( 0, 0 ) + + +view computer ( x, y ) = + [ square blue 40 + |> move x y + ] + + +update computer ( x, y ) = + ( x + toX computer.keyboard + , y + toY computer.keyboard + ) + """ + } + + +mario : Example +mario = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "evancz" "elm-playground" (V.Version 1 0 3) + ] + , indirect = + [ Package "elm" "json" (V.Version 1 1 3) + , Package "elm" "svg" (V.Version 1 0 1) + , Package "elm" "time" (V.Version 1 0 0) + , Package "elm" "url" (V.Version 1 0 0) + , Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + , content = String.trim """ +module Main exposing (main) + +-- Walk around with the arrow keys. Press the UP arrow to jump! +-- +-- Learn more about the playground here: +-- https://package.elm-lang.org/packages/evancz/elm-playground/latest/ + +import Playground exposing (..) + + + +-- MAIN + + +main = + game view + update + { x = 0 + , y = 0 + , vx = 0 + , vy = 0 + , dir = "right" + } + + + +-- VIEW + + +view computer mario = + let + w = + computer.screen.width + + h = + computer.screen.height + + b = + computer.screen.bottom + in + [ rectangle (rgb 174 238 238) w h + , rectangle (rgb 74 163 41) w 100 + |> moveY b + , image 70 70 (toGif mario) + |> move mario.x (b + 76 + mario.y) + ] + + +toGif mario = + if mario.y > 0 then + "/images/mario/jump/" ++ mario.dir ++ ".gif" + + else if mario.vx /= 0 then + "/images/mario/walk/" ++ mario.dir ++ ".gif" + + else + "/images/mario/stand/" ++ mario.dir ++ ".gif" + + + +-- UPDATE + + +update computer mario = + let + dt = + 1.666 + + vx = + toX computer.keyboard + + vy = + if mario.y == 0 then + if computer.keyboard.up then + 5 + + else + 0 + + else + mario.vy - dt / 8 + + x = + mario.x + dt * vx + + y = + mario.y + dt * vy + in + { x = x + , y = max 0 y + , vx = vx + , vy = vy + , dir = + if vx == 0 then + mario.dir + + else if vx < 0 then + "left" + + else + "right" + } + """ + } + + +mouse : Example +mouse = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "evancz" "elm-playground" (V.Version 1 0 3) + ] + , indirect = + [ Package "elm" "json" (V.Version 1 1 3) + , Package "elm" "svg" (V.Version 1 0 1) + , Package "elm" "time" (V.Version 1 0 0) + , Package "elm" "url" (V.Version 1 0 0) + , Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + , content = String.trim """ +module Main exposing (main) + +-- Draw a cicle around the mouse. Change its color by pressing down. +-- +-- Learn more about the playground here: +-- https://package.elm-lang.org/packages/evancz/elm-playground/latest/ + +import Playground exposing (..) + + +main = + game view update () + + +view computer memory = + [ circle lightPurple 30 + |> moveX computer.mouse.x + |> moveY computer.mouse.y + |> fade + (if computer.mouse.down then + 0.2 + + else + 1 + ) + ] + + +update computer memory = + memory + """ + } + + +numbers : Example +numbers = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "elm" "random" (V.Version 1 0 0) + ] + , indirect = + [ Package "elm" "json" (V.Version 1 1 3) + , Package "elm" "time" (V.Version 1 0 0) + , Package "elm" "url" (V.Version 1 0 0) + , Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + , content = String.trim """ +module Main exposing (main) + +-- Press a button to generate a random number between 1 and 6. +-- +-- Read how it works: +-- https://guide.elm-lang.org/effects/random.html + +import Browser +import Html exposing (..) +import Html.Events exposing (..) +import Random + + + +-- MAIN + + +main = + Browser.element + { init = init + , update = update + , subscriptions = subscriptions + , view = view + } + + + +-- MODEL + + +type alias Model = + { dieFace : Int + } + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( Model 1 + , Cmd.none + ) + + + +-- UPDATE + + +type Msg + = Roll + | NewFace Int + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + Roll -> + ( model + , Random.generate NewFace (Random.int 1 6) + ) + + NewFace newFace -> + ( Model newFace + , Cmd.none + ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : Model -> Html Msg +view model = + div [] + [ h1 [] [ text (String.fromInt model.dieFace) ] + , button [ onClick Roll ] [ text "Roll" ] + ] + """ + } + + +picture : Example +picture = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "evancz" "elm-playground" (V.Version 1 0 3) + ] + , indirect = + [ Package "elm" "json" (V.Version 1 1 3) + , Package "elm" "svg" (V.Version 1 0 1) + , Package "elm" "time" (V.Version 1 0 0) + , Package "elm" "url" (V.Version 1 0 0) + , Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + , content = String.trim """ +module Main exposing (main) + +-- Create pictures from simple shapes. Like a tree! +-- +-- Learn more about the playground here: +-- https://package.elm-lang.org/packages/evancz/elm-playground/latest/ + +import Playground exposing (..) + + +main = + picture + [ rectangle brown 40 200 + |> moveDown 80 + , circle green 100 + |> moveUp 100 + ] + """ + } + + +positions : Example +positions = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "elm" "random" (V.Version 1 0 0) + ] + , indirect = + [ Package "elm" "json" (V.Version 1 1 3) + , Package "elm" "time" (V.Version 1 0 0) + , Package "elm" "url" (V.Version 1 0 0) + , Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + , content = String.trim """ +module Main exposing (main) + +-- A button that moves to random positions when pressed. +-- +-- Dependencies: +-- guida install elm/random + +import Browser +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Random + + + +-- MAIN + + +main = + Browser.element + { init = init + , update = update + , subscriptions = subscriptions + , view = view + } + + + +-- MODEL + + +type alias Model = + { x : Int + , y : Int + } + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( Model 100 100 + , Cmd.none + ) + + + +-- UPDATE + + +type Msg + = Clicked + | NewPosition ( Int, Int ) + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + Clicked -> + ( model + , Random.generate NewPosition positionGenerator + ) + + NewPosition ( x, y ) -> + ( Model x y + , Cmd.none + ) + + +positionGenerator : Random.Generator ( Int, Int ) +positionGenerator = + Random.map2 Tuple.pair + (Random.int 50 350) + (Random.int 50 350) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : Model -> Html Msg +view model = + button + [ style "position" "absolute" + , style "top" (String.fromInt model.x ++ "px") + , style "left" (String.fromInt model.y ++ "px") + , onClick Clicked + ] + [ text "Click me!" ] + """ + } + + +quotes : Example +quotes = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "elm" "http" (V.Version 2 0 0) + , Package "elm" "json" (V.Version 1 1 3) + ] + , indirect = + [ Package "elm" "bytes" (V.Version 1 0 8) + , Package "elm" "file" (V.Version 1 0 5) + , Package "elm" "time" (V.Version 1 0 0) + , Package "elm" "url" (V.Version 1 0 0) + , Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + , content = String.trim """ +module Main exposing (main) + +-- Press a button to send a GET request for random quotes. +-- +-- Read how it works: +-- https://guide.elm-lang.org/effects/json.html + +import Browser +import Html exposing (..) +import Html.Attributes exposing (style) +import Html.Events exposing (..) +import Http +import Json.Decode exposing (Decoder, field, int, map4, string) + + + +-- MAIN + + +main = + Browser.element + { init = init + , update = update + , subscriptions = subscriptions + , view = view + } + + + +-- MODEL + + +type Model + = Failure + | Loading + | Success Quote + + +type alias Quote = + { quote : String + , source : String + , author : String + , year : Int + } + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( Loading, getRandomQuote ) + + + +-- UPDATE + + +type Msg + = MorePlease + | GotQuote (Result Http.Error Quote) + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + MorePlease -> + ( Loading, getRandomQuote ) + + GotQuote result -> + case result of + Ok quote -> + ( Success quote, Cmd.none ) + + Err _ -> + ( Failure, Cmd.none ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : Model -> Html Msg +view model = + div [] + [ h2 [] [ text "Random Quotes" ] + , viewQuote model + ] + + +viewQuote : Model -> Html Msg +viewQuote model = + case model of + Failure -> + div [] + [ text "I could not load a random quote for some reason. " + , button [ onClick MorePlease ] [ text "Try Again!" ] + ] + + Loading -> + text "Loading..." + + Success quote -> + div [] + [ button [ onClick MorePlease, style "display" "block" ] [ text "More Please!" ] + , blockquote [] [ text quote.quote ] + , p [ style "text-align" "right" ] + [ text "— " + , cite [] [ text quote.source ] + , text (" by " ++ quote.author ++ " (" ++ String.fromInt quote.year ++ ")") + ] + ] + + + +-- HTTP + + +getRandomQuote : Cmd Msg +getRandomQuote = + Http.get + { url = "https://elm-lang.org/api/random-quotes" + , expect = Http.expectJson GotQuote quoteDecoder + } + + +quoteDecoder : Decoder Quote +quoteDecoder = + map4 Quote + (field "quote" string) + (field "source" string) + (field "author" string) + (field "year" int) + """ + } + + +shapes : Example +shapes = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "elm" "svg" (V.Version 1 0 1) + ] + , indirect = + [ Package "elm" "json" (V.Version 1 1 3) + , Package "elm" "time" (V.Version 1 0 0) + , Package "elm" "url" (V.Version 1 0 0) + , Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + , content = String.trim """ +module Main exposing (main) + +-- Scalable Vector Graphics (SVG) can be a nice way to draw things in 2D. +-- Here are some common SVG shapes. +-- +-- Dependencies: +-- guida install elm/svg + +import Html exposing (Html) +import Svg exposing (..) +import Svg.Attributes exposing (..) + + +main : Html msg +main = + svg + [ viewBox "0 0 400 400" + , width "400" + , height "400" + ] + [ circle + [ cx "50" + , cy "50" + , r "40" + , fill "red" + , stroke "black" + , strokeWidth "3" + ] + [] + , rect + [ x "100" + , y "10" + , width "40" + , height "40" + , fill "green" + , stroke "black" + , strokeWidth "2" + ] + [] + , line + [ x1 "20" + , y1 "200" + , x2 "200" + , y2 "20" + , stroke "blue" + , strokeWidth "10" + , strokeLinecap "round" + ] + [] + , polyline + [ points "200,40 240,40 240,80 280,80 280,120 320,120 320,160" + , fill "none" + , stroke "red" + , strokeWidth "4" + , strokeDasharray "20,2" + ] + [] + , text_ + [ x "130" + , y "130" + , fill "black" + , textAnchor "middle" + , dominantBaseline "central" + , transform "rotate(-45 130,130)" + ] + [ text "Welcome to Shapes Club" + ] + ] + + + +-- There are a lot of odd things about SVG, so always try to find examples +-- to help you understand the weird stuff. Like these: +-- +-- https://www.w3schools.com/graphics/svg_examples.asp +-- https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d +-- +-- If you cannot find relevant examples, make an experiment. If you push +-- through the weirdness, you can do a lot with SVG. + """ + } + + +textFields : Example +textFields = + { direct = Defaults.direct + , indirect = Defaults.indirect + , content = String.trim """ +module Main exposing (main) + +-- A text input for reversing text. Very useful! +-- +-- Read how it works: +-- https://guide.elm-lang.org/architecture/text_fields.html + +import Browser +import Html exposing (Attribute, Html, div, input, text) +import Html.Attributes exposing (..) +import Html.Events exposing (onInput) + + + +-- MAIN + + +main = + Browser.sandbox { init = init, update = update, view = view } + + + +-- MODEL + + +type alias Model = + { content : String + } + + +init : Model +init = + { content = "" } + + + +-- UPDATE + + +type Msg + = Change String + + +update : Msg -> Model -> Model +update msg model = + case msg of + Change newContent -> + { model | content = newContent } + + + +-- VIEW + + +view : Model -> Html Msg +view model = + div [] + [ input [ placeholder "Text to reverse", value model.content, onInput Change ] [] + , div [] [ text (String.reverse model.content) ] + ] + """ + } + + +thwomp : Example +thwomp = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "elm" "json" (V.Version 1 1 3) + , Package "elm-explorations" "linear-algebra" (V.Version 1 0 3) + , Package "elm-explorations" "webgl" (V.Version 1 1 3) + ] + , indirect = + [ Package "elm" "time" (V.Version 1 0 0) + , Package "elm" "url" (V.Version 1 0 0) + , Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + , content = String.trim """ +module Main exposing (main) + +-- Thwomp looks at your mouse. What is it up to? +-- +-- Dependencies: +-- guida install elm/json +-- guida install elm-explorations/linear-algebra +-- guida install elm-explorations/webgl +-- +-- Thanks to The PaperNES Guy for the texture: +-- https://the-papernes-guy.deviantart.com/art/Thwomps-Thwomps-Thwomps-186879685 + +import Browser +import Browser.Dom as Dom +import Browser.Events as E +import Html exposing (Html) +import Html.Attributes exposing (height, style, width) +import Json.Decode as D +import Math.Matrix4 as Mat4 exposing (Mat4) +import Math.Vector2 as Vec2 exposing (Vec2, vec2) +import Math.Vector3 as Vec3 exposing (Vec3, vec3) +import Result +import Task +import WebGL +import WebGL.Texture as Texture + + + +-- MAIN + + +main : Program () Model Msg +main = + Browser.element + { init = init + , view = view + , update = \\msg model -> ( update msg model, Cmd.none ) + , subscriptions = subscriptions + } + + + +-- MODEL + + +type alias Model = + { width : Float + , height : Float + , x : Float + , y : Float + , side : Maybe Texture.Texture + , face : Maybe Texture.Texture + } + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( { width = 0 + , height = 0 + , x = 0 + , y = 0 + , face = Nothing + , side = Nothing + } + , Cmd.batch + [ Task.perform GotViewport Dom.getViewport + , Task.attempt GotFace (Texture.loadWith options "/images/thwomp-face.jpg") + , Task.attempt GotSide (Texture.loadWith options "/images/thwomp-side.jpg") + ] + ) + + +options : Texture.Options +options = + { magnify = Texture.nearest + , minify = Texture.nearest + , horizontalWrap = Texture.repeat + , verticalWrap = Texture.repeat + , flipY = True + } + + + +-- UPDATE + + +type Msg + = GotFace (Result Texture.Error Texture.Texture) + | GotSide (Result Texture.Error Texture.Texture) + | GotViewport Dom.Viewport + | Resized Int Int + | MouseMoved Float Float + + +update : Msg -> Model -> Model +update msg model = + case msg of + GotFace result -> + { model + | face = + Result.toMaybe result + } + + GotSide result -> + { model + | side = + Result.toMaybe result + } + + GotViewport { viewport } -> + { model + | width = + viewport.width + , height = + viewport.height + } + + Resized width height -> + { model + | width = + toFloat width + , height = + toFloat height + } + + MouseMoved x y -> + { model + | x = + x + , y = + y + } + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions _ = + Sub.batch + [ E.onResize Resized + , E.onMouseMove decodeMovement + ] + + +decodeMovement : D.Decoder Msg +decodeMovement = + D.map2 MouseMoved + (D.field "pageX" D.float) + (D.field "pageY" D.float) + + + +-- VIEW + + +view : Model -> Html Msg +view model = + case Maybe.map2 Tuple.pair model.face model.side of + Nothing -> + Html.text "Loading textures..." + + Just ( face, side ) -> + let + perspective = + toPerspective model.x model.y model.width model.height + in + WebGL.toHtml + [ style "display" "block" + , style "position" "absolute" + , style "left" "0" + , style "top" "0" + , width (round model.width) + , height (round model.height) + ] + [ WebGL.entity vertexShader + fragmentShader + faceMesh + { perspective = perspective + , texture = face + } + , WebGL.entity vertexShader + fragmentShader + sidesMesh + { perspective = perspective + , texture = side + } + ] + + +toPerspective : Float -> Float -> Float -> Float -> Mat4 +toPerspective x y width height = + let + eye = + Vec3.scale 6 <| + Vec3.normalize <| + vec3 (0.5 - x / width) (y / height - 0.5) 1 + in + Mat4.mul + (Mat4.makePerspective 45 (width / height) 0.01 100) + (Mat4.makeLookAt eye (vec3 0 0 0) Vec3.j) + + + +-- MESHES + + +type alias Vertex = + { position : Vec3 + , coord : Vec2 + } + + +faceMesh : WebGL.Mesh Vertex +faceMesh = + WebGL.triangles square + + +sidesMesh : WebGL.Mesh Vertex +sidesMesh = + WebGL.triangles <| + List.concatMap rotatedSquare <| + [ ( 90, 0 ) + , ( 180, 0 ) + , ( 270, 0 ) + , ( 0, 90 ) + , ( 0, 270 ) + ] + + +rotatedSquare : ( Float, Float ) -> List ( Vertex, Vertex, Vertex ) +rotatedSquare ( angleXZ, angleYZ ) = + let + transformMat = + Mat4.mul + (Mat4.makeRotate (degrees angleXZ) Vec3.j) + (Mat4.makeRotate (degrees angleYZ) Vec3.i) + + transform vertex = + { vertex | position = Mat4.transform transformMat vertex.position } + + transformTriangle ( a, b, c ) = + ( transform a, transform b, transform c ) + in + List.map transformTriangle square + + +square : List ( Vertex, Vertex, Vertex ) +square = + let + topLeft = + Vertex (vec3 -1 1 1) (vec2 0 1) + + topRight = + Vertex (vec3 1 1 1) (vec2 1 1) + + bottomLeft = + Vertex (vec3 -1 -1 1) (vec2 0 0) + + bottomRight = + Vertex (vec3 1 -1 1) (vec2 1 0) + in + [ ( topLeft, topRight, bottomLeft ) + , ( bottomLeft, topRight, bottomRight ) + ] + + + +-- SHADERS + + +type alias Uniforms = + { perspective : Mat4 + , texture : Texture.Texture + } + + +vertexShader : WebGL.Shader Vertex Uniforms { vcoord : Vec2 } +vertexShader = + [glsl| + attribute vec3 position; + attribute vec2 coord; + uniform mat4 perspective; + varying vec2 vcoord; + + void main () { + gl_Position = perspective * vec4(position, 1.0); + vcoord = coord.xy; + } + |] + + +fragmentShader : WebGL.Shader {} Uniforms { vcoord : Vec2 } +fragmentShader = + [glsl| + precision mediump float; + uniform sampler2D texture; + varying vec2 vcoord; + + void main () { + gl_FragColor = texture2D(texture, vcoord); + } + |] + """ + } + + +time : Example +time = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "elm" "time" (V.Version 1 0 0) + ] + , indirect = + [ Package "elm" "json" (V.Version 1 1 3) + , Package "elm" "url" (V.Version 1 0 0) + , Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + , content = String.trim """ +module Main exposing (main) + +-- Show the current time in your time zone. +-- +-- Read how it works: +-- https://guide.elm-lang.org/effects/time.html +-- +-- For an analog clock, check out this SVG example: +-- https://guida-lang.org/examples/clock + +import Browser +import Html exposing (..) +import Task +import Time + + + +-- MAIN + + +main = + Browser.element + { init = init + , view = view + , update = update + , subscriptions = subscriptions + } + + + +-- MODEL + + +type alias Model = + { zone : Time.Zone + , time : Time.Posix + } + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( Model Time.utc (Time.millisToPosix 0) + , Task.perform AdjustTimeZone Time.here + ) + + + +-- UPDATE + + +type Msg + = Tick Time.Posix + | AdjustTimeZone Time.Zone + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + Tick newTime -> + ( { model | time = newTime } + , Cmd.none + ) + + AdjustTimeZone newZone -> + ( { model | zone = newZone } + , Cmd.none + ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Time.every 1000 Tick + + + +-- VIEW + + +view : Model -> Html Msg +view model = + let + hour = + String.fromInt (Time.toHour model.zone model.time) + + minute = + String.fromInt (Time.toMinute model.zone model.time) + + second = + String.fromInt (Time.toSecond model.zone model.time) + in + h1 [] [ text (hour ++ ":" ++ minute ++ ":" ++ second) ] + """ + } + + +triangle : Example +triangle = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "elm-explorations" "linear-algebra" (V.Version 1 0 3) + , Package "elm-explorations" "webgl" (V.Version 1 1 3) + ] + , indirect = + [ Package "elm" "json" (V.Version 1 1 3) + , Package "elm" "time" (V.Version 1 0 0) + , Package "elm" "url" (V.Version 1 0 0) + , Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + , content = String.trim """ +module Main exposing (main) + +-- guida install elm-explorations/linear-algebra +-- guida install elm-explorations/webgl + +import Browser +import Browser.Events as E +import Html exposing (Html) +import Html.Attributes exposing (height, style, width) +import Math.Matrix4 as Mat4 exposing (Mat4) +import Math.Vector3 as Vec3 exposing (Vec3, vec3) +import WebGL + + + +-- MAIN + + +main = + Browser.element + { init = init + , view = view + , update = update + , subscriptions = subscriptions + } + + + +-- MODEL + + +type alias Model = + Float + + +init : () -> ( Model, Cmd Msg ) +init () = + ( 0, Cmd.none ) + + + +-- UPDATE + + +type Msg + = TimeDelta Float + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg currentTime = + case msg of + TimeDelta delta -> + ( delta + currentTime, Cmd.none ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions _ = + E.onAnimationFrameDelta TimeDelta + + + +-- VIEW + + +view : Model -> Html msg +view t = + WebGL.toHtml + [ width 400 + , height 400 + , style "display" "block" + ] + [ WebGL.entity vertexShader fragmentShader mesh { perspective = perspective (t / 1000) } + ] + + +perspective : Float -> Mat4 +perspective t = + Mat4.mul + (Mat4.makePerspective 45 1 0.01 100) + (Mat4.makeLookAt (vec3 (4 * cos t) 0 (4 * sin t)) (vec3 0 0 0) (vec3 0 1 0)) + + + +-- MESH + + +type alias Vertex = + { position : Vec3 + , color : Vec3 + } + + +mesh : WebGL.Mesh Vertex +mesh = + WebGL.triangles + [ ( Vertex (vec3 0 0 0) (vec3 1 0 0) + , Vertex (vec3 1 1 0) (vec3 0 1 0) + , Vertex (vec3 1 -1 0) (vec3 0 0 1) + ) + ] + + + +-- SHADERS + + +type alias Uniforms = + { perspective : Mat4 + } + + +vertexShader : WebGL.Shader Vertex Uniforms { vcolor : Vec3 } +vertexShader = + [glsl| + attribute vec3 position; + attribute vec3 color; + uniform mat4 perspective; + varying vec3 vcolor; + + void main () { + gl_Position = perspective * vec4(position, 1.0); + vcolor = color; + } + |] + + +fragmentShader : WebGL.Shader {} Uniforms { vcolor : Vec3 } +fragmentShader = + [glsl| + precision mediump float; + varying vec3 vcolor; + + void main () { + gl_FragColor = vec4(vcolor, 1.0); + } + |] + """ + } + + +turtle : Example +turtle = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "evancz" "elm-playground" (V.Version 1 0 3) + ] + , indirect = + [ Package "elm" "json" (V.Version 1 1 3) + , Package "elm" "svg" (V.Version 1 0 1) + , Package "elm" "time" (V.Version 1 0 0) + , Package "elm" "url" (V.Version 1 0 0) + , Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + , content = String.trim """ +module Main exposing (main) + +-- Use arrow keys to move the turtle around. +-- +-- Forward with UP and turn with LEFT and RIGHT. +-- +-- Learn more about the playground here: +-- https://package.elm-lang.org/packages/evancz/elm-playground/latest/ + +import Playground exposing (..) + + +main = + game view + update + { x = 0 + , y = 0 + , angle = 0 + } + + +view computer turtle = + [ rectangle blue computer.screen.width computer.screen.height + , image 96 96 "/images/turtle.gif" + |> move turtle.x turtle.y + |> rotate turtle.angle + ] + + +update computer turtle = + { x = turtle.x + toY computer.keyboard * cos (degrees turtle.angle) + , y = turtle.y + toY computer.keyboard * sin (degrees turtle.angle) + , angle = turtle.angle - toX computer.keyboard + } + """ + } + + +upload : Example +upload = + { direct = + [ Package "elm" "browser" (V.Version 1 0 2) + , Package "elm" "core" (V.Version 1 0 5) + , Package "elm" "file" (V.Version 1 0 5) + , Package "elm" "html" (V.Version 1 0 0) + , Package "elm" "json" (V.Version 1 1 3) + ] + , indirect = + [ Package "elm" "bytes" (V.Version 1 0 8) + , Package "elm" "time" (V.Version 1 0 0) + , Package "elm" "url" (V.Version 1 0 0) + , Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + , content = String.trim """ +module Main exposing (main) + +-- File upload with the node. +-- +-- Dependencies: +-- guida install elm/file +-- guida install elm/json + +import Browser +import File exposing (File) +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Json.Decode as D + + + +-- MAIN + + +main = + Browser.element + { init = init + , view = view + , update = update + , subscriptions = subscriptions + } + + + +-- MODEL + + +type alias Model = + List File + + +init : () -> ( Model, Cmd Msg ) +init _ = + ( [], Cmd.none ) + + + +-- UPDATE + + +type Msg + = GotFiles (List File) + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + GotFiles files -> + ( files, Cmd.none ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : Model -> Html Msg +view model = + div [] + [ input + [ type_ "file" + , multiple True + , on "change" (D.map GotFiles filesDecoder) + ] + [] + , div [] [ text (Debug.toString model) ] + ] + + +filesDecoder : D.Decoder (List File) +filesDecoder = + D.at [ "target", "files" ] (D.list File.decoder) + """ + } diff --git a/src/Data/Exit.elm b/src/Data/Exit.elm new file mode 100644 index 0000000..a08942c --- /dev/null +++ b/src/Data/Exit.elm @@ -0,0 +1,107 @@ +module Data.Exit exposing (Exit(..), decoder, toString, toStringDetails) + +import Json.Decode as JD +import Json.Encode as JE + + +type Exit + = RequestBadPackageRegistry + | RequestBadArtifacts String + | RequestBodySizeTooLarge Int + | RequestFormLargerThan32KB + | RequestBodyDecodingFailed + | RequestFormSizeTooLarge Int + | RequestFormTooManyFields + | RequestUnknownError String + + +toString : Exit -> String +toString exit = + case exit of + RequestBodySizeTooLarge _ -> + "BodySizeTooLarge" + + RequestFormLargerThan32KB -> + "FormLargerThan32KB" + + RequestBodyDecodingFailed -> + "BodyDecodingFailed" + + RequestFormSizeTooLarge _ -> + "FormSizeTooLarge" + + RequestFormTooManyFields -> + "FormTooManyFields" + + RequestUnknownError _ -> + "UnknownError" + + RequestBadPackageRegistry -> + "BadPackageRegistry" + + RequestBadArtifacts _ -> + "BadArtifacts" + + +toStringDetails : Exit -> String +toStringDetails exit = + case exit of + RequestBodySizeTooLarge size -> + "{ size: " ++ String.fromInt size ++ " }" + + RequestFormLargerThan32KB -> + "" + + RequestBodyDecodingFailed -> + "" + + RequestFormSizeTooLarge size -> + "{ size: " ++ String.fromInt size ++ " }" + + RequestFormTooManyFields -> + "" + + RequestUnknownError message -> + "{ message: " ++ message ++ " }" + + RequestBadPackageRegistry -> + "" + + RequestBadArtifacts path -> + "{ path: " ++ path ++ " }" + + +decoder : JD.Decoder Exit +decoder = + let + decodeExtras str = + case str of + "RequestBodySizeTooLarge" -> + JD.map RequestBodySizeTooLarge (JD.field "size" JD.int) + + "RequestFormLargerThan32KB" -> + JD.succeed RequestFormLargerThan32KB + + "RequestBodyDecodingFailed" -> + JD.succeed RequestBodyDecodingFailed + + "RequestFormSizeTooLarge" -> + JD.map RequestFormSizeTooLarge (JD.field "size" JD.int) + + "RequestFormTooManyFields" -> + JD.succeed RequestFormTooManyFields + + "RequestUnknownError" -> + JD.map RequestUnknownError (JD.field "message" JD.string) + + "RequestBadPackageRegistry" -> + JD.succeed RequestBadPackageRegistry + + "RequestBadArtifacts" -> + JD.map RequestBadArtifacts (JD.field "path" JD.string) + + _ -> + JD.succeed (RequestUnknownError "Could not decode unknown error status") + in + JD.field "exit" JD.string + |> JD.andThen decodeExtras diff --git a/src/Data/Fetched.elm b/src/Data/Fetched.elm new file mode 100644 index 0000000..4517592 --- /dev/null +++ b/src/Data/Fetched.elm @@ -0,0 +1,71 @@ +module Data.Fetched exposing + ( Fetched(..) + , map + , map2 + , withDefault + ) + +import Http + + +type Fetched a + = Loading + | Success a + | Failed Http.Error + + +map : (a -> b) -> Fetched a -> Fetched b +map func ma = + case ma of + Success a -> + Success (func a) + + Loading -> + Loading + + Failed err -> + Failed err + + +map2 : (a -> b -> c) -> Fetched a -> Fetched b -> Fetched c +map2 func ma mb = + case ( ma, mb ) of + ( Success a, Success b ) -> + Success (func a b) + + ( Success a, Loading ) -> + Loading + + ( Success a, Failed err ) -> + Failed err + + ( Loading, Success b ) -> + Loading + + ( Loading, Loading ) -> + Loading + + ( Loading, Failed err ) -> + Failed err + + ( Failed err, Success b ) -> + Failed err + + ( Failed err, Loading ) -> + Failed err + + ( Failed err, Failed _ ) -> + Failed err + + +withDefault : a -> Fetched a -> a +withDefault default ma = + case ma of + Loading -> + default + + Success a -> + a + + Failed _ -> + default diff --git a/src/Data/Frame.elm b/src/Data/Frame.elm new file mode 100644 index 0000000..eb20ede --- /dev/null +++ b/src/Data/Frame.elm @@ -0,0 +1,251 @@ +module Data.Frame exposing + ( Frame(..) + , ceiling + , floor + , format + , formatLong + , hasPrev + , isWithin + , isWithinPrev + , toInterval + , toMs + , toString + , toTimes + ) + +import Data.Registry.Package as Pkg +import Data.Time +import DateFormat as F +import Dict exposing (Dict) +import Http +import Json.Decode as JD +import Time + + +type Frame + = Days14 + | Days7 + | Days2 + | Hours24 + | Hours12 + + + +-- TRANSLATIONS + + +toMs : Frame -> Int +toMs frame = + case frame of + Days14 -> + 1000 * 60 * 60 * 24 * 14 + + Days7 -> + 1000 * 60 * 60 * 24 * 7 + + Days2 -> + 1000 * 60 * 60 * 24 * 2 + + Hours24 -> + 1000 * 60 * 60 * 24 + + Hours12 -> + 1000 * 60 * 60 * 12 + + +toInterval : Frame -> Int +toInterval frame = + case frame of + Days14 -> + 1000 * 60 * 60 * 24 + + Days7 -> + 1000 * 60 * 60 * 12 + + Days2 -> + 1000 * 60 * 60 * 2 + + Hours24 -> + 1000 * 60 * 60 + + Hours12 -> + 1000 * 60 * 30 + + +toString : Frame -> String +toString frame = + case frame of + Days14 -> + "14 days" + + Days7 -> + "7 days" + + Days2 -> + "2 days" + + Hours24 -> + "24 hours" + + Hours12 -> + "12 hours" + + +toTimes : Time.Zone -> Time.Posix -> Frame -> List Int +toTimes zone now frame = + let + interval = + toInterval frame + + amount = + toMs frame // interval + + first = + Time.posixToMillis (floor zone frame now) + + toTime mult = + first - mult * interval + in + List.map toTime (List.range 0 amount) + + + +-- QUESTIONS + + +hasPrev : Frame -> Bool +hasPrev frame = + case frame of + Days14 -> + False + + Days7 -> + True + + Days2 -> + True + + Hours24 -> + True + + Hours12 -> + True + + +isWithin : Time.Posix -> Frame -> Int -> Bool +isWithin now frame timestamp = + let + frameMs = + toMs frame + + nowMs = + Time.posixToMillis now + + lowerLimit = + nowMs - frameMs + in + timestamp > lowerLimit + + +isWithinPrev : Time.Posix -> Frame -> Int -> Bool +isWithinPrev now frame timestamp = + let + frameMs = + toMs frame + + nowMs = + Time.posixToMillis now + + lowerLimit = + nowMs - frameMs * 2 + + upperLimit = + nowMs - frameMs + in + timestamp > lowerLimit && timestamp <= upperLimit + + + +-- MANIPULATION + + +ceiling : Time.Zone -> Frame -> Time.Posix -> Time.Posix +ceiling zone frame = + case frame of + Days14 -> + Data.Time.ceilingDay zone 1 + + Days7 -> + Data.Time.ceilingHour zone 12 + + Days2 -> + Data.Time.ceilingHour zone 2 + + Hours24 -> + Data.Time.ceilingHour zone 1 + + Hours12 -> + Data.Time.ceilingMinute zone 30 + + +floor : Time.Zone -> Frame -> Time.Posix -> Time.Posix +floor zone frame = + case frame of + Days14 -> + Data.Time.floorDay zone 1 + + Days7 -> + Data.Time.floorHour zone 12 + + Days2 -> + Data.Time.floorHour zone 2 + + Hours24 -> + Data.Time.floorHour zone 1 + + Hours12 -> + Data.Time.floorMinute zone 30 + + + +-- FORMATTING + + +format : Frame -> Time.Zone -> Time.Posix -> String +format frame = + F.format <| + case frame of + Days14 -> + [ F.monthNameAbbreviated, F.text " ", F.dayOfMonthSuffix ] + + Days7 -> + [ F.dayOfMonthNumber, F.text "/", F.monthNumber, F.text " ", F.hourNumber, F.amPmLowercase ] + + Days2 -> + [ F.hourMilitaryFixed, F.text ":", F.minuteFixed ] + + Hours24 -> + [ F.hourMilitaryFixed, F.text ":", F.minuteFixed ] + + Hours12 -> + [ F.hourMilitaryFixed, F.text ":", F.minuteFixed ] + + +formatLong : Frame -> Time.Zone -> Time.Posix -> String +formatLong frame = + F.format <| + case frame of + Days14 -> + [ F.monthNameAbbreviated, F.text " ", F.dayOfMonthSuffix ] + + Days7 -> + [ F.monthNameAbbreviated, F.text " ", F.dayOfMonthSuffix ] + + Days2 -> + [ F.monthNameAbbreviated, F.text " ", F.dayOfMonthSuffix, F.text " ", F.hourMilitaryFixed, F.text ":", F.minuteFixed ] + + Hours24 -> + [ F.monthNameAbbreviated, F.text " ", F.dayOfMonthSuffix, F.text " ", F.hourMilitaryFixed, F.text ":", F.minuteFixed ] + + Hours12 -> + [ F.monthNameAbbreviated, F.text " ", F.dayOfMonthSuffix, F.text " ", F.hourMilitaryFixed, F.text ":", F.minuteFixed ] diff --git a/src/Data/Header.elm b/src/Data/Header.elm new file mode 100644 index 0000000..389bcb4 --- /dev/null +++ b/src/Data/Header.elm @@ -0,0 +1,345 @@ +module Data.Header exposing + ( Exposed + , Exposing(..) + , Import + , Imports(..) + , defaultImports + , parse + ) + +import Dict exposing (Dict) +import Parser exposing (..) +import Set exposing (Set) + + + +-- IMPORTS + + +type Imports + = Imports (List Import) + + +defaultImports : Imports +defaultImports = + Imports defaults + + + +-- IMPORT + + +type alias Import = + { module_ : String + , alias_ : Maybe String + , exposing_ : Exposing + } + + +type Exposing + = All + | Some Exposed + | None + + +type alias Exposed = + { ops : Set String + , lower : Set String + , upper : Dict String Bool + } + + + +-- PARSE + + +parse : String -> Maybe ( Imports, Int ) +parse source = + Result.toMaybe (Parser.run parser source) + + +parser : Parser ( Imports, Int ) +parser = + succeed Tuple.pair + |. whitespace + |. newline + |. oneOf + [ module_ + |. whitespace + |. newline + , succeed () + ] + |= imports + |= getRow + + + +-- MODULE + + +module_ : Parser () +module_ = + oneOf [ keyword "port" |. whitespace, succeed () ] + |. keyword "module" + |. whitespace + |. moduleName + |. whitespace + |. exposing_ + + + +-- IMPORTS + + +imports : Parser Imports +imports = + loop defaults importsHelp + + +importsHelp : List Import -> Parser (Step (List Import) Imports) +importsHelp revImports = + oneOf + [ map (\i -> Loop (i :: revImports)) import_ + , succeed (Done (Imports revImports)) + ] + + + +-- IMPORT + + +import_ : Parser Import +import_ = + succeed Import + |. keyword "import" + |. whitespace + |= moduleName + |. whitespace + |= oneOf + [ succeed Just + |. keyword "as" + |. whitespace + |= upper + |. whitespace + , succeed Nothing + ] + |= oneOf + [ exposing_ + , succeed None + ] + |. whitespace + |. newline + + + +-- EXPOSING + + +exposing_ : Parser Exposing +exposing_ = + succeed identity + |. keyword "exposing" + |. whitespace + |= oneOf + [ backtrackable (exposingDotDot All) + , map toExposing <| + sequence + { start = "(" + , separator = "," + , end = ")" + , spaces = whitespace + , item = exposedItem + , trailing = Forbidden + } + ] + + +toExposing : List ExposedItem -> Exposing +toExposing items = + Some <| List.foldl addItem (Exposed Set.empty Set.empty Dict.empty) items + + +addItem : ExposedItem -> Exposed -> Exposed +addItem item exposed = + case item of + Op name -> + { exposed | ops = Set.insert name exposed.ops } + + Lower name -> + { exposed | lower = Set.insert name exposed.lower } + + Upper name bool -> + { exposed | upper = Dict.insert name bool exposed.upper } + + + +-- EXPOSED ITEM + + +type ExposedItem + = Op String + | Lower String + | Upper String Bool + + +exposedItem : Parser ExposedItem +exposedItem = + oneOf + [ map Lower lower + , map Op operator + , succeed Upper + |= upper + |. whitespace + |= oneOf + [ exposingDotDot True + , succeed False + ] + ] + + +exposingDotDot : a -> Parser a +exposingDotDot value = + succeed value + |. symbol "(" + |. whitespace + |. symbol ".." + |. whitespace + |. symbol ")" + + + +-- VARIABLES +-- +-- Fairly loosey-goosey implementations here. +-- No checking for keywords and no checking for +-- valid module names. (e.g. Html....attrs) is +-- allowed. + + +lower : Parser String +lower = + variable + { start = Char.isLower + , inner = \c -> Char.isAlphaNum c || c == '_' + , reserved = Set.empty + } + + +upper : Parser String +upper = + variable + { start = Char.isUpper + , inner = \c -> Char.isAlphaNum c || c == '_' + , reserved = Set.empty + } + + +moduleName : Parser String +moduleName = + variable + { start = Char.isUpper + , inner = \c -> Char.isAlphaNum c || c == '_' || c == '.' + , reserved = Set.empty + } + + + +-- WHITESPACE + + +newline : Parser () +newline = + getCol + |> andThen + (\col -> + if col == 1 then + succeed () + + else + problem "" + ) + + +whitespace : Parser () +whitespace = + loop 0 spaceStepper + + +spaceStepper : Int -> Parser (Step Int ()) +spaceStepper oldOffset = + succeed + (\newOffset -> + if oldOffset == newOffset then + Done () + + else + Loop newOffset + ) + |. whitespaceChunk + |= getOffset + + +whitespaceChunk : Parser () +whitespaceChunk = + oneOf + [ lineComment "--" + , multiComment "{-" "-}" Nestable + , spaces + ] + + + +-- OPERATORS + + +operator : Parser String +operator = + succeed identity + |. symbol "(" + |= getChompedString (chompIf isOperatorChar |. chompWhile isOperatorChar) + |. symbol ")" + + +isOperatorChar : Char -> Bool +isOperatorChar char = + Set.member char operatorChars + + +operatorChars : Set.Set Char +operatorChars = + Set.fromList (String.toList "+-/*=.<>:&|^?%!") + + + +-- DEFAULTS + + +defaults : List Import +defaults = + [ Import "Basics" Nothing All + , Import "Debug" Nothing None + , Import "List" Nothing listExposed + , Import "Maybe" Nothing (typeOpen "Maybe") + , Import "Result" Nothing (typeOpen "Result") + , Import "String" Nothing (typeClosed "String") + , Import "Char" Nothing (typeClosed "Char") + , Import "Tuple" Nothing None + , Import "Platform" Nothing (typeClosed "Program") + , Import "Platform.Cmd" (Just "Cmd") (typeClosed "Cmd") + , Import "Platform.Sub" (Just "Sub") (typeClosed "Sub") + ] + + +typeOpen : String -> Exposing +typeOpen name = + Some <| Exposed Set.empty Set.empty (Dict.singleton name True) + + +typeClosed : String -> Exposing +typeClosed name = + Some <| Exposed Set.empty Set.empty (Dict.singleton name False) + + +listExposed : Exposing +listExposed = + Some <| Exposed (Set.singleton "::") Set.empty (Dict.singleton "List" False) diff --git a/src/Data/Hint.elm b/src/Data/Hint.elm new file mode 100644 index 0000000..e11c650 --- /dev/null +++ b/src/Data/Hint.elm @@ -0,0 +1,260 @@ +module Data.Hint exposing + ( Hint + , Info(..) + , Table + , buildTable + , defaultTable + , lookup + ) + +import Data.Deps as Deps +import Data.Header as Header +import Dict exposing (Dict) +import Set exposing (Set) + + + +-- TABLE + + +type Table + = Table (Dict String Info) + + +type Info + = Specific Hint + | Ambiguous + + +type alias Hint = + { text : String + , href : String + } + + + +-- LOOKUP + + +lookup : String -> Table -> Maybe Info +lookup name (Table table) = + Dict.get name table + + + +-- DEFAULT TABLE + + +defaultTable : Table +defaultTable = + Table keywordsAndConventions + + + +-- NOTE: The logic in editor.js normalizes certain keywords: +-- +-- 1. "type" is converted to "alias" if it appears to be in a type alias +-- 2. "as" is converted to "import" if it appears to be in an import +-- + + +keywordsAndConventions : Dict String Info +keywordsAndConventions = + let + hint t h = + Specific (Hint t h) + + if_ = + hint "If Expressions" "https://guide.elm-lang.org/core_language.html#if-expressions" + + tea_ = + hint "The Elm Architecture" "https://guide.elm-lang.org/architecture/" + + subs_ = + hint "Subscriptions" "https://guide.elm-lang.org/effects/" + + case_ = + hint "Pattern Matching" "https://guide.elm-lang.org/types/pattern_matching.html" + + import_ = + hint "Imports" "https://github.com/elm/compiler/blob/master/hints/imports.md" + in + Dict.fromList + [ ( "if", if_ ) + , ( "then", if_ ) + , ( "else", if_ ) + , ( "case", case_ ) + , ( "of", case_ ) + , ( "import", import_ ) + , ( "exposing", import_ ) + , ( ":", hint "Reading Types" "https://guide.elm-lang.org/types/reading_types.html" ) + , ( "type", hint "Custom Types" "https://guide.elm-lang.org/types/custom_types.html" ) + , ( "alias", hint "Type Aliases" "https://guide.elm-lang.org/types/type_aliases.html" ) + , ( "port", hint "Ports" "https://guide.elm-lang.org/interop/ports.html" ) + + -- CONVENTIONS + , ( "Model", tea_ ) + , ( "Msg", tea_ ) + , ( "Cmd", hint "Commands" "https://guide.elm-lang.org/effects/" ) + , ( "Sub", subs_ ) + , ( "def:view", tea_ ) + , ( "def:update", tea_ ) + , ( "def:init", tea_ ) + , ( "def:subscriptions", subs_ ) + + -- IMPROVE add hints for `let` and `as` patterns + -- , ("let", let_) + -- , ("in", let_) + -- , ("as", hint "As Patterns" "/IMPROVE") + ] + + + +-- BUILD TABLE + + +buildTable : Header.Imports -> Deps.Info -> Table +buildTable (Header.Imports imports) depsInfo = + Table <| + Dict.union keywordsAndConventions <| + List.foldl addHint Dict.empty (List.foldl (addImport depsInfo) [] imports) + + +addHint : ( String, Hint ) -> Dict String Info -> Dict String Info +addHint ( key, hint ) table = + case Dict.get key table of + Nothing -> + Dict.insert key (Specific hint) table + + Just info -> + case info of + Ambiguous -> + table + + Specific { text, href } -> + if hint.text == text && hint.href == href then + table + + else + Dict.insert key Ambiguous table + + + +-- ADD IMPORT + + +addImport : Deps.Info -> Header.Import -> List ( String, Hint ) -> List ( String, Hint ) +addImport depsInfo imp hints = + case Dict.get imp.module_ depsInfo of + Nothing -> + hints + + Just info -> + let + name = + imp.module_ + + url = + "https://package.elm-lang.org/packages/" ++ info.pkg ++ "/latest/" ++ name + in + hints + |> addQualified name imp.alias_ url info + |> addExposed name imp.exposing_ url info + + + +-- ADD QUALIFIED + + +addQualified : String -> Maybe String -> String -> Deps.ModuleInfo -> List ( String, Hint ) -> List ( String, Hint ) +addQualified moduleName maybeAlias moduleUrl info hints = + let + qualifier = + Maybe.withDefault moduleName maybeAlias + + toHint name = + ( qualifier ++ "." ++ name + , Hint (moduleName ++ "." ++ name) (moduleUrl ++ "#" ++ name) + ) + + toVariantHint typeName variant = + ( qualifier ++ "." ++ variant + , Hint (moduleName ++ "." ++ variant) (moduleUrl ++ "#" ++ typeName) + ) + in + ( "module:" ++ moduleName, Hint moduleName moduleUrl ) + :: hints + |> addNames toHint info.values + |> addNames toHint info.aliases + |> addUnions toHint toVariantHint info.unions + + +addNames : (a -> b) -> Set a -> List b -> List b +addNames func set list = + Set.foldr (\a bs -> func a :: bs) list set + + +addUnions : (k -> b) -> (k -> v -> b) -> Dict k (List v) -> List b -> List b +addUnions nameFunc variantFunc unions initialHints = + let + addVariant name variant hints = + variantFunc name variant :: hints + + addUnion name variants hints = + nameFunc name :: List.foldr (addVariant name) hints variants + in + Dict.foldr addUnion initialHints unions + + + +-- ADD EXPOSED + + +addExposed : String -> Header.Exposing -> String -> Deps.ModuleInfo -> List ( String, Hint ) -> List ( String, Hint ) +addExposed moduleName exposing_ moduleUrl info hints = + let + toHint name = + ( name, Hint (moduleName ++ "." ++ name) (moduleUrl ++ "#" ++ name) ) + + toOperatorHint op = + ( op, Hint ("(" ++ op ++ ")") (moduleUrl ++ "#" ++ op) ) + + toVariantHint typeName variant = + ( variant, Hint (moduleName ++ "." ++ variant) (moduleUrl ++ "#" ++ typeName) ) + in + case exposing_ of + Header.None -> + hints + + Header.All -> + hints + |> addNames toOperatorHint info.ops + |> addNames toHint info.aliases + |> addNames toHint info.values + |> addUnions toHint toVariantHint info.unions + + Header.Some { ops, lower, upper } -> + hints + |> addNames toOperatorHint (Set.intersect info.ops ops) + |> addNames toHint (Set.intersect info.values lower) + |> addNames toHint (Set.filter (\k -> Dict.member k upper) info.aliases) + |> addSomeUnions toHint toVariantHint info.unions upper + + +addSomeUnions : (String -> b) -> (String -> v -> b) -> Dict String (List v) -> Dict String Bool -> List b -> List b +addSomeUnions nameFunc variantFunc allUnions exposedUnions initialHints = + let + ignore _ _ hints = + hints + + addVariant name variant hints = + variantFunc name variant :: hints + + addUnion name variants isOpen hints = + if isOpen then + nameFunc name :: List.foldr (addVariant name) hints variants + + else + nameFunc name :: hints + in + Dict.merge ignore addUnion ignore allUnions exposedUnions initialHints diff --git a/src/Data/Http.elm b/src/Data/Http.elm new file mode 100644 index 0000000..acfda1b --- /dev/null +++ b/src/Data/Http.elm @@ -0,0 +1,58 @@ +module Data.Http exposing + ( Error(..) + , expectJson + , onlyDecodedErrors + ) + +import Http +import Json.Decode as D + + +type Error x + = ServerError Int x + | DecodeError Int D.Error + | HttpError Http.Error + + +onlyDecodedErrors : Error x -> Maybe x +onlyDecodedErrors err = + case err of + ServerError _ x -> + Just x + + DecodeError _ _ -> + Nothing + + HttpError _ -> + Nothing + + +expectJson : (Result (Error x) a -> msg) -> D.Decoder x -> D.Decoder a -> Http.Expect msg +expectJson toMsg decodeErr decodeOk = + Http.expectStringResponse toMsg <| + \response -> + case response of + Http.BadUrl_ url -> + Err (HttpError <| Http.BadUrl url) + + Http.Timeout_ -> + Err (HttpError Http.Timeout) + + Http.NetworkError_ -> + Err (HttpError Http.NetworkError) + + Http.BadStatus_ metadata body -> + case D.decodeString decodeErr body of + Ok value -> + Err (ServerError metadata.statusCode value) + + Err err -> + Err (DecodeError metadata.statusCode err) + + Http.GoodStatus_ metadata body -> + case D.decodeString decodeOk body of + Ok value -> + Ok value + + Err err -> + Err (DecodeError metadata.statusCode err) diff --git a/src/Data/Problem.elm b/src/Data/Problem.elm new file mode 100644 index 0000000..c98f1f8 --- /dev/null +++ b/src/Data/Problem.elm @@ -0,0 +1,254 @@ +module Data.Problem exposing + ( Location(..) + , Problem + , Problems + , focusNext + , focusPrevious + , generic + , getAll + , getFocused + , getNext + , getPlainText + , getPrevious + , getRegion + , getSummary + , hasNext + , hasPrevious + , init + , merge + , reindex + , toIndexedProblems + , toManyIndexedProblems + ) + +{-| The formatting of compilation errors. +-} + +import Elm.Error as Error +import Html exposing (..) +import Html.Attributes exposing (..) + + + +-- PROCESSING + + +type alias Problem = + { index : Int + , location : Location + , title : String + , message : List Error.Chunk + } + + +type Location + = General { path : Maybe String } + | Specific { path : String, name : String, region : Error.Region } + + +toIndexedProblems : Error.Error -> List Problem +toIndexedProblems errors = + case errors of + Error.GeneralProblem problem -> + [ { index = 0 + , location = General { path = problem.path } + , title = problem.title + , message = problem.message + } + ] + + Error.ModuleProblems modules -> + let + toModuleProblems module_ ( nextIndex, all ) = + List.foldr (toSingleProblem module_) ( nextIndex, all ) module_.problems + + toSingleProblem module_ problem ( nextIndex, all ) = + let + indexedProblem = + { index = nextIndex + , location = + Specific + { path = module_.path + , name = module_.name + , region = problem.region + } + , title = problem.title + , message = problem.message + } + in + ( nextIndex + 1, indexedProblem :: all ) + in + List.foldr toModuleProblems ( 0, [] ) modules + |> Tuple.second + + +toManyIndexedProblems : List Error.Error -> List Problem +toManyIndexedProblems errors = + reindex <| List.concatMap toIndexedProblems errors + + +reindex : List Problem -> List Problem +reindex = + List.indexedMap (\i p -> { p | index = i }) + + +getRegion : Problem -> Maybe Error.Region +getRegion problem = + case problem.location of + General _ -> + Nothing + + Specific specific -> + Just specific.region + + + +-- GENERIC ERROR + + +generic : Problem +generic = + { index = 0 + , location = General { path = Nothing } + , title = "INTERNAL ERROR" + , message = + [ Error.Unstyled "Oops, something went wrong.\n\nThe problem has been reported and will be addressed " + , Error.Unstyled "as soon as possible. Please\ntry again later!" + ] + } + + + +-- MANY + + +type alias Problems = + ( List Problem, Problem, List Problem ) + + +init : List Problem -> Maybe Problems +init pbs = + case pbs of + first :: rest -> + Just ( [], first, rest ) + + _ -> + Nothing + + +merge : Problems -> Problems -> Problems +merge ( prevA, currA, nextA ) pbs = + ( prevA, currA, nextA ++ getAll pbs ) + + +getAll : Problems -> List Problem +getAll ( prev, curr, next ) = + List.reverse prev ++ [ curr ] ++ next + + +getFocused : Problems -> Problem +getFocused ( _, current, _ ) = + current + + +getPrevious : Problems -> Maybe Problem +getPrevious ( prev, _, _ ) = + case prev of + first :: _ -> + Just first + + [] -> + Nothing + + +getNext : Problems -> Maybe Problem +getNext ( _, _, next ) = + case next of + first :: _ -> + Just first + + [] -> + Nothing + + +hasPrevious : Problems -> Bool +hasPrevious ( prev, curr, next ) = + not (List.isEmpty prev) + + +hasNext : Problems -> Bool +hasNext ( prev, curr, next ) = + not (List.isEmpty next) + + +focusNext : Problems -> Problems +focusNext pbs = + case pbs of + ( prev, curr, next :: remaining ) -> + ( curr :: prev, next, remaining ) + + ( prev, curr, [] ) -> + pbs + + +focusPrevious : Problems -> Problems +focusPrevious pbs = + case pbs of + ( prev :: remaining, curr, next ) -> + ( remaining, prev, curr :: next ) + + ( [], curr, next ) -> + pbs + + + +-- HELPERS + + +getSummary : Problem -> String +getSummary problem = + let + plainText = + getPlainText problem + + firstLine = + List.head (String.split "\n\n" plainText) + + firstColon = + List.head (String.split ":" plainText) + in + case ( firstLine, firstColon ) of + ( Just firstLine_, Just firstColon_ ) -> + if String.length firstLine_ > String.length firstColon_ then + firstColon_ + + else + firstLine_ + + ( Just firstLine_, Nothing ) -> + firstLine_ + + ( Nothing, Just firstColon_ ) -> + firstColon_ + + ( Nothing, Nothing ) -> + "Click to see full error message!" + + +getPlainText : Problem -> String +getPlainText problem = + let + toPlain chunks = + case chunks of + [] -> + "" + + chunk :: rest -> + case chunk of + Error.Unstyled string -> + string ++ toPlain rest + + Error.Styled _ string -> + string ++ toPlain rest + in + toPlain problem.message diff --git a/src/Data/Registry.elm b/src/Data/Registry.elm new file mode 100644 index 0000000..a82f677 --- /dev/null +++ b/src/Data/Registry.elm @@ -0,0 +1,289 @@ +port module Data.Registry exposing + ( Action(..) + , Registry + , attemptEdit + , dismissAll + , fetch + , filterKeys + , filterStatus + , fromNews + , fromSolution + , getErrors + , getValues + , initial + , initialWithDefaults + , insert + , installResult + , mapStatus + , search + , setStatus + , uninstallResult + , update + ) + +import Constant +import Data.Http +import Data.Registry.Defaults as Defaults +import Data.Registry.Package as Package +import Data.Registry.Solution as Solution +import Data.Registry.Status as Status +import Data.Version as V +import Dict exposing (Dict) +import Elm.Error as Error +import Http +import Json.Decode as Decode +import Json.Encode as Encode +import Regex + + +type alias Registry = + Dict Package.Key ( Package.Package, Status.Status ) + + + +-- INIT + + +initial : Registry +initial = + Dict.empty + |> insert (Status.DirectDep << .version) Defaults.direct + |> insert (Status.IndirectDep << .version) Defaults.indirect + + +initialWithDefaults : List Package.Package -> List Package.Package -> Registry +initialWithDefaults direct indirect = + Dict.empty + |> insert (Status.DirectDep << .version) direct + |> insert (Status.IndirectDep << .version) indirect + + +fetch : (Result Http.Error (List Package.Package) -> msg) -> Cmd msg +fetch onResult = + Http.post + { url = Constant.server ++ "/all-packages" + , body = Http.emptyBody + , expect = Http.expectJson onResult decoder + } + + +fromNews : List Package.Package -> Registry -> Registry +fromNews news registry = + let + toStatus maybeStatus _ = + case maybeStatus of + Just state -> + state + + Nothing -> + Status.NotInstalled + in + update toStatus news registry + + + +-- API + + +insert : (Package.Package -> Status.Status) -> List Package.Package -> Registry -> Registry +insert toStatus = + update (always toStatus) + + +update : (Maybe Status.Status -> Package.Package -> Status.Status) -> List Package.Package -> Registry -> Registry +update toStatus packages registry = + let + fold package = + Dict.update (Package.toKey package) (updateOne package) + + updateOne package maybeValue = + Just ( package, toStatus (Maybe.map Tuple.second maybeValue) package ) + in + List.foldl fold registry packages + + +mapStatus : (Package.Package -> Status.Status -> Status.Status) -> Registry -> Registry +mapStatus toStatus registry = + let + func key ( package, state ) = + ( package, toStatus package state ) + in + Dict.map func registry + + +setStatus : Package.Package -> Status.Status -> Registry -> Registry +setStatus pkg state = + Dict.insert (Package.toKey pkg) ( pkg, state ) + + +dismissAll : Registry -> Registry +dismissAll = + Dict.map <| + \_ ( pkg, state ) -> + case state of + Status.Failed _ -> + ( pkg, Status.NotInstalled ) + + _ -> + ( pkg, state ) + + +getValues : Registry -> List ( Package.Package, Status.Status ) +getValues registry = + Dict.values registry + + +getErrors : Registry -> List Error.Error +getErrors registry = + List.filterMap (Status.getError << Tuple.second) (Dict.values registry) + + +filterStatus : (Status.Status -> Bool) -> Registry -> Registry +filterStatus inGroup = + Dict.filter (\key ( pkg, state ) -> inGroup state) + + +filterKeys : List Package.Key -> Registry -> List ( Package.Package, Status.Status ) +filterKeys searched registry = + List.filterMap (\s -> Dict.get s registry) searched + + +fromSolution : Solution.Solution -> Registry -> Registry +fromSolution solution registry = + let + resetStatus state = + case state of + Status.Loading -> + state + + Status.NotInstalled -> + state + + Status.DirectDep _ -> + Status.NotInstalled + + Status.IndirectDep _ -> + Status.NotInstalled + + Status.Failed _ -> + state + + insertDeps toStatus deps reg = + List.foldl (fold toStatus) reg (Dict.toList deps) + + fold toStatus ( key, version ) = + Dict.update key <| + Maybe.map (\( pkg, _ ) -> ( pkg, toStatus version )) + in + registry + |> mapStatus (always resetStatus) + |> insertDeps Status.DirectDep solution.direct + |> insertDeps Status.IndirectDep solution.indirect + + +search : String -> List ( Package.Package, Status.Status ) -> List ( Package.Package, Status.Status ) +search query packages = + let + regex = + String.split "" query + |> List.intersperse ".*" + |> String.concat + |> Regex.fromStringWith { caseInsensitive = True, multiline = False } + |> Maybe.withDefault Regex.never + + match ( pkg, _ ) = + Regex.contains regex (Package.toName pkg) + + queryLower = + String.toLower query + + toNameLower pkg = + String.toLower (Package.toName pkg) + + order ( pkg, _ ) = + if queryLower == pkg.project then + 0 + + else if queryLower == pkg.author then + 1 + + else if String.contains queryLower (toNameLower pkg) then + 2 + + else + 3 + in + packages + |> List.filter match + |> List.sortBy order + + + +-- EDIT + + +type Action + = Install + | Uninstall + + +attemptEdit : Action -> Package.Package -> Cmd msg +attemptEdit action package = + case action of + Install -> + install { packageString = Package.toName package, versionString = V.toString package.version } + + Uninstall -> + uninstall { packageString = Package.toName package, versionString = V.toString package.version } + + +port install : { packageString : String, versionString : String } -> Cmd msg + + +port installResult : ({ packageString : String, versionString : String, result : Encode.Value } -> msg) -> Sub msg + + +port uninstall : { packageString : String, versionString : String } -> Cmd msg + + +port uninstallResult : ({ packageString : String, versionString : String, result : Encode.Value } -> msg) -> Sub msg + + + +-- DECODER / REGISTRY + + +decoder : Decode.Decoder (List Package.Package) +decoder = + Decode.map + (Dict.toList + >> List.concatMap + (\( name, versions ) -> + case String.split "/" name of + [ author, project ] -> + List.map (Package.Package author project) versions + + _ -> + [] + ) + ) + (Decode.dict decodeVersions) + + +decodeVersions : Decode.Decoder (List V.Version) +decodeVersions = + Decode.list decodeVersion + + +decodeVersion : Decode.Decoder V.Version +decodeVersion = + Decode.andThen + (\version -> + case List.map String.toInt (String.split "." version) of + [ Just major, Just minor, Just patch ] -> + Decode.succeed (V.Version major minor patch) + + _ -> + Decode.fail ("Failed to parse version `" ++ version ++ "`") + ) + Decode.string diff --git a/src/Data/Registry/Defaults.elm b/src/Data/Registry/Defaults.elm new file mode 100644 index 0000000..d247016 --- /dev/null +++ b/src/Data/Registry/Defaults.elm @@ -0,0 +1,64 @@ +module Data.Registry.Defaults exposing + ( decode + , direct + , indirect + , locked + , popular + ) + +import Data.Registry.Package as Package +import Data.Version as V +import Dict +import Json.Decode as D + + +direct : List Package.Package +direct = + [ Package.Package "elm" "browser" (V.Version 1 0 2) + , Package.Package "elm" "core" (V.Version 1 0 5) + , Package.Package "elm" "html" (V.Version 1 0 0) + ] + + +indirect : List Package.Package +indirect = + [ Package.Package "elm" "json" (V.Version 1 1 3) + , Package.Package "elm" "time" (V.Version 1 0 0) + , Package.Package "elm" "url" (V.Version 1 0 0) + , Package.Package "elm" "virtual-dom" (V.Version 1 0 2) + ] + + +locked : List Package.Key +locked = + [ ( "elm", "browser" ) + , ( "elm", "core" ) + ] + + +popular : List Package.Key +popular = + [ ( "elm", "http" ) + , ( "elm", "html" ) + , ( "elm", "random" ) + , ( "elm", "time" ) + , ( "elm", "file" ) + , ( "elm", "json" ) + , ( "elm", "svg" ) + , ( "evancz", "elm-playground" ) + , ( "elm-explorations", "webgl" ) + , ( "w0rm", "elm-physics" ) + , ( "rtfeldman", "elm-css" ) + , ( "mdgriffith", "elm-ui" ) + ] + + +decode : D.Decoder (List Package.Package) +decode = + let + shape ( name, version ) = + Package.keyFromName name + |> Maybe.map (\( author, project ) -> Package.Package author project version) + in + D.dict V.decoder + |> D.map (Dict.toList >> List.filterMap shape) diff --git a/src/Data/Registry/Package.elm b/src/Data/Registry/Package.elm new file mode 100644 index 0000000..de319a1 --- /dev/null +++ b/src/Data/Registry/Package.elm @@ -0,0 +1,65 @@ +module Data.Registry.Package exposing + ( Key + , Name + , Package + , keyFromName + , nameFromKey + , toDocsLink + , toKey + , toName + ) + +import Data.Version as V + + +type alias Package = + { author : String + , project : String + , version : V.Version + } + + +type alias Name = + String + + +type alias Key = + ( String, String ) + + +toKey : Package -> Key +toKey pkg = + ( pkg.author, pkg.project ) + + +toName : Package -> Name +toName pkg = + pkg.author ++ "/" ++ pkg.project + + + +-- CONVERSION + + +keyFromName : Name -> Maybe Key +keyFromName str = + case String.split "/" str of + author :: project :: [] -> + Just ( author, project ) + + _ -> + Nothing + + +nameFromKey : Key -> Name +nameFromKey ( author, project ) = + author ++ "/" ++ project + + + +-- DOCS + + +toDocsLink : Package -> String +toDocsLink package = + "https://package.elm-lang.org/packages/" ++ toName package ++ "/" ++ V.toString package.version diff --git a/src/Data/Registry/Solution.elm b/src/Data/Registry/Solution.elm new file mode 100644 index 0000000..03597e5 --- /dev/null +++ b/src/Data/Registry/Solution.elm @@ -0,0 +1,78 @@ +module Data.Registry.Solution exposing (Solution, decoder, empty, encode, toSolution) + +import Data.Registry.Defaults as Defaults +import Data.Registry.Package as Package +import Data.Registry.Status as Status +import Data.Version as V +import Dict exposing (Dict) +import Json.Decode as JD +import Json.Encode as JE + + +type alias Solution = + { elm : V.Version + , direct : Dict Package.Key V.Version + , indirect : Dict Package.Key V.Version + } + + +empty : Solution +empty = + Solution (V.Version 0 19 1) Dict.empty Dict.empty + + + +-- API + + +toSolution : List ( Package.Package, Status.Status ) -> Solution +toSolution packages = + let + addToSolution ( package, state ) solution = + case state of + Status.DirectDep version -> + { solution | direct = Dict.insert (Package.toKey package) version solution.direct } + + Status.IndirectDep version -> + { solution | indirect = Dict.insert (Package.toKey package) version solution.indirect } + + _ -> + solution + in + List.foldl addToSolution empty packages + + + +-- DECODE / ENCODE + + +decoder : JD.Decoder Solution +decoder = + JD.map3 Solution + (JD.field "elm-version" V.decoder) + (JD.at [ "dependencies", "direct" ] decodeDeps) + (JD.at [ "dependencies", "indirect" ] decodeDeps) + + +decodeDeps : JD.Decoder (Dict Package.Key V.Version) +decodeDeps = + let + shape dict = + Dict.toList dict + |> List.filterMap onlyValid + |> Dict.fromList + + onlyValid ( name, version ) = + Package.keyFromName name + |> Maybe.map (\key -> ( key, version )) + in + JD.map shape (JD.dict V.decoder) + + +encode : Solution -> JE.Value +encode solution = + JE.object + [ ( "elm-version", V.encode solution.elm ) + , ( "direct", JE.dict Package.nameFromKey V.encode solution.direct ) + , ( "indirect", JE.dict Package.nameFromKey V.encode solution.indirect ) + ] diff --git a/src/Data/Registry/Status.elm b/src/Data/Registry/Status.elm new file mode 100644 index 0000000..e63bc27 --- /dev/null +++ b/src/Data/Registry/Status.elm @@ -0,0 +1,114 @@ +module Data.Registry.Status exposing + ( Status(..) + , getError + , isDirectDep + , isFailed + , isIndirectDep + , isSearchable + ) + +import Data.Version as V +import Elm.Error as Error exposing (Error) + + +type Status + = Loading + | NotInstalled + | DirectDep V.Version + | IndirectDep V.Version + | Failed Error.Error + + +isDirectDep : Status -> Bool +isDirectDep state = + case state of + NotInstalled -> + False + + Loading -> + True + + DirectDep _ -> + True + + IndirectDep _ -> + False + + Failed _ -> + False + + +isIndirectDep : Status -> Bool +isIndirectDep state = + case state of + NotInstalled -> + False + + Loading -> + False + + DirectDep _ -> + False + + IndirectDep _ -> + True + + Failed _ -> + False + + +isFailed : Status -> Bool +isFailed state = + case state of + NotInstalled -> + False + + Loading -> + False + + DirectDep _ -> + False + + IndirectDep _ -> + False + + Failed _ -> + True + + +isSearchable : Status -> Bool +isSearchable state = + case state of + Loading -> + False + + NotInstalled -> + True + + DirectDep _ -> + False + + IndirectDep _ -> + True + + Failed _ -> + False + + +getError : Status -> Maybe Error +getError state = + case state of + NotInstalled -> + Nothing + + Loading -> + Nothing + + DirectDep _ -> + Nothing + + IndirectDep _ -> + Nothing + + Failed error -> + Just error diff --git a/src/Data/Status.elm b/src/Data/Status.elm new file mode 100644 index 0000000..0bd3170 --- /dev/null +++ b/src/Data/Status.elm @@ -0,0 +1,196 @@ +module Data.Status exposing + ( Status(..) + , changed + , compiling + , getProblems + , hasProblems + , isCompiling + , problems + , success + , withProblems + ) + +import Data.Problem as Problem exposing (Problem, Problems) +import Elm.Error as Error +import Json.Decode as D +import Json.Encode as E + + +type Status + = Changed + | Compiling + | Success + | HasProblems Problems + | HasProblemsButChanged Problems + | HasProblemsButRecompiling Problems + | Failed String + + +getProblems : Status -> Maybe Problems +getProblems status = + case status of + Changed -> + Nothing + + Compiling -> + Nothing + + Success -> + Nothing + + Failed _ -> + Just ( [], Problem.generic, [] ) + + HasProblems pbs -> + Just pbs + + HasProblemsButChanged pbs -> + Just pbs + + HasProblemsButRecompiling pbs -> + Just pbs + + +hasProblems : Status -> Bool +hasProblems status = + case status of + Changed -> + False + + Compiling -> + False + + Success -> + False + + Failed _ -> + True + + HasProblems _ -> + True + + HasProblemsButChanged _ -> + True + + HasProblemsButRecompiling _ -> + True + + +withProblems : Status -> (Problems -> Problems) -> Status +withProblems status func = + case status of + Changed -> + Changed + + Compiling -> + Compiling + + Success -> + Success + + Failed msg -> + Failed msg + + HasProblems pbs -> + HasProblems (func pbs) + + HasProblemsButChanged pbs -> + HasProblemsButChanged (func pbs) + + HasProblemsButRecompiling pbs -> + HasProblemsButRecompiling (func pbs) + + +isCompiling : Status -> Bool +isCompiling status = + case status of + Changed -> + False + + Compiling -> + True + + Success -> + False + + Failed _ -> + False + + HasProblems _ -> + False + + HasProblemsButChanged _ -> + False + + HasProblemsButRecompiling _ -> + True + + +changed : Status -> Status +changed status = + case status of + Changed -> + Changed + + Compiling -> + Changed + + Success -> + Changed + + Failed _ -> + HasProblemsButChanged ( [], Problem.generic, [] ) + + HasProblems pbs -> + HasProblemsButChanged pbs + + HasProblemsButChanged pbs -> + HasProblemsButChanged pbs + + HasProblemsButRecompiling pbs -> + HasProblemsButChanged pbs + + +compiling : Status -> Status +compiling status = + case status of + Changed -> + Compiling + + Compiling -> + Compiling + + Success -> + Compiling + + Failed _ -> + HasProblemsButRecompiling ( [], Problem.generic, [] ) + + HasProblems pbs -> + HasProblemsButRecompiling pbs + + HasProblemsButChanged pbs -> + HasProblemsButRecompiling pbs + + HasProblemsButRecompiling pbs -> + HasProblemsButRecompiling pbs + + +success : Status +success = + Success + + +problems : E.Value -> Status +problems value = + case D.decodeValue Error.decoder value of + Ok errors -> + case Problem.init (Problem.toIndexedProblems errors) of + Just pbs -> + HasProblems pbs + + Nothing -> + Failed "Somehow returned zero problems." + + Err err -> + Failed ("Could not decode compilation problems: " ++ D.errorToString err) diff --git a/src/Data/Time.elm b/src/Data/Time.elm new file mode 100644 index 0000000..fc598da --- /dev/null +++ b/src/Data/Time.elm @@ -0,0 +1,387 @@ +module Data.Time exposing + ( ceilingDay + , ceilingHour + , ceilingMinute + , ceilingMonth + , ceilingMs + , ceilingSecond + , ceilingYear + , floorDay + , floorHour + , floorMinute + , floorMonth + , floorMs + , floorSecond + , floorYear + , mapMs + , mapTime + , withMs + ) + +import Time +import Time.Extra as T + + +withMs : (Int -> a) -> Time.Posix -> a +withMs func time = + func (Time.posixToMillis time) + + +mapMs : (Int -> Int) -> Time.Posix -> Time.Posix +mapMs func time = + Time.millisToPosix (withMs func time) + + +mapTime : (Time.Posix -> Time.Posix) -> Int -> Int +mapTime func time = + Time.posixToMillis (func (Time.millisToPosix time)) + + + +-- FLOOR + + +floorMs : Time.Zone -> Int -> Time.Posix -> Time.Posix +floorMs zone mult stamp = + let + parts = + T.posixToParts zone stamp + + rem = + remainderBy mult parts.millisecond + in + if rem == 0 then + T.partsToPosix zone parts + + else + T.add T.Millisecond -rem zone stamp + + +floorSecond : Time.Zone -> Int -> Time.Posix -> Time.Posix +floorSecond zone mult stamp = + let + parts = + T.posixToParts zone (T.floor T.Second zone stamp) + + rem = + remainderBy mult parts.second + + new = + T.partsToPosix zone parts + in + if rem == 0 then + new + + else + T.add T.Second -rem zone stamp + + +floorMinute : Time.Zone -> Int -> Time.Posix -> Time.Posix +floorMinute zone mult stamp = + let + parts = + T.posixToParts zone (T.floor T.Minute zone stamp) + + rem = + remainderBy mult parts.minute + + new = + T.partsToPosix zone parts + in + if rem == 0 then + new + + else + T.add T.Minute -rem zone new + + +floorHour : Time.Zone -> Int -> Time.Posix -> Time.Posix +floorHour zone mult stamp = + let + parts = + T.posixToParts zone (T.floor T.Hour zone stamp) + + rem = + remainderBy mult parts.hour + + new = + T.partsToPosix zone parts + in + if rem == 0 then + new + + else + T.add T.Hour -rem zone new + + +floorDay : Time.Zone -> Int -> Time.Posix -> Time.Posix +floorDay zone mult stamp = + if mult == 7 then + T.floor T.Week zone stamp + + else + T.floor T.Day zone stamp + + +floorMonth : Time.Zone -> Int -> Time.Posix -> Time.Posix +floorMonth zone mult stamp = + let + parts = + T.posixToParts zone (T.floor T.Month zone stamp) + + monthInt = + monthAsInt parts.month + + rem = + remainderBy mult (monthInt - 1) + + newMonth = + if rem == 0 then + monthInt + + else + monthInt - rem + in + T.partsToPosix zone { parts | month = intAsMonth newMonth } + + +floorYear : Time.Zone -> Int -> Time.Posix -> Time.Posix +floorYear zone mult stamp = + let + parts = + T.posixToParts zone (T.ceiling T.Year zone stamp) + + rem = + remainderBy mult parts.year + + newYear = + if rem == 0 then + parts.year + + else + parts.year - rem + in + T.partsToPosix zone { parts | year = newYear } + + + +-- CEILING + + +ceilingMs : Time.Zone -> Int -> Time.Posix -> Time.Posix +ceilingMs zone mult stamp = + let + parts = + T.posixToParts zone stamp + + rem = + remainderBy mult parts.millisecond + in + if rem == 0 then + T.partsToPosix zone parts + + else + T.add T.Millisecond (mult - rem) zone stamp + + +ceilingSecond : Time.Zone -> Int -> Time.Posix -> Time.Posix +ceilingSecond zone mult stamp = + let + parts = + T.posixToParts zone (T.ceiling T.Second zone stamp) + + rem = + remainderBy mult parts.second + + new = + T.partsToPosix zone parts + in + if rem == 0 then + new + + else + T.add T.Second (mult - rem) zone new + + +ceilingMinute : Time.Zone -> Int -> Time.Posix -> Time.Posix +ceilingMinute zone mult stamp = + let + parts = + T.posixToParts zone (T.ceiling T.Minute zone stamp) + + rem = + remainderBy mult parts.minute + + new = + T.partsToPosix zone parts + in + if rem == 0 then + new + + else + T.add T.Minute (mult - rem) zone new + + +ceilingHour : Time.Zone -> Int -> Time.Posix -> Time.Posix +ceilingHour zone mult stamp = + let + parts = + T.posixToParts zone (T.ceiling T.Hour zone stamp) + + rem = + remainderBy mult parts.hour + + new = + T.partsToPosix zone parts + in + if rem == 0 then + new + + else + T.add T.Hour (mult - rem) zone new + + +ceilingDay : Time.Zone -> Int -> Time.Posix -> Time.Posix +ceilingDay zone mult stamp = + if mult == 7 then + T.ceiling T.Week zone stamp + + else + T.ceiling T.Day zone stamp + + +ceilingMonth : Time.Zone -> Int -> Time.Posix -> Time.Posix +ceilingMonth zone mult stamp = + let + parts = + T.posixToParts zone (T.ceiling T.Month zone stamp) + + monthInt = + monthAsInt parts.month + + -- 12 + rem = + remainderBy mult (monthInt - 1) + + -- 11 % 3 = 2 + newMonth = + if rem == 0 then + monthInt + + else + monthInt - rem + mult + + -- 12 - 2 + 3 = 13 + in + T.partsToPosix zone <| + if newMonth > 12 then + { parts | year = parts.year + 1, month = intAsMonth (newMonth - 12) } + + else + { parts | month = intAsMonth newMonth } + + +ceilingYear : Time.Zone -> Int -> Time.Posix -> Time.Posix +ceilingYear zone mult stamp = + let + parts = + T.posixToParts zone (T.ceiling T.Year zone stamp) + + rem = + remainderBy mult parts.year + + newYear = + if rem == 0 then + parts.year + + else + parts.year - rem + mult + in + T.partsToPosix zone { parts | year = newYear } + + + +-- HELP + + +monthAsInt : Time.Month -> Int +monthAsInt month = + case month of + Time.Jan -> + 1 + + Time.Feb -> + 2 + + Time.Mar -> + 3 + + Time.Apr -> + 4 + + Time.May -> + 5 + + Time.Jun -> + 6 + + Time.Jul -> + 7 + + Time.Aug -> + 8 + + Time.Sep -> + 9 + + Time.Oct -> + 10 + + Time.Nov -> + 11 + + Time.Dec -> + 12 + + +intAsMonth : Int -> Time.Month +intAsMonth int = + case int of + 1 -> + Time.Jan + + 2 -> + Time.Feb + + 3 -> + Time.Mar + + 4 -> + Time.Apr + + 5 -> + Time.May + + 6 -> + Time.Jun + + 7 -> + Time.Jul + + 8 -> + Time.Aug + + 9 -> + Time.Sep + + 10 -> + Time.Oct + + 11 -> + Time.Nov + + 12 -> + Time.Dec + + _ -> + Time.Dec diff --git a/src/Data/Version.elm b/src/Data/Version.elm new file mode 100644 index 0000000..ed9f034 --- /dev/null +++ b/src/Data/Version.elm @@ -0,0 +1,53 @@ +module Data.Version exposing + ( Version(..) + , decoder + , encode + , fromString + , toString + ) + +import Json.Decode as D +import Json.Encode as E + + +type Version + = Version Int Int Int + + +toString : Version -> String +toString (Version ma mi pa) = + String.fromInt ma ++ "." ++ String.fromInt mi ++ "." ++ String.fromInt pa + + +fromString : String -> Maybe Version +fromString string = + case String.split "." string of + [ maS, miS, paS ] -> + case ( String.toInt maS, String.toInt miS, String.toInt paS ) of + ( Just ma, Just mi, Just pa ) -> + Just (Version ma mi pa) + + _ -> + Nothing + + _ -> + Nothing + + +decoder : D.Decoder Version +decoder = + let + validate s = + case fromString s of + Just version -> + D.succeed version + + Nothing -> + D.fail "Version was of wrong format." + in + D.andThen validate D.string + + +encode : Version -> E.Value +encode = + E.string << toString diff --git a/src/Data/Window.elm b/src/Data/Window.elm new file mode 100644 index 0000000..b0fc838 --- /dev/null +++ b/src/Data/Window.elm @@ -0,0 +1,17 @@ +module Data.Window exposing (Window, isLessThan, isMobile) + + +type alias Window = + { width : Int + , height : Int + } + + +isMobile : Window -> Bool +isMobile window = + isLessThan window 1000 + + +isLessThan : Window -> Int -> Bool +isLessThan window num = + window.width <= num diff --git a/src/Icon.elm b/src/Icon.elm index 6ec44d6..6eb88e3 100644 --- a/src/Icon.elm +++ b/src/Icon.elm @@ -2,6 +2,7 @@ module Icon exposing ( arrowPath , check , discord + , exclamationCircle , github , info , lockClosed @@ -11,13 +12,12 @@ module Icon exposing , xMark ) -import Html import Html.Attributes.Aria as Aria import Svg exposing (Svg) import Svg.Attributes as SvgAttr -arrowPath : List (Html.Attribute msg) -> Svg msg +arrowPath : List (Svg.Attribute msg) -> Svg msg arrowPath attrs = Svg.svg (SvgAttr.fill "none" @@ -36,7 +36,7 @@ arrowPath attrs = ] -check : List (Html.Attribute msg) -> Svg msg +check : List (Svg.Attribute msg) -> Svg msg check attrs = Svg.svg (SvgAttr.fill "none" @@ -55,7 +55,7 @@ check attrs = ] -github : List (Html.Attribute msg) -> Svg msg +github : List (Svg.Attribute msg) -> Svg msg github attrs = Svg.svg (SvgAttr.viewBox "0 0 20 20" @@ -71,7 +71,7 @@ github attrs = ] -info : List (Html.Attribute msg) -> Svg msg +info : List (Svg.Attribute msg) -> Svg msg info attrs = Svg.svg (SvgAttr.viewBox "0 0 16 16" @@ -103,7 +103,7 @@ info attrs = ] -discord : List (Html.Attribute msg) -> Svg msg +discord : List (Svg.Attribute msg) -> Svg msg discord attrs = Svg.svg (SvgAttr.viewBox "0 0 20 20" @@ -114,7 +114,25 @@ discord attrs = ] -lockClosed : List (Html.Attribute msg) -> Svg msg +exclamationCircle : List (Svg.Attribute msg) -> Svg msg +exclamationCircle attrs = + Svg.svg + (SvgAttr.viewBox "0 0 24 24" + :: SvgAttr.fill "none" + :: SvgAttr.strokeWidth "1.5" + :: SvgAttr.stroke "currentColor" + :: attrs + ) + [ Svg.path + [ SvgAttr.strokeLinecap "round" + , SvgAttr.strokeLinejoin "round" + , SvgAttr.d "M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z" + ] + [] + ] + + +lockClosed : List (Svg.Attribute msg) -> Svg msg lockClosed attrs = Svg.svg (SvgAttr.viewBox "0 0 24 24" @@ -132,7 +150,7 @@ lockClosed attrs = ] -logo : List (Html.Attribute msg) -> Svg msg +logo : List (Svg.Attribute msg) -> Svg msg logo attrs = Svg.svg (SvgAttr.viewBox "0 0 52.917 52.917" @@ -147,7 +165,7 @@ logo attrs = ] -plus : List (Html.Attribute msg) -> Svg msg +plus : List (Svg.Attribute msg) -> Svg msg plus attrs = Svg.svg (SvgAttr.viewBox "0 0 24 24" @@ -166,7 +184,7 @@ plus attrs = ] -trash : List (Html.Attribute msg) -> Svg msg +trash : List (Svg.Attribute msg) -> Svg msg trash attrs = Svg.svg (SvgAttr.viewBox "0 0 24 24" @@ -185,7 +203,7 @@ trash attrs = ] -xMark : List (Html.Attribute msg) -> Svg msg +xMark : List (Svg.Attribute msg) -> Svg msg xMark attrs = Svg.svg (SvgAttr.fill "none" diff --git a/src/Page/Try.elm b/src/Page/Try.elm index 191a2ff..dbe8464 100644 --- a/src/Page/Try.elm +++ b/src/Page/Try.elm @@ -13,6 +13,10 @@ import Components.Button as Button import Components.ResourcePattern as ResourcePattern import Components.ThemeToggle as ThemeToggle import DOM +import Data.Examples as Examples exposing (Example) +import Data.Registry.Defaults as Defaults +import Data.Registry.Package exposing (Package) +import Data.Version as V import Elm.Error import Errors import Html exposing (Html) @@ -27,6 +31,7 @@ import Layout.Main as Layout import Route import Session exposing (Session) import Svg.Attributes as SvgAttr +import Ui.Package port setEditorContentAndRebuild : String -> Cmd msg @@ -43,12 +48,12 @@ port rebuildResult : (Encode.Value -> msg) -> Sub msg type alias Model = - { showPackages : Bool - , status : Status + { status : Status , maybeResult : Maybe (Result Elm.Error.Error String) , example : Maybe Route.Example , mouseX : Int , mouseY : Int + , packageUi : Ui.Package.Model } @@ -61,8 +66,101 @@ type Status init : Maybe Route.Example -> ( Model, Cmd Msg ) init maybeExample = - ( { showPackages = False - , status = + let + example : Example + example = + case maybeExample of + Just Route.Animation -> + Examples.animation + + Just Route.Book -> + Examples.book + + Just Route.Buttons -> + Examples.buttons + + Just Route.Cards -> + Examples.cards + + Just Route.Clock -> + Examples.clock + + Just Route.Crate -> + Examples.crate + + Just Route.Cube -> + Examples.cube + + Just Route.DragAndDrop -> + Examples.dragAndDrop + + Just Route.FirstPerson -> + Examples.firstPerson + + Just Route.Forms -> + Examples.forms + + Just Route.Groceries -> + Examples.groceries + + Just Route.Hello -> + Examples.hello + + Just Route.ImagePreviews -> + Examples.imagePreviews + + Just Route.Keyboard -> + Examples.keyboard + + Just Route.Mario -> + Examples.mario + + Just Route.Mouse -> + Examples.mouse + + Just Route.Numbers -> + Examples.numbers + + Just Route.Picture -> + Examples.picture + + Just Route.Positions -> + Examples.positions + + Just Route.Quotes -> + Examples.quotes + + Just Route.Shapes -> + Examples.shapes + + Just Route.TextFields -> + Examples.textFields + + Just Route.Thwomp -> + Examples.thwomp + + Just Route.Time -> + Examples.time + + Just Route.Triangle -> + Examples.triangle + + Just Route.Turtle -> + Examples.turtle + + Just Route.Upload -> + Examples.upload + + Nothing -> + { direct = Defaults.direct + , indirect = Defaults.indirect + , content = "" + } + + ( packageUi, packageUiCmd ) = + Ui.Package.init example.direct example.indirect + in + ( { status = case maybeExample of Just _ -> Compiling @@ -73,40 +171,55 @@ init maybeExample = , example = maybeExample , mouseX = 0 , mouseY = 0 + , packageUi = packageUi } , Cmd.batch - [ case maybeExample of - Just example -> - Http.get - { url = "/example-files/" ++ Route.exampleSrc example ++ ".guida" - , expect = Http.expectString GotExampleContent - } - - Nothing -> - Cmd.none - , registryFetch + [ setupProject + { direct = List.map toPortPackage example.direct + , indirect = List.map toPortPackage example.indirect + , content = example.content + } + , Cmd.map OnPackageMsg packageUiCmd ] ) +type alias PortPackage = + { author : String + , project : String + , version : String + } + + +toPortPackage : Package -> PortPackage +toPortPackage { author, project, version } = + { author = author + , project = project + , version = V.toString version + } + + +port setupProject : { direct : List PortPackage, indirect : List PortPackage, content : String } -> Cmd msg + + -- UPDATE type Msg - = TogglePackages + = OpenPackages | GotExampleContent (Result Http.Error String) | Rebuild | RebuildResult Encode.Value | OnMouseOver DOM.Rectangle Int Int - | GotRegistry (Result Http.Error (List Package)) + | OnPackageMsg Ui.Package.Msg update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of - TogglePackages -> - ( { model | showPackages = not model.showPackages }, Cmd.none ) + OpenPackages -> + ( { model | packageUi = Ui.Package.open model.packageUi }, Cmd.none ) GotExampleContent (Ok content) -> ( { model | status = Success }, setEditorContentAndRebuild content ) @@ -135,19 +248,22 @@ update msg model = OnMouseOver { left, top } clientX clientY -> ( { model | mouseX = clientX - round left, mouseY = clientY - round top }, Cmd.none ) - GotRegistry (Ok news) -> + OnPackageMsg subMsg -> + let + ( packageUi, shouldRebuild, packageUiCmd ) = + Ui.Package.update subMsg model.packageUi + in ( { model - | registry = - registryInitialWithDefaults model.defaultDirect model.defaultIndirect - |> registryFromNews news - |> Fetched.Success + | packageUi = packageUi + , status = + -- if shouldRebuild then + -- Status.changed model.status + -- else + model.status } - , Cmd.none + , Cmd.map OnPackageMsg packageUiCmd ) - GotRegistry (Err ((Http.BadBody errMsg) as err)) -> - ( { model | registry = Fetched.Failed err }, Cmd.none ) - decodeResult : Decode.Decoder (Result Elm.Error.Error String) decodeResult = @@ -163,7 +279,10 @@ decodeResult = subscriptions : Model -> Sub Msg subscriptions _ = - rebuildResult RebuildResult + Sub.batch + [ rebuildResult RebuildResult + , Sub.map OnPackageMsg Ui.Package.subscriptions + ] @@ -258,7 +377,7 @@ view session toSessionMsg toMsg model = Nothing -> "Try Guida!" , body = - Layout.fullscreenView session toSessionMsg packagesDialogView <| + Layout.fullscreenView session toSessionMsg (packagesDialogView toMsg model) <| [ Html.section [ Attr.class "flex flex-col h-min-full md:h-full md:grid md:grid-cols-2 md:grid-rows-[1fr_min-content]" ] [ Html.div [ Attr.class "h-full overflow-auto border-b md:border-b-0 md:border-r border-gray-200" @@ -286,7 +405,7 @@ view session toSessionMsg toMsg model = [ Html.li [] [ Html.button [ Attr.class "flex gap-0.5 items-center py-0.5 px-2 text-sm/7 text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" - , Events.onClick (toMsg Rebuild) + , Events.onClick (toMsg OpenPackages) ] [ Html.text "Packages" ] @@ -305,118 +424,117 @@ view session toSessionMsg toMsg model = } -packagesDialogView : Html msg -packagesDialogView = - let - openAttrs : List (Html.Attribute msg) - openAttrs = - if True then - [ Attr.attribute "open" "true" ] - - else - [] - in - Html.node "dialog" - (Attr.class "fixed inset-0 z-50" - :: openAttrs - ) - [ Html.div - [ Attr.class "fixed inset-0 top-14 bg-zinc-400/20 backdrop-blur-xs data-closed:opacity-0 data-enter:duration-300 data-enter:ease-out data-leave:duration-200 data-leave:ease-in dark:bg-black/40" - - -- , Events.onClick (toSessionMsg Session.ToggleMobileNavigation) - ] - [] - , Html.div [ Attr.class "fixed top-0 bottom-0 left-0 w-full overflow-y-auto bg-white px-4 pt-6 pb-4 ring-1 shadow-lg shadow-zinc-900/10 ring-zinc-900/7.5 duration-500 ease-in-out data-closed:-translate-x-full min-[416px]:max-w-sm sm:px-6 sm:pb-10 dark:bg-zinc-900 dark:ring-zinc-800" ] - [ Html.nav - [ Attr.class "relative flex flex-1 flex-col" - ] - [ Html.ul [ Aria.role "list", Attr.class "flex flex-1 flex-col gap-y-4" ] - [ Html.li [] - [ Html.div - [ Attr.class "text-xs/6 font-semibold text-gray-400" - ] - [ Html.text "Installed" ] - , Html.ul - [ Aria.role "list" - , Attr.class "mt-2 space-y-1" - ] - [ Html.li - [ Attr.class "group flex justify-between gap-x-3 rounded-md p-1 text-sm/6 font-semibold text-gray-700 hover:bg-gray-50 hover:text-amber-600 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-white" - ] - [ Html.span - [ Attr.class "truncate" - ] - [ Html.text "elm/random" ] - , Html.div [ Attr.class "flex gap-x-3" ] - [ Html.span [ Attr.class "flex size-6 shrink-0 items-center text-[0.625rem] font-medium text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] - [ Html.text "2.0.1" ] - , Button.view Button.Button Button.Text Nothing [] [ Icon.lockClosed [ SvgAttr.class "h-5 w-5 text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] ] - ] - ] - ] - ] - , Html.li [] - [ Html.div - [ Attr.class "text-xs/6 font-semibold text-gray-400" - ] - [ Html.text "Registry" ] - , Html.input - [ Attr.class "mt-2 block w-full rounded-md bg-white px-3 py-1.5 text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6 dark:bg-white/5 dark:text-white dark:outline-white/10 dark:placeholder:text-gray-500 dark:focus:outline-indigo-500" - , Attr.type_ "search" - , Attr.id "registry-search" - , Attr.placeholder "Search" - , Aria.ariaLabel "Search" - ] - [] - , Html.ul - [ Aria.role "list" - , Attr.class "mt-2 space-y-1" - ] - [ Html.li - [ Attr.class "group flex justify-between gap-x-3 rounded-md p-1 text-sm/6 font-semibold text-gray-700 hover:bg-gray-50 hover:text-amber-600 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-white" - ] - [ Html.span - [ Attr.class "truncate" - ] - [ Html.text "elm/html" ] - , Html.div [ Attr.class "flex gap-x-3" ] - [ Html.span [ Attr.class "flex size-6 shrink-0 items-center text-[0.625rem] font-medium text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] - [ Html.text "1.0.0" ] - , Button.view Button.Button Button.Text Nothing [] [ Icon.plus [ SvgAttr.class "h-5 w-5 text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] ] - ] - ] - , Html.li - [ Attr.class "group flex justify-between gap-x-3 rounded-md p-1 text-sm/6 font-semibold text-gray-700 hover:bg-gray-50 hover:text-amber-600 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-white" - ] - [ Html.span - [ Attr.class "truncate" - ] - [ Html.text "elm/random" ] - , Html.div [ Attr.class "flex gap-x-3" ] - [ Html.span [ Attr.class "flex size-6 shrink-0 items-center text-[0.625rem] font-medium text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] - [ Html.text "2.0.1" ] - , Button.view Button.Button Button.Text Nothing [] [ Icon.trash [ SvgAttr.class "h-5 w-5 text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] ] - ] - ] - , Html.li - [ Attr.class "group flex justify-between gap-x-3 rounded-md p-1 text-sm/6 font-semibold text-gray-700 hover:bg-gray-50 hover:text-amber-600 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-white" - ] - [ Html.span - [ Attr.class "truncate" - ] - [ Html.text "elm/random" ] - , Html.div [ Attr.class "flex gap-x-3" ] - [ Html.span [ Attr.class "flex size-6 shrink-0 items-center text-[0.625rem] font-medium text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] - [ Html.text "2.0.1" ] - , Button.view Button.Button Button.Text Nothing [] [ Icon.lockClosed [ SvgAttr.class "h-5 w-5 text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] ] - ] - ] - ] - ] - ] - ] - ] - ] +packagesDialogView : (Msg -> msg) -> Model -> Html msg +packagesDialogView toMsg model = + -- let + -- openAttrs : List (Html.Attribute msg) + -- openAttrs = + -- if True then + -- [ Attr.attribute "open" "true" ] + -- else + -- [] + -- in + -- Html.node "dialog" + -- (Attr.class "fixed inset-0 z-50" + -- :: openAttrs + -- ) + -- [ Html.div + -- [ Attr.class "fixed inset-0 top-14 bg-zinc-400/20 backdrop-blur-xs data-closed:opacity-0 data-enter:duration-300 data-enter:ease-out data-leave:duration-200 data-leave:ease-in dark:bg-black/40" + -- -- , Events.onClick (toSessionMsg Session.ToggleMobileNavigation) + -- ] + -- [] + -- , Html.div [ Attr.class "fixed top-0 bottom-0 left-0 w-full overflow-y-auto bg-white px-4 pt-6 pb-4 ring-1 shadow-lg shadow-zinc-900/10 ring-zinc-900/7.5 duration-500 ease-in-out data-closed:-translate-x-full min-[416px]:max-w-sm sm:px-6 sm:pb-10 dark:bg-zinc-900 dark:ring-zinc-800" ] + -- [ Html.nav + -- [ Attr.class "relative flex flex-1 flex-col" + -- ] + -- [ Html.ul [ Aria.role "list", Attr.class "flex flex-1 flex-col gap-y-4" ] + -- [ Html.li [] + -- [ Html.div + -- [ Attr.class "text-xs/6 font-semibold text-gray-400" + -- ] + -- [ Html.text "Installed" ] + -- , Html.ul + -- [ Aria.role "list" + -- , Attr.class "mt-2 space-y-1" + -- ] + -- [ Html.li + -- [ Attr.class "group flex justify-between gap-x-3 rounded-md p-1 text-sm/6 font-semibold text-gray-700 hover:bg-gray-50 hover:text-amber-600 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-white" + -- ] + -- [ Html.span + -- [ Attr.class "truncate" + -- ] + -- [ Html.text "elm/random" ] + -- , Html.div [ Attr.class "flex gap-x-3" ] + -- [ Html.span [ Attr.class "flex size-6 shrink-0 items-center text-[0.625rem] font-medium text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] + -- [ Html.text "2.0.1" ] + -- , Button.view Button.Button Button.Text Nothing [] [ Icon.lockClosed [ SvgAttr.class "h-5 w-5 text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] ] + -- ] + -- ] + -- ] + -- ] + -- , Html.li [] + -- [ Html.div + -- [ Attr.class "text-xs/6 font-semibold text-gray-400" + -- ] + -- [ Html.text "Registry" ] + -- , Html.input + -- [ Attr.class "mt-2 block w-full rounded-md bg-white px-3 py-1.5 text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6 dark:bg-white/5 dark:text-white dark:outline-white/10 dark:placeholder:text-gray-500 dark:focus:outline-indigo-500" + -- , Attr.type_ "search" + -- , Attr.id "registry-search" + -- , Attr.placeholder "Search" + -- , Aria.ariaLabel "Search" + -- ] + -- [] + -- , Html.ul + -- [ Aria.role "list" + -- , Attr.class "mt-2 space-y-1" + -- ] + -- [ Html.li + -- [ Attr.class "group flex justify-between gap-x-3 rounded-md p-1 text-sm/6 font-semibold text-gray-700 hover:bg-gray-50 hover:text-amber-600 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-white" + -- ] + -- [ Html.span + -- [ Attr.class "truncate" + -- ] + -- [ Html.text "elm/html" ] + -- , Html.div [ Attr.class "flex gap-x-3" ] + -- [ Html.span [ Attr.class "flex size-6 shrink-0 items-center text-[0.625rem] font-medium text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] + -- [ Html.text "1.0.0" ] + -- , Button.view Button.Button Button.Text Nothing [] [ Icon.plus [ SvgAttr.class "h-5 w-5 text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] ] + -- ] + -- ] + -- , Html.li + -- [ Attr.class "group flex justify-between gap-x-3 rounded-md p-1 text-sm/6 font-semibold text-gray-700 hover:bg-gray-50 hover:text-amber-600 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-white" + -- ] + -- [ Html.span + -- [ Attr.class "truncate" + -- ] + -- [ Html.text "elm/random" ] + -- , Html.div [ Attr.class "flex gap-x-3" ] + -- [ Html.span [ Attr.class "flex size-6 shrink-0 items-center text-[0.625rem] font-medium text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] + -- [ Html.text "2.0.1" ] + -- , Button.view Button.Button Button.Text Nothing [] [ Icon.trash [ SvgAttr.class "h-5 w-5 text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] ] + -- ] + -- ] + -- , Html.li + -- [ Attr.class "group flex justify-between gap-x-3 rounded-md p-1 text-sm/6 font-semibold text-gray-700 hover:bg-gray-50 hover:text-amber-600 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-white" + -- ] + -- [ Html.span + -- [ Attr.class "truncate" + -- ] + -- [ Html.text "elm/random" ] + -- , Html.div [ Attr.class "flex gap-x-3" ] + -- [ Html.span [ Attr.class "flex size-6 shrink-0 items-center text-[0.625rem] font-medium text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] + -- [ Html.text "2.0.1" ] + -- , Button.view Button.Button Button.Text Nothing [] [ Icon.lockClosed [ SvgAttr.class "h-5 w-5 text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] ] + -- ] + -- ] + -- ] + -- ] + -- ] + -- ] + -- ] + -- ] + Html.map (toMsg << OnPackageMsg) (Ui.Package.view model.packageUi) rebuildButton : (Msg -> msg) -> Model -> Html msg diff --git a/src/Ui/Color.elm b/src/Ui/Color.elm new file mode 100644 index 0000000..8650059 --- /dev/null +++ b/src/Ui/Color.elm @@ -0,0 +1,71 @@ +module Ui.Color exposing (..) + +import Element as E + + +black = + E.rgb255 0 0 0 + + +textColor = + E.rgb255 70 70 70 + + +white = + E.rgb255 255 255 255 + + +border = + E.rgb255 230 230 230 + + +orange = + E.rgb255 235 155 1 + + +orangeOp = + E.rgba255 235 155 1 + + +pink = + E.rgb255 224 11 135 + + +green = + E.rgb255 39 134 42 + + +yellow = + E.rgb255 247 200 56 + + +gray = + E.rgb255 117 113 94 + + +grayOp = + E.rgba255 117 113 94 + + +blue = + E.rgb255 18 147 216 + + +blueOp = + E.rgba255 18 147 216 + + +blueText = + E.rgb255 3 122 186 + + +shade = + E.rgb255 250 250 250 + + +red = + E.rgb255 215 31 10 + + +redOp = + E.rgba255 215 31 10 diff --git a/src/Ui/ColumnDivider.elm b/src/Ui/ColumnDivider.elm new file mode 100644 index 0000000..c7655c5 --- /dev/null +++ b/src/Ui/ColumnDivider.elm @@ -0,0 +1,313 @@ +module Ui.ColumnDivider exposing (Model, Msg, init, isRightMost, openLeft, pushLeft, pushRight, update, view) + +{-| Control the sizes of the two columns, editor and result. + +Relies on column-divider.js being present. + +-} + +import Data.Window as Window exposing (Window) +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (on, onClick, preventDefaultOn) +import Html.Lazy exposing (..) +import Json.Decode as D +import Json.Encode as E + + +type alias Model = + { percent : Float + , movement : Movement + } + + +type Movement + = None + | Moving Float Float Bool + + + +-- EXTERNAL API + + +pushRight : Window -> Int -> Model -> Model +pushRight window px model = + let + newPx = + toFloat (window.width - px) * (model.percent / 100) + in + { model | percent = toPercentage window newPx } + + +pushLeft : Window -> Int -> Model -> Model +pushLeft window px model = + let + oldPx = + toFloat window.width * (model.percent / 100) + + newPerc = + oldPx * 100 / toFloat (window.width - px) + in + { model | percent = newPerc } + + +openLeft : Window -> Model -> Model +openLeft window model = + { model | percent = rightMost window } + + +isRightMost : Window -> Model -> Bool +isRightMost window model = + model.percent >= rightMost window + + + +-- INTERNAL API + + +leftMost : Window -> Float +leftMost window = + toPercentage window 35 + + +rightMost : Window -> Float +rightMost window = + 100 + - toPercentage window + (if Window.isMobile window then + 25 + + else + 35 + ) + + +halfPoint : Float +halfPoint = + 50 + + +clamp : Window -> Float -> Float +clamp window = + Basics.max (leftMost window) >> Basics.min (rightMost window) + + +jump : Window -> Float -> Float +jump window percent = + if percent >= rightMost window then + leftMost window + + else if percent <= leftMost window then + if Window.isMobile window then + rightMost window + + else + halfPoint + + else if percent >= halfPoint then + rightMost window + + else + leftMost window + + +isSignificant : Float -> Float -> Bool +isSignificant initial latest = + abs (initial - latest) > 4 + + +isMoving : Model -> Bool +isMoving model = + case model.movement of + Moving _ _ _ -> + True + + None -> + False + + +toPercentage : Window -> Float -> Float +toPercentage window px = + px * 100 / toFloat window.width + + +fromPercentage : Window -> Float -> Float +fromPercentage window percent = + percent * toFloat window.width / 100 + + + +-- INIT + + +init : Window -> Model +init window = + { percent = 50 + , movement = None + } + + + +-- UPDATE + + +type Msg + = OnDown Float + | OnMove Float + | OnUp Float + | OnClick + | OnClickLeft + + +update : Window -> Msg -> Model -> Model +update window msg model = + case msg of + OnDown initial -> + { model | movement = Moving model.percent initial False } + + OnMove latest -> + case model.movement of + Moving percent initial False -> + if isSignificant initial latest then + { model | percent = toPercentage window latest, movement = Moving percent initial True } + + else + { model | percent = toPercentage window latest } + + Moving _ _ True -> + { model | percent = toPercentage window latest } + + None -> + { model | percent = toPercentage window latest } + + OnUp latest -> + case model.movement of + Moving _ _ True -> + { model | movement = None, percent = toPercentage window latest } + + Moving percent _ False -> + { model | movement = None, percent = jump window percent } + + None -> + { model | movement = None, percent = jump window model.percent } + + OnClick -> + { model | movement = None, percent = jump window model.percent } + + OnClickLeft -> + { model | percent = rightMost window } + + + +-- VIEW + + +view : (Msg -> msg) -> Window -> Model -> List (Html msg) -> List (Html msg) -> Html msg +view onMsg window model leftChildren rightChildren = + let + percent = + clamp window model.percent + in + div + [ id "double-pane" + , style "width" "100%" + , style "display" "flex" + ] + [ viewLeft onMsg window model percent leftChildren + , Html.map onMsg (viewDivider window model percent) + , viewRight window model percent rightChildren + ] + + +viewLeft : (Msg -> msg) -> Window -> Model -> Float -> List (Html msg) -> Html msg +viewLeft onMsg window model percent = + let + events = + if percent <= leftMost window then + [ preventDefaultOn "touchend" (D.succeed ( onMsg OnClickLeft, True )) + , onClick (onMsg OnClickLeft) + ] + + else + [] + in + div <| + [ id "left-side" + , style "width" (String.fromFloat percent ++ "%") + , style "pointer-events" + (if isMoving model then + "none" + + else + "auto" + ) + , style "user-select" + (if isMoving model then + "none" + + else + "auto" + ) + , style "transition" + (if isMoving model then + "none" + + else + "width 0.5s" + ) + ] + ++ events + + +viewRight : Window -> Model -> Float -> List (Html msg) -> Html msg +viewRight window model percent = + div + [ id "right-side" + , style "width" (String.fromFloat (100 - percent) ++ "%") + , style "pointer-events" + (if isMoving model then + "none" + + else + "auto" + ) + , style "user-select" + (if isMoving model then + "none" + + else + "auto" + ) + , style "transition" + (if isMoving model then + "none" + + else + "width 0.5s" + ) + ] + + +viewDivider : Window -> Model -> Float -> Html Msg +viewDivider window model percent = + node "column-divider" + [ on "down" (D.map OnDown decodePixels) + , on "move" (D.map OnMove decodePixels) + , on "up" (D.map OnUp decodePixels) + , on "_click" (D.succeed OnClick) + , property "pixels" (E.float (fromPercentage window percent)) + , style "width" + (if isRightMost window model then + "40px" + + else + "10px" + ) + , style "left" (String.fromFloat percent ++ "%") + ] + [] + + +decodePixels : D.Decoder Float +decodePixels = + D.at [ "target", "pixels" ] D.float diff --git a/src/Ui/Editor.elm b/src/Ui/Editor.elm new file mode 100644 index 0000000..486303b --- /dev/null +++ b/src/Ui/Editor.elm @@ -0,0 +1,337 @@ +port module Ui.Editor exposing (..) + + +{-| Control the code editor. + +Relies on code-editor.js being present. + +-} + + +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (onClick, on, onMouseOver, onMouseLeave) +import Html.Lazy exposing (..) +import Http +import Json.Encode as E +import Json.Decode as D +import Dict exposing (Dict) +import Elm.Error as Error exposing (Region) +import FeatherIcons as I +import Constant + +import Data.Deps as Deps +import Data.Header as Header +import Data.Hint as Hint +import Data.Problem as Problem +import Data.Status as Status +import Data.Registry.Solution as Solution +import Data.Registry.Package as Package +import Data.Version as Version exposing (Version(..)) +import Ui.Icon + + + +-- PORTS + + +port submitSource : String -> Cmd msg +port gotErrors : (E.Value -> msg) -> Sub msg +port gotSuccess : (() -> msg) -> Sub msg + + + +-- MODEL + + +type alias Model = + { source : String + , hint : Maybe String + , hintTable : Hint.Table + , imports : Header.Imports + , importEnd : Int + , dependencies : DepsInfo + , selection : Maybe Region + } + + +type DepsInfo + = Loading + | Failure + | Success Deps.Info + + +setSelection : Region -> Model -> Model +setSelection region model = + { model | selection = Just region } + + + +-- INIT + + +init : String -> ( Model, Cmd Msg ) +init source = + let defaults = + { source = source + , hint = Nothing + , hintTable = Hint.defaultTable + , imports = Header.defaultImports + , importEnd = 0 + , dependencies = Loading + , selection = Nothing + } + in + case Header.parse source of + Nothing -> + ( defaults + , fetchDepsInfo + ) + + Just ( imports, importEnd ) -> + ( { defaults | imports = imports, importEnd = importEnd } + , fetchDepsInfo + ) + + +fetchDepsInfo : Cmd Msg +fetchDepsInfo = + Http.get + { url = Constant.server ++ "/api/compile/deps-info.json" + , expect = Http.expectJson GotDepsInfo Deps.decoder + } + + + +-- UPDATE + + +type Msg + = OnChange String (Maybe Region) + | OnSave String (Maybe Region) + | OnHint (Maybe String) + | OnCompile + | GotDepsInfo (Result Http.Error Deps.Info) + | GotSuccess + | GotErrors E.Value + + +update : Msg -> Model -> Status.Status -> ( Model, Status.Status, Cmd Msg ) +update msg model status = + case msg of + OnChange source selection -> + ( { model + | source = source + , selection = selection + } + , Status.changed status + , Cmd.none + ) + + OnHint hint -> + ( { model | hint = hint }, status, Cmd.none ) + + OnSave source selection -> + ( updateImports + { model + | source = source + , selection = selection + } + , Status.compiling status + , submitSource source + ) + + OnCompile -> + ( updateImports model + , Status.compiling status + , submitSource model.source + ) + + GotDepsInfo result -> + case result of + Err _ -> + ( { model | dependencies = Failure } + , status + , Cmd.none + ) + + Ok info -> + ( { model | hintTable = Hint.buildTable model.imports info, dependencies = Success info } + , status + , Cmd.none + ) + + GotSuccess -> + ( model, Status.success, Cmd.none ) + + GotErrors errors -> + ( model, Status.problems errors, Cmd.none ) + + +updateImports : Model -> Model +updateImports model = + case Header.parse model.source of + Nothing -> + model + + Just ( imports, importEnd ) -> + case model.dependencies of + Failure -> + model + + Loading -> + model + + Success info -> + { model + | hintTable = Hint.buildTable imports info + , imports = imports + , importEnd = importEnd + } + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions _ = + Sub.batch [ gotErrors GotErrors, gotSuccess (always GotSuccess) ] + + + +-- VIEW + + +viewEditor : Solution.Solution -> Bool -> Model -> Html Msg +viewEditor solution isLight model = + Html.form + [ id "editor" + , action (Constant.server ++ "/api/compile") + , method "post" + , enctype "multipart/form-data" + , target "output" + ] + [ textarea [ id "code", name "code", style "display" "none" ] [] + , case solution.hash of + Just hash -> viewSolutionInput solution hash + Nothing -> text "" + , lazy4 viewEditor_ model.source model.selection isLight model.importEnd + ] + + +viewSolutionInput : Solution.Solution -> String -> Html msg +viewSolutionInput solution hash = + let toString dict = + dict + |> Dict.toList + |> List.map (\(key, version) -> Package.nameFromKey key ++ ":" ++ Version.toString version) + |> String.join "," + in + fieldset + [ style "display" "none" ] + [ input [ id "hash", name "hash", value hash ] [] + , input [ id "direct", name "direct", value (toString solution.direct) ] [] + , input [ id "indirect", name "indirect", value (toString solution.indirect) ] [] + ] + + +viewEditor_ : String -> Maybe Region -> Bool -> Int -> Html Msg +viewEditor_ source selection lights importEnd = + let theme = + if lights then "light" else "dark" + in + node "code-editor" + [ property "source" (E.string source) + , property "theme" (E.string theme) + , property "importEnd" (E.int importEnd) + , property "selection" <| + case selection of + Nothing -> encodeBlankSelection + Just region -> encodeSelection region + , on "save" (D.map2 OnSave decodeSource decodeSelection) + , on "change" (D.map2 OnChange decodeSource decodeSelection) + , on "hint" (D.map OnHint decodeHint) + ] + [] + + + +-- VIEW / HINT + + +viewHint : Model -> Html msg +viewHint model = + case model.hint of + Nothing -> + text "" + + Just hint -> + lazy2 viewHint_ hint model.hintTable + + +viewHint_ : String -> Hint.Table -> Html msg +viewHint_ token table = + case Hint.lookup token table of + Just info -> + case info of + Hint.Ambiguous -> + text "" + + Hint.Specific hint -> + Ui.Icon.link [ style "padding" "0 10px" ] + { icon = I.helpCircle + , iconColor = Nothing + , label = Just hint.text + , alt = "Read more about " ++ hint.text + , link = hint.href + } + + Nothing -> + text "" + + + +-- ENCODE / DECODE + + +encodeSelection : Region -> E.Value +encodeSelection { start, end } = + E.object + [ ( "start", E.object [ ( "line", E.int start.line ), ( "column", E.int start.column ) ] ) + , ( "end", E.object [ ( "line", E.int end.line ), ( "column", E.int end.column ) ] ) + ] + + +encodeBlankSelection : E.Value +encodeBlankSelection = + E.object + [ ( "start", E.null ) + , ( "end", E.null ) + ] + + +decodeSource : D.Decoder String +decodeSource = + D.at [ "target", "source" ] D.string + + +decodeSelection : D.Decoder (Maybe Region) +decodeSelection = + D.at [ "target", "selection" ] <| + D.map2 (Maybe.map2 Region) + (D.field "start" (D.nullable decodePosition)) + (D.field "end" (D.nullable decodePosition)) + + +decodePosition : D.Decoder Error.Position +decodePosition = + D.map2 Error.Position + (D.field "line" D.int) + (D.field "column" D.int) + + +decodeHint : D.Decoder (Maybe String) +decodeHint = + D.at [ "target", "hint" ] (D.nullable D.string) + diff --git a/src/Ui/Elements.elm b/src/Ui/Elements.elm new file mode 100644 index 0000000..6d74aea --- /dev/null +++ b/src/Ui/Elements.elm @@ -0,0 +1,77 @@ +module Ui.Elements exposing (..) + + +import Element as E +import Element.Background as BG +import Element.Border as B +import Element.Font as F +import Element.Input as I +import FeatherIcons as I +import Ui.Color exposing (..) + + +iconTitle : I.Icon -> String -> E.Element msg +iconTitle icon str = + E.row + [ F.size 18 + , F.color black + , E.spacing 12 + ] + [ simpleIcon 18 icon + , E.text str + ] + + +iconNote : I.Icon -> String -> E.Element msg +iconNote icon str = + E.row + [ F.size 14 + , F.bold + , F.color gray + , E.spacing 12 + ] + [ simpleIcon 14 icon + , E.text str + ] + + +iconButton : msg -> E.Color -> I.Icon -> String -> List (E.Attribute msg) -> E.Element msg +iconButton msg color icon label attrs = + I.button attrs + { onPress = Just msg + , label = + E.row + [ E.spacing 8, F.color color, F.size 14 ] + [ simpleIcon 14 icon, E.text label ] + } + + +simpleIcon : Int -> I.Icon -> E.Element msg +simpleIcon size icon = + icon + |> I.withSize (toFloat size) + |> I.toHtml [] + |> E.html + |> E.el [] + + +simpleCircle : Int -> E.Color -> E.Element msg +simpleCircle size color = + E.el + [ B.width 0 + , B.rounded 50 + , E.height (E.px size) + , E.width (E.px size) + , E.centerY + , BG.color color + ] + E.none + + +wall : E.Element msg +wall = + E.el + [ E.height E.fill + , B.color border + , B.widthEach { top = 0, bottom = 0, left = 1, right = 0 } + ] E.none \ No newline at end of file diff --git a/src/Ui/Font.elm b/src/Ui/Font.elm new file mode 100644 index 0000000..8c29028 --- /dev/null +++ b/src/Ui/Font.elm @@ -0,0 +1,19 @@ +module Ui.Font exposing (..) + +import Element.Font as F + + +ibmPlex : F.Font +ibmPlex = + F.external + { url = "https://fonts.googleapis.com/css?family=IBM+Plex+Sans" + , name = "IBM Plex Sans" + } + + +courierPrime : F.Font +courierPrime = + F.external + { url = "https://fonts.googleapis.com/css?family=Courier+Prime" + , name = "Courier Prime" + } diff --git a/src/Ui/Icon.elm b/src/Ui/Icon.elm new file mode 100644 index 0000000..d3eaba1 --- /dev/null +++ b/src/Ui/Icon.elm @@ -0,0 +1,98 @@ +module Ui.Icon exposing + ( simpleIcon + , Button, button + , Link, link + ) + + +import FeatherIcons as I +import Html as H +import Html.Attributes as HA +import Html.Events as HE + + + +-- SIMPLE ICON + + +simpleIcon : List (H.Attribute msg) -> Maybe String -> I.Icon -> H.Html msg +simpleIcon attrs color icon = + icon + |> I.withSize 14 + |> I.withClass ("icon " ++ Maybe.withDefault "" color) + |> I.toHtml attrs + + + +-- BUTTON / GENERAL + + +type alias Button msg = + { background : Maybe String + , icon : I.Icon + , iconColor : Maybe String + , labelColor : Maybe String + , label : Maybe String + , alt : String + , onClick : Maybe msg + } + + +button : List (H.Attribute msg) -> Button msg -> H.Html msg +button attrs config = + let defaultAttrs = + [ HA.attribute "aria-label" config.alt + , HA.classList classes + , case config.onClick of + Just msg -> HE.onClick msg + Nothing -> HA.disabled True + ] + + classes = + case config.background of + Just background -> + [ ( "icon-button", True ) + , ( "background", True ) + , ( background, True ) + ] + + Nothing -> + [ ( "icon-button", True ) ] + in + H.button + (attrs ++ defaultAttrs) + [ simpleIcon [] config.iconColor config.icon + , case config.label of + Just label -> H.span [ HA.class (Maybe.withDefault "" config.labelColor) ] [ H.text label ] + Nothing -> H.text "" + ] + + + +-- LINK / GENERAL + + +type alias Link = + { icon : I.Icon + , iconColor : Maybe String + , label : Maybe String + , alt : String + , link : String + } + + +link : List (H.Attribute msg) -> Link -> H.Html msg +link attrs config = + let defaultAttrs = + [ HA.attribute "aria-label" config.alt + , HA.class "icon-button" + , HA.target "_blank" + , HA.href config.link + ] + in + H.a (attrs ++ defaultAttrs) + [ simpleIcon [] config.iconColor config.icon + , case config.label of + Just label -> H.span [] [ H.text label ] + Nothing -> H.text "" + ] \ No newline at end of file diff --git a/src/Ui/Navigation.elm b/src/Ui/Navigation.elm new file mode 100644 index 0000000..cea5c55 --- /dev/null +++ b/src/Ui/Navigation.elm @@ -0,0 +1,203 @@ +module Ui.Navigation exposing + ( Navigation, view + , elmLogo, toggleOpen, lights, packages, compilation, share, deploy, toggleSplit + ) + +{-| The navigation bar. + +-} + +import FeatherIcons as I +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (onClick, on) +import Svg exposing (svg, use) +import Svg.Attributes as SA +import Data.Status as Status +import Ui.Icon + + +{-| -} +type alias Navigation msg = + { isLight : Bool + , isOpen : Bool + , left : List (Html msg) + , right : List (Html msg) + } + + +{-| -} +view : Navigation msg -> Html msg +view config = + nav + [ id "menu" + , classList + [ ( "open", config.isOpen ) + , ( "closed", not config.isOpen ) + ] + ] + [ section + [ id "actions" ] + [ aside [] config.left + , aside [] config.right + ] + ] + + + +-- BUTTON / PREMADE + + +{-| -} +elmLogo : Html msg +elmLogo = + a [ href "/", class "menu-link", target "_blank" ] + [ svg + [ SA.height "14" + , SA.width "14" + ] + [ use [ SA.xlinkHref "#logo" ] [] + ] + ] + + +{-| -} +toggleOpen : msg -> Bool -> Html msg +toggleOpen onClick_ isMenuOpen = + Ui.Icon.button [ style "padding" "0 10px" ] + { background = Nothing + , icon = if isMenuOpen then I.chevronDown else I.chevronUp + , iconColor = Nothing + , labelColor = Nothing + , label = Nothing + , alt = if isMenuOpen then "Close menu" else "Open menu" + , onClick = Just onClick_ + } + + +{-| -} +toggleSplit : msg -> Html msg +toggleSplit onClick_ = + Ui.Icon.button [ style "padding" "0 5px" ] + { background = Nothing + , icon = I.code + , iconColor = Nothing + , labelColor = Nothing + , label = Nothing + , alt = "Open or close result" + , onClick = Just onClick_ + } + + +{-| -} +lights : msg -> Bool -> Html msg +lights onClick_ isLight = + Ui.Icon.button [ style "padding" "0 10px" ] + { background = Nothing + , icon = if isLight then I.moon else I.sun + , iconColor = Nothing + , labelColor = Nothing + , label = Just "Lights" + , alt = "Switch the color scheme" + , onClick = Just onClick_ + } + + +{-| -} +compilation : msg -> Status.Status -> Html msg +compilation onClick_ status = + let ( icon, iconColor, label ) = + case status of + Status.Changed -> + ( I.refreshCcw + , Just "blue" + , "Rebuild" + ) + + Status.Compiling -> + ( I.loader + , Nothing + , "Compiling..." + ) + + Status.Success -> + ( I.check + , Just "green" + , "Success" + ) + + Status.HasProblems _ -> + ( I.x + , Just "red" + , "Problems found" + ) + + Status.HasProblemsButChanged _ -> + ( I.refreshCcw + , Just "blue" + , "Rebuild" + ) + + Status.HasProblemsButRecompiling _ -> + ( I.loader + , Nothing + , "Compiling..." + ) + + Status.Failed _ -> + ( I.x + , Just "red" + , "Try again later." + ) + in + Ui.Icon.button [ style "padding" "0 10px" ] + { background = Nothing + , icon = icon + , iconColor = iconColor + , label = Just label + , labelColor = Nothing + , alt = "Compile your code (Ctrl-Enter)" + , onClick = Just onClick_ + } + + +{-| -} +packages : msg -> Bool -> Html msg +packages onClick_ isOpen = + Ui.Icon.button [ style "padding" "0 10px" ] + { background = if isOpen then Just "lightblue" else Nothing + , icon = I.package + , iconColor = if isOpen then Just "blue" else Nothing + , label = Just "Packages" + , labelColor = if isOpen then Just "blue" else Nothing + , alt = "Add a package" + , onClick = Just onClick_ + } + + +{-| -} +share : msg -> Html msg +share onClick_ = + Ui.Icon.button [ style "padding" "0 10px" ] + { background = Nothing + , icon = I.link + , iconColor = Nothing + , label = Just "Share" + , labelColor = Nothing + , alt = "Copy link to this code" + , onClick = Just onClick_ + } + + +{-| -} +deploy : msg -> Html msg +deploy onClick_ = + Ui.Icon.button [ style "padding" "0 10px" ] + { background = Nothing + , icon = I.send + , iconColor = Nothing + , label = Just "Deploy" + , labelColor = Nothing + , alt = "Deploy this project without editor attached" + , onClick = Just onClick_ + } diff --git a/src/Ui/Package.elm b/src/Ui/Package.elm new file mode 100644 index 0000000..10749c5 --- /dev/null +++ b/src/Ui/Package.elm @@ -0,0 +1,620 @@ +module Ui.Package exposing + ( Model + , Msg + , init + , open + , subscriptions + , update + , view + ) + +import Components.Button as Button +import Data.Fetched as Fetched +import Data.Http +import Data.Problem as Problem +import Data.Registry as Registry +import Data.Registry.Defaults as Defaults +import Data.Registry.Package as Package +import Data.Registry.Solution as Solution exposing (Solution) +import Data.Registry.Status as Status +import Data.Version as V +import Dict +import Element exposing (Attr) +import Elm.Error as Error +import FeatherIcons +import Html exposing (Html) +import Html.Attributes as Attr +import Html.Attributes.Aria as Aria +import Html.Events as Events +import Html.Keyed as HK +import Html.Lazy as HL +import Http +import Icon +import Json.Decode as Decode +import Json.Encode as E +import Process +import Svg exposing (Svg) +import Svg.Attributes as SvgAttr +import Task +import Ui.Icon +import Ui.Navigation + + +type alias Model = + { query : String + , registry : Fetched.Fetched Registry.Registry + , hash : Maybe String + , debounce : Int + , defaultDirect : List Package.Package + , defaultIndirect : List Package.Package + , isOpen : Bool + } + + +init : List Package.Package -> List Package.Package -> ( Model, Cmd Msg ) +init direct indirect = + ( { query = "" + , registry = Fetched.Loading + , hash = Nothing + , debounce = 0 + , defaultDirect = direct + , defaultIndirect = indirect + , isOpen = False + } + , Registry.fetch GotRegistry + ) + + +width : Int +width = + 350 + + +widthPx : String +widthPx = + String.fromInt width ++ "px" + + +getRegistry : Model -> Registry.Registry +getRegistry model = + case model.registry of + Fetched.Loading -> + Registry.initialWithDefaults model.defaultDirect model.defaultIndirect + + Fetched.Failed _ -> + Registry.initialWithDefaults model.defaultDirect model.defaultIndirect + + Fetched.Success registry -> + registry + + +getSolution : Model -> Solution.Solution +getSolution model = + Solution.toSolution (Dict.values (getRegistry model)) + + +getProblems : Model -> Maybe Problem.Problems +getProblems model = + Registry.getErrors (getRegistry model) + -- |> List.filterMap Data.Http.onlyDecodedErrors + -- TODO + |> Problem.toManyIndexedProblems + |> Problem.init + + +dismissAll : Model -> Model +dismissAll model = + (\( m, _, _ ) -> m) <| + updateRegistry model False <| + \registry -> + ( model, Registry.dismissAll registry, Cmd.none ) + + +open : Model -> Model +open model = + { model | isOpen = True } + + + +-- UPDATE + + +type Msg + = GotRegistry (Result Http.Error (List Package.Package)) + | OnQuery String + | OnDebounce + | OnInstall Package.Package + | OnUninstall Package.Package + | OnDismiss Package.Package + | OnEdited { packageString : String, versionString : String, result : E.Value } + | OnReportResult (Result Http.Error String) + | OnClose + + +update : Msg -> Model -> ( Model, Bool, Cmd Msg ) +update msg model = + case msg of + GotRegistry (Ok news) -> + ( { model + | registry = + Registry.initialWithDefaults model.defaultDirect model.defaultIndirect + |> Registry.fromNews news + |> Fetched.Success + } + , False + , Cmd.none + ) + + GotRegistry (Err err) -> + ( { model | registry = Fetched.Failed err } + , False + , Cmd.none + ) + + OnQuery query -> + ( { model | query = query, debounce = model.debounce + 1 } + , False + , Task.perform (\_ -> OnDebounce) (Process.sleep 300) + ) + + OnDebounce -> + ( { model | debounce = model.debounce - 1 } + , False + , Cmd.none + ) + + OnInstall package -> + updateRegistry model False <| + \registry -> + ( model + , Registry.setStatus package Status.Loading registry + , Registry.attemptEdit Registry.Install package + ) + + OnUninstall package -> + updateRegistry model False <| + \registry -> + ( model + , Registry.setStatus package Status.Loading registry + , Registry.attemptEdit Registry.Uninstall package + ) + + OnDismiss package -> + updateRegistry model False <| + \registry -> + ( model + , Registry.setStatus package Status.NotInstalled registry + , Cmd.none + ) + + OnEdited { packageString, versionString, result } -> + case ( String.split "/" packageString, List.map String.toInt (String.split "." versionString), Decode.decodeValue resultDecoder result ) of + ( [ author, project ], [ Just major, Just minor, Just patch ], Ok (Ok solution) ) -> + let + package : Package.Package + package = + { author = author + , project = project + , version = V.Version major minor patch + } + in + updateRegistry model True <| + \registry -> + ( model + , registry + |> Registry.setStatus package Status.NotInstalled + |> Registry.fromSolution solution + , Cmd.none + ) + + ( [ author, project ], [ Just major, Just minor, Just patch ], Ok (Err err) ) -> + let + package : Package.Package + package = + { author = author + , project = project + , version = V.Version major minor patch + } + in + updateRegistry model False <| + \registry -> + ( model + , Registry.setStatus package (Status.Failed err) registry + , Cmd.none + ) + + _ -> + ( model, False, Cmd.none ) + + OnReportResult _ -> + ( model, False, Cmd.none ) + + OnClose -> + ( { model | isOpen = False }, False, Cmd.none ) + + +resultDecoder : Decode.Decoder (Result Error.Error Solution) +resultDecoder = + Decode.oneOf + [ Decode.map Ok Solution.decoder + , Decode.map Err Error.decoder + ] + + +updateRegistry : Model -> Bool -> (Registry.Registry -> ( Model, Registry.Registry, Cmd Msg )) -> ( Model, Bool, Cmd Msg ) +updateRegistry model shouldRebuild updater = + case model.registry of + Fetched.Loading -> + ( model, shouldRebuild, Cmd.none ) + + Fetched.Failed _ -> + ( model, shouldRebuild, Cmd.none ) + + Fetched.Success registry -> + let + ( newModel, newRegistry, cmd ) = + updater registry + in + ( { newModel | registry = Fetched.Success newRegistry }, shouldRebuild, cmd ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Sub Msg +subscriptions = + Sub.batch + [ Registry.installResult OnEdited + , Registry.uninstallResult OnEdited + ] + + + +-- VIEW + + +view : Model -> Html Msg +view model = + let + openAttrs : List (Html.Attribute msg) + openAttrs = + if model.isOpen then + [ Attr.attribute "open" "true" ] + + else + [] + in + Html.node "dialog" + (Attr.class "backdrop:bg-transparent z-50" + :: openAttrs + ) + [ Html.div [ Attr.class "fixed inset-0 bg-gray-900/80 transition-opacity duration-300 ease-linear data-closed:opacity-0" ] [] + , Html.div [ Attr.class "fixed inset-0 flex focus:outline-none" ] + [ Html.div [ Attr.class "group/dialog-panel relative mr-16 flex w-full max-w-xs flex-1 transform transition duration-300 ease-in-out data-closed:-translate-x-full" ] + [ Html.div [ Attr.class "absolute top-0 left-full flex w-16 justify-center pt-5 duration-300 ease-in-out group-data-closed/dialog-panel:opacity-0" ] + [ Html.button + [ Attr.type_ "button" + , Attr.class "-m-2.5 p-2.5" + , Events.onClick OnClose + ] + [ Html.span [ Attr.class "sr-only" ] + [ Html.text "Close sidebar" + ] + , Icon.xMark [ SvgAttr.class "size-6 text-white" ] + ] + ] + , Html.div + [ Attr.class "fixed top-0 bottom-0 left-0 w-full overflow-y-auto bg-white px-4 pt-6 pb-4 ring-1 shadow-lg shadow-zinc-900/10 ring-zinc-900/7.5 duration-500 ease-in-out data-closed:-translate-x-full min-[416px]:max-w-sm sm:px-6 sm:pb-10 dark:bg-zinc-900 dark:ring-zinc-800" ] + [ Html.nav + [ Attr.class "relative flex flex-1 flex-col" + ] + [ viewRegistry model + ] + ] + ] + ] + ] + + + +-- VIEW REGISTRY + + +viewRegistry : Model -> Html Msg +viewRegistry model = + let + direct = + getRegistry model + |> Registry.filterStatus Status.isDirectDep + |> Registry.getValues + + failed = + getRegistry model + |> Registry.filterStatus Status.isFailed + |> Registry.getValues + in + case model.registry of + Fetched.Loading -> + Icon.arrowPath [ SvgAttr.class "h-5 w-5 text-gray-400 dark:text-gray-500 animate-spin" ] + + Fetched.Failed _ -> + Html.div [ Attr.class "rounded-md bg-red-50 p-3 dark:bg-red-500/15 dark:outline dark:outline-red-500/25" ] + [ Html.div [ Attr.class "flex" ] + [ Html.div [ Attr.class "shrink-0" ] + [ Icon.exclamationCircle [ SvgAttr.class "size-5 text-red-400" ] + ] + , Html.div + [ Attr.class "ml-2" + ] + [ Html.h3 + [ Attr.class "text-sm text-red-800 dark:text-red-200" + ] + [ Html.text "Could not fetch packages! Please try again later." ] + ] + ] + ] + + Fetched.Success registry -> + Html.ul [ Aria.role "list", Attr.class "flex flex-1 flex-col gap-y-4" ] + [ Html.li [] + [ Html.div + [ Attr.class "text-xs/6 font-semibold text-gray-400" + ] + [ Html.text "Installed" ] + , HK.ul + [ Aria.role "list" + , Attr.class "mt-2 space-y-1" + ] + (List.map viewKeyedPackage direct) + , HK.ul + [ Aria.role "list" + , Attr.class "mt-2 space-y-1" + ] + (List.map viewKeyedPackage failed) + ] + , Html.li [] + [ Html.div + [ Attr.class "text-xs/6 font-semibold text-gray-400" + ] + [ Html.text "Registry" ] + , viewQuery model + , if String.isEmpty model.query then + HL.lazy viewPopular registry + + else if model.debounce /= 0 then + Html.div [ Attr.class "mt-2" ] + [ Icon.arrowPath [ SvgAttr.class "h-5 w-5 text-gray-400 dark:text-gray-500 animate-spin" ] ] + + else + viewSearchResults model registry + ] + ] + + + +-- VIEW POPULAR + + +viewPopular : Registry.Registry -> Html Msg +viewPopular registry = + let + popular = + registry + |> Registry.filterStatus Status.isSearchable + |> Registry.filterKeys Defaults.popular + in + HK.ul + [ Aria.role "list" + , Attr.class "mt-2 space-y-1" + ] + (List.map viewKeyedPackage popular) + + + +-- VIEW SEARCH RESULTS + + +viewQuery : Model -> Html Msg +viewQuery model = + Html.input + [ Attr.class "mt-2 block w-full rounded-md bg-white px-3 py-1.5 text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6 dark:bg-white/5 dark:text-white dark:outline-white/10 dark:placeholder:text-gray-500 dark:focus:outline-indigo-500" + , Attr.type_ "search" + , Attr.id "registry-search" + , Attr.placeholder "Search" + , Attr.value model.query + , Aria.ariaLabel "Search" + , Events.onInput OnQuery + ] + [] + + +viewSearchResults : Model -> Registry.Registry -> Html Msg +viewSearchResults model registry = + let + results = + registry + |> Registry.filterStatus Status.isSearchable + |> Registry.getValues + |> Registry.search model.query + in + HK.ul + [ Aria.role "list" + , Attr.class "mt-2 space-y-1" + ] + (List.map viewKeyedPackage results) + + + +-- KEYED CONTAINER + + +viewKeyedContainer : List (Html.Attribute msg) -> List ( String, Html msg ) -> Html msg +viewKeyedContainer attrs = + HK.node "div" (attrs ++ [ Attr.id "package-options" ]) + + +viewKeyedPackage : ( Package.Package, Status.Status ) -> ( String, Html Msg ) +viewKeyedPackage ( package, state ) = + ( Package.toName package + , HL.lazy viewPackage ( package, state ) + ) + + + +-- VIEW PACKAGE + + +viewPackage : ( Package.Package, Status.Status ) -> Html Msg +viewPackage ( package, state ) = + -- [ Html.li + -- [ Attr.class "group flex justify-between gap-x-3 rounded-md p-1 text-sm/6 font-semibold text-gray-700 hover:bg-gray-50 hover:text-amber-600 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-white" + -- ] + -- [ Html.span + -- [ Attr.class "truncate" + -- ] + -- [ Html.text "elm/html" ] + -- , Html.div [ Attr.class "flex gap-x-3" ] + -- [ Html.span [ Attr.class "flex size-6 shrink-0 items-center text-[0.625rem] font-medium text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] + -- [ Html.text "1.0.0" ] + -- , Button.view Button.Button Button.Text Nothing [] [ Icon.plus [ SvgAttr.class "h-5 w-5 text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] ] + -- ] + -- ] + -- , Html.li + -- [ Attr.class "group flex justify-between gap-x-3 rounded-md p-1 text-sm/6 font-semibold text-gray-700 hover:bg-gray-50 hover:text-amber-600 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-white" + -- ] + -- [ Html.span + -- [ Attr.class "truncate" + -- ] + -- [ Html.text "elm/random" ] + -- , Html.div [ Attr.class "flex gap-x-3" ] + -- [ Html.span [ Attr.class "flex size-6 shrink-0 items-center text-[0.625rem] font-medium text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] + -- [ Html.text "2.0.1" ] + -- , Button.view Button.Button Button.Text Nothing [] [ Icon.trash [ SvgAttr.class "h-5 w-5 text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] ] + -- ] + -- ] + -- , Html.li + -- [ Attr.class "group flex justify-between gap-x-3 rounded-md p-1 text-sm/6 font-semibold text-gray-700 hover:bg-gray-50 hover:text-amber-600 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-white" + -- ] + -- [ Html.span + -- [ Attr.class "truncate" + -- ] + -- [ Html.text "elm/random" ] + -- , Html.div [ Attr.class "flex gap-x-3" ] + -- [ Html.span [ Attr.class "flex size-6 shrink-0 items-center text-[0.625rem] font-medium text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] + -- [ Html.text "2.0.1" ] + -- , Button.view Button.Button Button.Text Nothing [] [ Icon.lockClosed [ SvgAttr.class "h-5 w-5 text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] ] + -- ] + -- ] + -- ] + Html.li [ Attr.class "group flex justify-between gap-x-3 rounded-md p-1 text-sm/6 font-semibold text-gray-700 hover:bg-gray-50 hover:text-amber-600 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-white" ] <| + case state of + Status.NotInstalled -> + viewOkPackage package + [ viewPackageVersion package.version + , viewButtonIcon "Install" Icon.plus (OnInstall package) + ] + + Status.IndirectDep version -> + viewOkPackage package + [ viewPackageVersion version + , viewButtonIcon "Install" Icon.plus (OnInstall package) + ] + + Status.Loading -> + viewOkPackage package + [ viewPackageVersion package.version + , viewSimpleIcon [ SvgAttr.class "animate-spin" ] Icon.arrowPath + ] + + Status.DirectDep version -> + viewOkPackage package + [ viewPackageVersion version + , if List.member (Package.toKey package) Defaults.locked then + viewSimpleIcon [] Icon.lockClosed + + else + viewButtonIcon "Uninstall" Icon.trash (OnUninstall package) + ] + + Status.Failed err -> + viewErrorPackage package err + + +viewOkPackage : Package.Package -> List (Html Msg) -> List (Html Msg) +viewOkPackage pkg actions = + [ Html.span [ Attr.class "truncate" ] [ viewPackageName pkg ] + , Html.div [ Attr.class "flex gap-x-3 items-center" ] actions + ] + + +viewErrorPackage : Package.Package -> Error.Error -> List (Html Msg) +viewErrorPackage package error = + let + viewErrorIcon = + Ui.Icon.simpleIcon + [ Attr.style "padding-right" "5px" + , Attr.style "top" "1px" + , Attr.style "width" "20px" + ] + (Just "red") + FeatherIcons.alertCircle + in + [ Html.div + [ Attr.class "package-option__left" + , Attr.style "display" "flex" + , Attr.style "font-size" "12px" + ] + [ viewErrorIcon + , Html.div + [ Attr.class "package-option__error" ] + [ Html.text "Could not install " + , Html.span + [ Attr.style "font-weight" "bold" ] + [ viewPackageDocsLink package <| + Package.toName package + ++ " " + ++ V.toString package.version + ] + , Html.text "." + ] + ] + , Html.div + [ Attr.class "package-option__right" ] + [ viewButtonIcon "Dismiss" Icon.xMark (OnDismiss package) ] + ] + + +viewPackageName : Package.Package -> Html Msg +viewPackageName pkg = + viewPackageDocsLink pkg (Package.toName pkg) + + +viewPackageDocsLink : Package.Package -> String -> Html Msg +viewPackageDocsLink pkg str = + Html.a + [ Attr.target "_blank" + , Attr.href (Package.toDocsLink pkg) + ] + [ Html.text str ] + + +viewPackageVersion : V.Version -> Html Msg +viewPackageVersion version = + Html.span [ Attr.class "flex size-6 shrink-0 items-center text-[0.625rem] font-medium text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] + [ Html.text (V.toString version) ] + + + +-- HELPERS + + +viewSimpleIcon : List (Svg.Attribute msg) -> (List (Svg.Attribute msg) -> Svg msg) -> Html msg +viewSimpleIcon extraAttrs icon = + icon ([ SvgAttr.class "h-5 w-5 text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] ++ extraAttrs) + + +viewButtonIcon : String -> (List (Svg.Attribute msg) -> Svg msg) -> msg -> Html msg +viewButtonIcon alt icon onClick = + Button.view (Button.Button onClick) Button.Text Nothing [ Aria.ariaLabel alt ] <| + [ icon [ SvgAttr.class "h-5 w-5 text-gray-400 group-hover:text-amber-600 dark:group-hover:text-white" ] ] diff --git a/src/Ui/Problem.elm b/src/Ui/Problem.elm new file mode 100644 index 0000000..21c819e --- /dev/null +++ b/src/Ui/Problem.elm @@ -0,0 +1,283 @@ +module Ui.Problem exposing (..) + +{-| The formatting of compilation problems. +-} + +import Data.Problem exposing (..) +import Elm.Error as Error +import FeatherIcons as I +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (onClick) +import Ui.Icon + + +type alias Config msg = + { onJump : Error.Region -> msg + , onPrevious : Maybe Error.Region -> msg + , onNext : Maybe Error.Region -> msg + , onMinimize : msg + } + + +viewCarousel : Config msg -> Problems -> Html msg +viewCarousel config problems = + let + focused = + getFocused problems + + previousRegion = + Maybe.andThen getRegion (getPrevious problems) + + nextRegion = + Maybe.andThen getRegion (getNext problems) + in + div + [ id "problems" ] + [ viewContainer + [ viewHeader + [ viewTitle focused.title + , viewNavigation + [ viewLocation config.onJump focused.location + , viewPreviousButton (config.onPrevious previousRegion) problems + , viewNextButton (config.onNext nextRegion) problems + , viewMinimize config.onMinimize + ] + ] + , viewBody focused.message + ] + ] + + +viewCarouselMini : Config msg -> Problems -> Html msg +viewCarouselMini config problems = + let + focused = + getFocused problems + + previousRegion = + Maybe.andThen getRegion (getPrevious problems) + + nextRegion = + Maybe.andThen getRegion (getNext problems) + + viewLocationMini = + case focused.location of + General _ -> + text "" + + Specific specific -> + a [ class "problem-region", onClick (config.onJump specific.region) ] + [ text (String.fromInt specific.region.start.line ++ "| ") ] + in + div + [ id "problems-mini" ] + [ viewLocationMini + , a [ onClick config.onMinimize ] [ text (getSummary focused) ] + , viewNavigation + [ viewPreviousButton (config.onPrevious previousRegion) problems + , viewNextButton (config.onNext nextRegion) problems + ] + ] + + +viewPreviousButton : msg -> Problems -> Html msg +viewPreviousButton onPrevious problems = + Ui.Icon.button [ style "padding" "0 10px" ] + { background = Nothing + , icon = I.chevronLeft + , iconColor = Nothing + , label = Nothing + , labelColor = Nothing + , alt = "See previous problem" + , onClick = + if hasPrevious problems then + Just onPrevious + + else + Nothing + } + + +viewNextButton : msg -> Problems -> Html msg +viewNextButton onNext problems = + Ui.Icon.button [ style "padding" "0 10px" ] + { background = Nothing + , icon = I.chevronRight + , iconColor = Nothing + , labelColor = Nothing + , label = Nothing + , alt = "See next problem" + , onClick = + if hasNext problems then + Just onNext + + else + Nothing + } + + +viewMinimize : msg -> Html msg +viewMinimize onMinimize = + Ui.Icon.button [ style "padding" "0 10px" ] + { background = Nothing + , icon = I.minimize2 + , iconColor = Nothing + , label = Nothing + , labelColor = Nothing + , alt = "Minimize problem view" + , onClick = Just onMinimize + } + + + +-- AS LIST + + +viewList : (Error.Region -> msg) -> Problems -> Html msg +viewList onJumpToProblem problems = + let + viewProblem problem = + viewContainer + [ viewHeader + [ viewTitle problem.title + , viewNavigation [ viewLocation onJumpToProblem problem.location ] + ] + , viewBody problem.message + ] + in + div [ id "problems" ] (List.map viewProblem (getAll problems)) + + + +-- PARTS + + +viewContainer : List (Html msg) -> Html msg +viewContainer = + div [ class "problem-container" ] + + +viewHeader : List (Html msg) -> Html msg +viewHeader = + div [ class "problem-header" ] + + +viewNavigation : List (Html msg) -> Html msg +viewNavigation = + nav [ class "problem-navigation" ] + + +viewTitle : String -> Html msg +viewTitle title = + div [ class "problem-title" ] [ text title ] + + +viewLocation : (Error.Region -> msg) -> Location -> Html msg +viewLocation onJumpToProblem location = + case location of + General { path } -> + viewModuleName path + + Specific { region } -> + viewRegion onJumpToProblem region + + +viewRegion : (Error.Region -> msg) -> Error.Region -> Html msg +viewRegion onJumpToProblem region = + a [ class "problem-region", onClick (onJumpToProblem region) ] + [ text "Jump to problem" ] + + +viewModuleName : Maybe String -> Html msg +viewModuleName name = + div [ class "problem-region" ] [ text (Maybe.withDefault "" name) ] + + +viewBody : List Error.Chunk -> Html msg +viewBody = + div [ class "problem-body" ] << viewChunks + + +viewChunks : List Error.Chunk -> List (Html msg) +viewChunks chunks = + case chunks of + [] -> + [ text "\n\n\n" ] + + chunk :: others -> + let + htmlChunk = + case chunk of + Error.Unstyled string -> + text string + + Error.Styled style string -> + span (styleToClasses style) [ text string ] + in + htmlChunk :: viewChunks others + + +styleToClasses : Error.Style -> List (Attribute msg) +styleToClasses { bold, underline, color } = + [ classList + [ ( "bold", bold ) + , ( "underline", underline ) + , ( Maybe.map colorToClass color + |> Maybe.withDefault "" + , True + ) + ] + ] + + +colorToClass : Error.Color -> String +colorToClass color = + case color of + Error.Red -> + "red" + + Error.RED -> + "red" + + Error.Magenta -> + "magenta" + + Error.MAGENTA -> + "magenta" + + Error.Yellow -> + "yellow" + + Error.YELLOW -> + "yellow" + + Error.Green -> + "green" + + Error.GREEN -> + "green" + + Error.Cyan -> + "cyan" + + Error.CYAN -> + "cyan" + + Error.Blue -> + "blue" + + Error.BLUE -> + "blue" + + Error.White -> + "white" + + Error.WHITE -> + "white" + + Error.Black -> + "black" + + Error.BLACK -> + "black"