@@ -0,0 +1,1210 @@
1+module Deps exposing (main)
5+import Browser.Dom as Dom
6+import Dict exposing (Dict)
7+import Elm.Constraint as Constraint exposing (Constraint)
8+import Html exposing (..)
9+import Html.Attributes exposing (..)
10+import Html.Events exposing (..)
11+import Html.Keyed as Keyed
12+import Html.Lazy exposing (..)
14+import Json.Decode as D
15+import Json.Encode as E
17+import Svg.Attributes as S
30+ , subscriptions = \_ -> Sub.none
43+ , registry : Registry
46+ , future : List Change
56+ = Failure Checkpoint (List Change)
57+ | Waiting Checkpoint (List Change)
61+type alias Checkpoint =
62+ { direct : Dict String Bounds
63+ , indirect : Dict String Bounds
68+ = New Version NewBounds
69+ | Old Version Version OldBounds
96+ | TweakOldDirect String OldBounds
97+ | TweakNewDirect String NewBounds
98+ | TweakOldIndirect String OldBounds
99+ | TweakNewIndirect String NewBounds
100+ | DeleteDirect String
101+ | DeleteIndirect String
109+ { direct : Dict String PBounds
110+ , indirect : Dict String PBounds
115+ = PNew (Maybe Version) NewBounds
116+ | POld Version Version OldBounds
119+toPreview : Origin -> Checkpoint -> List Change -> Preview
120+toPreview origin checkpoint changes =
122+ toPreviewBounds _ bounds =
124+ New vsn nb -> PNew (Just vsn) nb
125+ Old old new ob -> POld old new ob
128+ { direct = Dict.map toPreviewBounds checkpoint.direct
129+ , indirect = Dict.map toPreviewBounds checkpoint.indirect
132+ List.foldr (step origin) start changes
135+step : Origin -> Change -> Preview -> Preview
136+step origin change preview =
139+ massChange OLocked preview
142+ massChange OPatch preview
145+ massChange OMinor preview
148+ massChange OMajor preview
153+ case Dict.get pkg origin.direct of
154+ Just vsn -> POld vsn vsn OLocked
156+ case Dict.get pkg origin.indirect of
157+ Just vsn -> POld vsn vsn OLocked
158+ Nothing -> PNew Nothing NAny
160+ { direct = Dict.insert pkg pBound preview.direct
161+ , indirect = Dict.remove pkg preview.indirect
164+ TweakOldDirect pkg oldBounds ->
165+ { direct = Dict.update pkg (alterOld oldBounds) preview.direct
166+ , indirect = preview.indirect
169+ TweakNewDirect pkg newBounds ->
170+ { direct = Dict.update pkg (alterNew newBounds) preview.direct
171+ , indirect = preview.indirect
174+ TweakOldIndirect pkg oldBounds ->
175+ { direct = preview.direct
176+ , indirect = Dict.update pkg (alterOld oldBounds) preview.indirect
179+ TweakNewIndirect pkg newBounds ->
180+ { direct = preview.direct
181+ , indirect = Dict.update pkg (alterNew newBounds) preview.indirect
185+ { direct = Dict.remove pkg preview.direct
186+ , indirect = preview.indirect
189+ DeleteIndirect pkg ->
190+ { direct = preview.direct
191+ , indirect = Dict.remove pkg preview.indirect
195+massChange : OldBounds -> Preview -> Preview
196+massChange oldBounds preview =
198+ changeBounds _ bounds =
200+ PNew vsn newBounds -> PNew vsn newBounds
201+ POld old new _ -> POld old new oldBounds
203+ { direct = Dict.map changeBounds preview.direct
204+ , indirect = Dict.map changeBounds preview.indirect
208+alterOld : OldBounds -> Maybe PBounds -> Maybe PBounds
209+alterOld ob maybeBounds =
216+ PNew vsn nb -> Just (PNew vsn nb)
217+ POld old new _ -> Just (POld old new ob)
220+alterNew : NewBounds -> Maybe PBounds -> Maybe PBounds
221+alterNew nb maybeBounds =
228+ PNew vsn _ -> Just (PNew vsn nb)
229+ POld old new ob -> Just (POld old new ob)
236+init : () -> (Model, Cmd Msg)
240+ chkp = toInitialCheckpoint origin
243+ { status = Waiting chkp []
245+ , search = { query = "", focus = Nothing }
246+ , registry = registryTODO
254+ { direct : Dict String Version
255+ , indirect : Dict String Version
263+ [ ("elm/browser", Version 1 0 1)
264+ , ("elm/core", Version 1 0 2)
265+ , ("elm/html", Version 1 0 0)
266+ , ("elm/http", Version 2 0 0)
267+ , ("elm/json", Version 1 1 2)
268+ , ("elm/project-metadata-utils", Version 1 0 0)
269+ , ("elm/svg", Version 1 0 1)
270+ , ("elm-explorations/markdown", Version 1 0 0)
274+ [ ("elm/parser", Version 1 1 0)
275+ , ("elm/time", Version 1 0 0)
276+ , ("elm/url", Version 1 0 0)
277+ , ("elm/virtual-dom", Version 1 0 2)
286+toInitialCheckpoint : Origin -> Checkpoint
287+toInitialCheckpoint origin =
288+ { direct = Dict.map (\_ v -> Old v v OLocked) origin.direct
289+ , indirect = Dict.map (\_ v -> Old v v OLocked) origin.indirect
293+toCheckpoint : Dict String Version -> Preview -> Maybe Checkpoint
294+toCheckpoint solution preview =
296+ direct = Dict.foldr (addBound solution) Dict.empty preview.direct
297+ indirect = Dict.foldr (addBound solution) Dict.empty preview.indirect
299+ if Dict.size direct == Dict.size preview.direct then
300+ Just (Checkpoint direct indirect)
305+addBound : Dict String Version -> String -> PBounds -> Dict String Bounds -> Dict String Bounds
306+addBound solution pkg bounds dict =
307+ case Dict.get pkg solution of
314+ Dict.insert pkg (New new newBounds) dict
316+ POld old _ oldBounds ->
317+ Dict.insert pkg (Old old new oldBounds) dict
329+ | GotSolution Int (Result Http.Error (Dict String Version))
330+ | SearchTouched SearchMsg
333+update : Msg -> Model -> (Model, Cmd Msg)
335+ case Debug.log "msg" msg of
340+ let (checkpoint, changes) = getCheckpoint model.status in
341+ await checkpoint (latest::changes) { model | future = [] }
344+ case getCheckpoint model.status of
345+ (checkpoint, latest :: previous) ->
346+ await checkpoint previous { model | future = latest :: model.future }
350+ [] -> ( model, Cmd.none )
352+ latest :: previous ->
353+ await (toInitialCheckpoint model.origin) previous
354+ { model | past = [], future = latest :: model.future }
357+ case model.future of
362+ let (checkpoint, changes) = getCheckpoint model.status in
363+ await checkpoint (next::changes) { model | future = nexterer }
365+ GotSolution id result ->
366+ if model.id /= id then
370+ (oldCheckpoint, changes) = getCheckpoint model.status
374+ ( { model | status = Failure oldCheckpoint changes }, Cmd.none )
377+ case toCheckpoint solution (toPreview model.origin oldCheckpoint changes) of
379+ ( { model | status = Failure oldCheckpoint changes }
383+ Just newCheckpoint ->
385+ | status = Success newCheckpoint
386+ , past = changes ++ model.past
391+ SearchTouched searchMsg ->
392+ case updateSearch model.registry searchMsg model.search of
396+ SUpdate newSearch ->
397+ ( { model | search = newSearch }
401+ SManualBlur newSearch ->
402+ ( { model | search = newSearch }
403+ , Task.attempt (\_ -> NoOp) (Dom.blur searchDepsID)
407+ let (checkpoint, changes) = getCheckpoint model.status in
408+ await checkpoint (AddDirect name :: changes)
410+ | search = { query = "", focus = Nothing }
415+getCheckpoint : Status -> (Checkpoint, List Change)
416+getCheckpoint status =
418+ Failure chkp cs -> (chkp, cs)
419+ Waiting chkp cs -> (chkp, cs)
420+ Success chkp -> (chkp, [])
423+await : Checkpoint -> List Change -> Model -> (Model, Cmd Msg)
424+await checkpoint changes model =
427+ preview = toPreview model.origin checkpoint changes
431+ | status = Waiting checkpoint changes
436+ { url = "/elm-stuff/solve"
440+ [ ("direct", E.dict identity encodeConstraint preview.direct)
441+ , ("indirect", E.dict identity encodeConstraint preview.indirect)
443+ , expect = Http.expectJson (GotSolution id) solutionDecoder
452+view : Model -> Browser.Document Msg
454+ { title = "elm.json"
457+ [ style "width" "calc(100% - 500px - 2em)"
458+ , style "position" "fixed"
461+ , style "bottom" "0"
462+ , style "overflow-x" "hidden"
463+ , style "overflow-y" "scroll"
464+ , style "filter" "blur(4px)"
465+ , style "white-space" "pre"
466+ , style "font-family" "monospace"
470+ , viewEditPanel model
475+viewEditPanel : Model -> Html Msg
476+viewEditPanel model =
478+ [ style "width" "500px"
479+ , style "position" "fixed"
482+ , style "bottom" "0"
483+ , style "overflow-y" "scroll"
484+ , style "background-color" "white"
485+ , style "padding" "1em"
487+ [ node "style" [] [ text styles ]
489+ [ style "display" "flex"
490+ , style "justify-content" "space-between"
493+ , lazy3 viewUndoRedo model.status model.past model.future
496+ [ style "display" "flex"
497+ , style "justify-content" "space-between"
498+ , style "align-items" "center"
500+ [ h2 [] [ text "Dependencies" ]
501+ , Html.map SearchTouched <|
502+ lazy4 viewSearch searchDepsID "Package Search" model.registry model.search
504+ , lazy2 viewStatus model.origin model.status
508+viewMassUpdates : Html Msg
511+ [ text "Mass Updates: "
512+ , activeButton (Commit MassLock ) (text "LOCK")
513+ , activeButton (Commit MassPatch) (text "PATCH")
514+ , activeButton (Commit MassMinor) (text "MINOR")
515+ , activeButton (Commit MassMajor) (text "MAJOR")
519+viewUndoRedo : Status -> List Change -> List Change -> Html Msg
520+viewUndoRedo status past future =
523+ List.isEmpty past &&
525+ Failure _ cs -> List.isEmpty cs
526+ Waiting _ cs -> List.isEmpty cs
533+ [ if hasNoPast then inactiveButton undoIcon else activeButton Undo undoIcon
534+ , if hasNoFuture then inactiveButton redoIcon else activeButton Redo redoIcon
538+activeButton : msg -> Html msg -> Html msg
539+activeButton msg content =
540+ button [ class "button", onClick msg ] [ content ]
543+inactiveButton : Html msg -> Html msg
544+inactiveButton content =
545+ button [ class "button-inactive" ] [ content ]
552+viewStatus : Origin -> Status -> Html Msg
553+viewStatus origin status =
555+ (directs, indirects) = viewStatusRows origin status
558+ [ viewTable "Direct" <| Dict.toList directs
559+ , viewTable "Indirect" <| Dict.toList indirects
563+viewStatusRows : Origin -> Status -> (Dict String (Html Msg), Dict String (Html Msg))
564+viewStatusRows origin status =
566+ Failure checkpoint changes ->
567+ let preview = toPreview origin checkpoint changes in
568+ ( Dict.map (lazy2 viewWaitingRow) preview.direct
569+ , Dict.map (lazy2 viewWaitingRow) preview.indirect
572+ Waiting checkpoint changes ->
573+ let preview = toPreview origin checkpoint changes in
574+ ( Dict.map (lazy2 viewWaitingRow) preview.direct
575+ , Dict.map (lazy2 viewWaitingRow) preview.indirect
578+ Success checkpoint ->
579+ ( Dict.map (lazy2 viewSuccessRow) checkpoint.direct
580+ , Dict.map (lazy2 viewSuccessRow) checkpoint.indirect
584+viewSuccessRow : String -> Bounds -> Html Msg
585+viewSuccessRow pkg bounds =
587+ New version newBounds ->
588+ viewRow pkg (RowNew version)
590+ Old old new oldBounds ->
591+ viewRow pkg (RowOld old new)
594+viewWaitingRow : String -> PBounds -> Html Msg
595+viewWaitingRow pkg bounds =
597+ PNew vsn newBounds ->
598+ viewRow pkg (RowNewGuess vsn)
600+ POld old new oldBounds ->
601+ viewRow pkg (RowOldGuess old new)
608+viewTable : String -> List (String, Html Msg) -> Html Msg
609+viewTable title rows =
610+ table [ style "padding-bottom" "1em" ]
612+ , thead [] [ tr [] [ td [ class "table-title" ] [ text title ] ] ]
613+ , Keyed.node "tbody" [] rows
617+viewColgroup : Html msg
620+ [ col [ style "width" "350px" ] []
621+ , col [ style "width" "50px" ] []
622+ , col [ style "width" "50px" ] []
623+ , col [ style "width" "50px" ] []
629+ | RowOld Version Version
630+ | RowNewGuess (Maybe Version)
631+ | RowOldGuess Version Version
634+viewRow : String -> RowInfo -> Html msg
638+ viewRowHelp pkg (text "") (text "") (viewVersion "black" vsn)
640+ RowNewGuess Nothing ->
641+ viewRowHelp pkg (text "") (text "") (text "")
643+ RowNewGuess (Just v) ->
644+ viewRowHelp pkg (text "") (text "") (viewVersion "#eeeeee" v)
648+ then viewRowHelp pkg (text "") (text "") (viewVersion "#cccccc" new)
649+ else viewRowHelp pkg (viewVersion "#cccccc" old) (viewArrow "#cccccc") (viewVersion "black" new)
651+ RowOldGuess old new ->
653+ then viewRowHelp pkg (text "") (text "") (viewVersion "#eeeeee" new)
654+ else viewRowHelp pkg (viewVersion "#eeeeee" old) (viewArrow "#eeeeee") (viewVersion "#eeeeee" new)
657+viewRowHelp : String -> Html msg -> Html msg -> Html msg -> Html msg
658+viewRowHelp pkg oldHtml arrowHtml newHtml =
660+ [ td [ style "font-family" "monospace" ] [ text pkg ]
661+ , td [ style "text-align" "right" ] [ oldHtml ]
662+ , td [ style "text-align" "center" ] [ arrowHtml ]
663+ , td [ ] [ newHtml ]
667+viewVersion : String -> Version -> Html msg
668+viewVersion color (Version x y z) =
670+ [ style "font-family" "monospace"
671+ , style "color" color
672+ , style "transition" "color 1s"
678+viewArrow : String -> Html msg
681+ [ style "color" color
682+ , style "transition" "color 1s"
692+type alias Registry = Dict String (List Char)
695+toRegistry : List String -> Registry
696+toRegistry packages =
697+ Dict.fromList (List.map (\n -> (n, toSearchChars n)) packages)
700+toSearchChars : String -> List Char
701+toSearchChars string =
702+ String.toList (String.toLower string)
705+registryTODO : Registry
708+ [ "elm-explorations/test"
709+ , "elm-explorations/markdown"
717+ , "elm/project-metadata-utils"
745+ | SClickMatch String
751+ | SManualBlur Search
755+updateSearch : Registry -> SearchMsg -> Search -> SearchNext
756+updateSearch registry msg search =
759+ SUpdate { query = query, focus = Just 0 }
763+ newFocus = Maybe.map (\n -> Basics.max 0 (n - 1)) search.focus
765+ SUpdate { search | focus = newFocus }
769+ numMatches = List.length (getBestMatches search.query registry)
770+ newFocus = Maybe.map (\n -> Basics.min numMatches (n + 1)) search.focus
772+ SUpdate { search | focus = newFocus }
775+ SUpdate { search | focus = Just 0 }
778+ SUpdate { search | focus = Nothing }
781+ SManualBlur { search | focus = Nothing }
784+ case search.focus of
789+ if Dict.member search.query registry
790+ then SAdd search.query
794+ case getMatch n (getBestMatches search.query registry) of
795+ Just match -> SUpdate { query = match, focus = Just 0 }
799+ if Dict.member search.query registry
800+ then SAdd search.query
803+ SClickMatch match ->
804+ SUpdate { query = match, focus = Just 0 }
807+getMatch : Int -> List (Int, String) -> Maybe String
813+ (_, match) :: worseMatches ->
819+ getMatch (n-1) worseMatches
826+searchDepsID : String
827+searchDepsID = "search-deps"
830+searchTestID : String
831+searchTestID = "search-test"
834+viewSearch : String -> String -> Registry -> Search -> Html SearchMsg
835+viewSearch searchID ghostText registry search =
836+ div [ style "position" "relative" ]
837+ [ lazy3 viewSearchQuery searchID ghostText search.query
838+ , lazy2 viewSearchAdd search.query registry
839+ , lazy3 viewSearchMatches search.query search.focus registry
843+viewSearchAdd : String -> Registry -> Html SearchMsg
844+viewSearchAdd query registry =
845+ if Dict.member query registry then
846+ activeButton SClickAdd (text "Add")
848+ inactiveButton (text "Add")
851+viewSearchMatches : String -> Maybe Int -> Registry -> Html SearchMsg
852+viewSearchMatches query focus registry =
858+ if String.isEmpty query
861+ case getBestMatches query registry of
866+ div [ class "search-matches" ] <|
867+ List.indexedMap (viewSearchMatch (n-1)) bestMatches
870+viewSearchMatch : Int -> Int -> (Int, String) -> Html SearchMsg
871+viewSearchMatch target actual (_, name) =
873+ [ class "search-match"
874+ , classList [("search-match-focused", target == actual)]
875+ , onClick (SClickMatch name)
877+ [ div [ style "padding" "0.5em 1em" ] [ text name ]
885+viewSearchQuery : String -> String -> String -> Html SearchMsg
886+viewSearchQuery searchID ghostText query =
890+ , placeholder ghostText
891+ , autocomplete False
892+ , class "search-input"
895+ , on "keydown" keyDecoder
902+keyDecoder : D.Decoder SearchMsg
905+ check up down enter escape value =
908+ else if value == down then
910+ else if value == enter then
912+ else if value == escape then
915+ D.fail "not up or down"
918+ [ D.field "key" D.string
919+ |> D.andThen (check "ArrowUp" "ArrowDown" "Enter" "Escape")
920+ , D.field "keyCode" D.int
921+ |> D.andThen (check 38 40 13 27)
929+getBestMatches : String -> Registry -> List (Int, String)
930+getBestMatches query registry =
931+ Dict.foldl (addMatch (toSearchChars query)) [] registry
934+addMatch : List Char -> String -> List Char -> List (Int, String) -> List (Int, String)
935+addMatch queryChars targetName targetChars bestMatches =
936+ case distance 0 queryChars targetChars of
941+ insert 4 targetName dist bestMatches
944+insert : Int -> String -> Int -> List (Int, String) -> List (Int, String)
945+insert limit name dist bestMatches =
953+ ((bestDist, bestName) as best) :: worseMatches ->
954+ if dist < bestDist then
955+ (dist, name) :: List.take (limit - 1) bestMatches
957+ best :: insert (limit - 1) name dist worseMatches
960+distance : Int -> List Char -> List Char -> Maybe Int
961+distance dist queryChars targetChars =
964+ case dist + List.length targetChars of
975+ distance dist qcs tcs
977+ distance (dist + 1) queryChars tcs
986+ icon "M255.545 8c-66.269.119-126.438 26.233-170.86 68.685L48.971 40.971C33.851 25.851 8 36.559 8 57.941V192c0 13.255 10.745 24 24 24h134.059c21.382 0 32.09-25.851 16.971-40.971l-41.75-41.75c30.864-28.899 70.801-44.907 113.23-45.273 92.398-.798 170.283 73.977 169.484 169.442C423.236 348.009 349.816 424 256 424c-41.127 0-79.997-14.678-110.63-41.556-4.743-4.161-11.906-3.908-16.368.553L89.34 422.659c-4.872 4.872-4.631 12.815.482 17.433C133.798 479.813 192.074 504 256 504c136.966 0 247.999-111.033 248-247.998C504.001 119.193 392.354 7.755 255.545 8z"
991+ icon "M256.455 8c66.269.119 126.437 26.233 170.859 68.685l35.715-35.715C478.149 25.851 504 36.559 504 57.941V192c0 13.255-10.745 24-24 24H345.941c-21.382 0-32.09-25.851-16.971-40.971l41.75-41.75c-30.864-28.899-70.801-44.907-113.23-45.273-92.398-.798-170.283 73.977-169.484 169.442C88.764 348.009 162.184 424 256 424c41.127 0 79.997-14.678 110.629-41.556 4.743-4.161 11.906-3.908 16.368.553l39.662 39.662c4.872 4.872 4.631 12.815-.482 17.433C378.202 479.813 319.926 504 256 504 119.034 504 8.001 392.967 8 256.002 7.999 119.193 119.646 7.755 256.455 8z"
994+unlockIcon : Html msg
996+ icon "M423.5 0C339.5.3 272 69.5 272 153.5V224H48c-26.5 0-48 21.5-48 48v192c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V272c0-26.5-21.5-48-48-48h-48v-71.1c0-39.6 31.7-72.5 71.3-72.9 40-.4 72.7 32.1 72.7 72v80c0 13.3 10.7 24 24 24h32c13.3 0 24-10.7 24-24v-80C576 68 507.5-.3 423.5 0z"
1001+ icon "M400 224h-24v-72C376 68.2 307.8 0 224 0S72 68.2 72 152v72H48c-26.5 0-48 21.5-48 48v192c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V272c0-26.5-21.5-48-48-48zm-104 0H152v-72c0-39.7 32.3-72 72-72s72 32.3 72 72v72z"
1004+icon : String -> Html msg
1007+ [ style "display" "inline-flex"
1008+ , style "align-self" "center"
1009+ , style "top" ".125em"
1010+ , style "position" "relative"
1013+ [ S.viewBox "0 0 512 512"
1018+ [ S.fill "currentColor"
1031+ Version Int Int Int
1035+-- ENCODE CONSTRAINTS
1038+encodeConstraint : PBounds -> E.Value
1039+encodeConstraint bounds =
1041+ POld (Version x y z) _ oldBounds ->
1043+ OLocked -> E.string <| v2s x y z ++ " <= v < " ++ v2s x y (z + 1)
1044+ OPatch -> E.string <| v2s x y z ++ " <= v < " ++ v2s x y max16
1045+ OMinor -> E.string <| v2s x y z ++ " <= v < " ++ v2s x max16 0
1046+ OMajor -> E.string <| v2s x y z ++ " <= v < " ++ v2s max16 0 0
1048+ OCustom c -> Constraint.encode c
1050+ PNew _ newBounds ->
1053+ NCustom c -> Constraint.encode c
1058+ E.string <| v2s 1 0 0 ++ " <= v <= " ++ v2s max16 max16 max16
1066+v2s : Int -> Int -> Int -> String
1067+v2s major minor patch =
1068+ String.fromInt major ++ "." ++ String.fromInt minor ++ "." ++ String.fromInt patch
1075+solutionDecoder : D.Decoder (Dict String Version)
1077+ D.dict versionDecoder
1080+versionDecoder : D.Decoder Version
1084+ case fromString str of
1085+ Just vsn -> D.succeed vsn
1086+ Nothing -> D.fail "invalid version number"
1088+ D.andThen toVersion D.string
1091+fromString : String -> Maybe Version
1093+ case List.map String.toInt (String.split "." string) of
1094+ [Just major, Just minor, Just patch] ->
1095+ fromStringHelp major minor patch
1101+fromStringHelp : Int -> Int -> Int -> Maybe Version
1102+fromStringHelp major minor patch =
1103+ if major >= 0 && minor >= 0 && patch >= 0 then
1104+ Just (Version major minor patch)
1110+-- TODO delete everything below here
1116+ font-family: sans-serif;
1118+ background-color: #cccccc;
1121+ padding: 0.5em 1em;
1122+ border: 1px solid #cccccc;
1123+ border-radius: 2px;
1126+ position: absolute;
1130+ background-color: white;
1133+ border-left: 1px solid #cccccc;
1134+ border-right: 1px solid #cccccc;
1135+ border-bottom: 1px solid #cccccc;
1137+.search-match:hover {
1138+ background-color: #eeeeee;
1141+.search-match-focused {
1142+ background-color: #60B5CC !important;
1143+ border-color: #60B5CC;
1147+ padding: 0.5em 1em;
1148+ border: 1px solid #60B5CC;
1149+ background-color: white;
1150+ border-radius: 2px;
1155+ background-color: #60B5CC;
1159+ border-color: #5A6378;
1160+ background-color: #5A6378;
1163+ padding: 0.5em 1em;
1164+ border: 1px solid #cccccc;
1165+ background-color: white;
1166+ border-radius: 2px;
1170+ text-transform: uppercase;
1180+ "type": "application",
1181+ "source-directories": [
1184+ "elm-version": "0.19.0",
1187+ "elm/browser": "1.0.1",
1188+ "elm/core": "1.0.2",
1189+ "elm/html": "1.0.0",
1190+ "elm/http": "2.0.0",
1191+ "elm/json": "1.1.2",
1192+ "elm/project-metadata-utils": "1.0.0",
1193+ "elm/svg": "1.0.1",
1194+ "elm-explorations/markdown": "1.0.0"
1197+ "elm/bytes": "1.0.7",
1198+ "elm/file": "1.0.1",
1199+ "elm/parser": "1.1.0",
1200+ "elm/time": "1.0.0",
1201+ "elm/url": "1.0.0",
1202+ "elm/virtual-dom": "1.0.2"
1205+ "test-dependencies": {