@@ -1,26 +1,27 @@
1port module Main exposing (main)
1port module Main exposing (main)
3import Animator
3import Animator
4+import Browser.Events exposing (onAnimationFrameDelta)
4import Dict exposing (Dict)
5import Dict exposing (Dict)
5import Element exposing (..)
6import Element exposing (..)
6import Element.Background as Background
7import Element.Background as Background
7import Element.Border as Border
8import Element.Border as Border
8−import Element.Events as Events
9+import Element.Events as Events exposing (onClick)
9import Element.Font as Font
10import Element.Font as Font
10import Element.Input as Input
11import Element.Input as Input
11import Element.Keyed as Keyed
12import Element.Keyed as Keyed
12−import Heroicons.Outline
13import Html.Attributes
13import Html.Attributes
15import Json.Decode as JD
14import Json.Decode as JD
16import List.Extra
15import List.Extra
17−import Svg.Attributes exposing (opacity)
16+import Set exposing (Set)
18import Time exposing (Posix)
17import Time exposing (Posix)
20import Ur.Cmd
19import Ur.Cmd
21import Ur.Deconstructor as D
20import Ur.Deconstructor as D
21+import Ur.Jam exposing (isSig)
22import Ur.Run
22import Ur.Run
23import Ur.Sub
23import Ur.Sub
24+import Ur.Types exposing (Noun(..))
26url : String
27url : String
@@ -28,18 +29,36 @@ url =
28 "http://localhost:8080"
29 "http://localhost:8080"
32+type SearchEngineState
38+deconstructSearchEngineState : D.Deconstructor SearchEngineState
39+deconstructSearchEngineState =
41+ [ D.const D.cord "loading" |> D.map (\_ -> Loading)
42+ , D.const D.cord "failed" |> D.map (\_ -> Failed)
43+ , D.const D.cord "completed" |> D.map (\_ -> Completed)
31type alias Model =
47type alias Model =
32 { ship : Maybe String
48 { ship : Maybe String
33 , search : String
49 , search : String
34 , initiatedSearch : Maybe String
50 , initiatedSearch : Maybe String
35 , searchResults : List SearchResult
51 , searchResults : List SearchResult
36 , resultTimelines : Dict String ( Animator.Timeline Int, Animator.Timeline Bool )
52 , resultTimelines : Dict String ( Animator.Timeline Int, Animator.Timeline Bool )
53+ , engines : List ( String, SearchEngineState )
54+ , selectedEngines : Set String
40type alias SearchResult =
58type alias SearchResult =
41 { title : String
59 { title : String
42 , link : String
60 , link : String
61+ , engines : Set String
@@ -48,9 +67,10 @@ type Msg
48 | GotShipName String
67 | GotShipName String
49 | UpdateSearch String
68 | UpdateSearch String
50 | Search String
69 | Search String
51− | UpdateSearchResults (List SearchResult)
70+ | UpdateSearchResults (List SearchResult) (List ( String, SearchEngineState ))
52 | OpenResult String
71 | OpenResult String
53 | Tick Posix
72 | Tick Posix
73+ | ToggleSearchEngine String
56main : Ur.Run.Program Model Msg
76main : Ur.Run.Program Model Msg
@@ -59,9 +79,11 @@ main =
60 ( { ship = Nothing
80 ( { ship = Nothing
61 , search = ""
81 , search = ""
62− , initiatedSearch = Nothing
82+ , initiatedSearch = Just "urbit"
63 , searchResults = []
83 , searchResults = []
64 , resultTimelines = Dict.empty
84 , resultTimelines = Dict.empty
86+ , selectedEngines = Set.empty
66 , Cmd.batch
88 , Cmd.batch
67 [ Ur.logIn url "lidlut-tabwed-pillex-ridrup"
89 [ Ur.logIn url "lidlut-tabwed-pillex-ridrup"
@@ -83,13 +105,19 @@ main =
83 , path = [ "search", query ]
105 , path = [ "search", query ]
84 , ship = ship_
106 , ship = ship_
85 , deconstructor =
107 , deconstructor =
109+ (D.list (D.cell D.cord deconstructSearchEngineState))
86− D.list (D.cell D.cord D.cord)
110+ (D.list (D.cell (D.list D.cord) (D.cell D.cord D.cord)))
112+ (\( engines, results ) ->
88− (List.map
114+ (List.map
89− (\( title, link ) ->
115+ (\( resultEngines, ( title, link ) ) ->
90− { title = title, link = link }
116+ { engines = Set.fromList resultEngines, title = title, link = link }
92− >> UpdateSearchResults
120+ engines
@@ -100,6 +128,42 @@ main =
131+debug : (String -> String) -> D.Deconstructor a -> D.Deconstructor a
140+ log (prettyNoun noun)
145+prettyNoun : Noun -> String
157+ Cell ( lhs, rhs ) ->
159+ go False lhs ++ " " ++ go True rhs
162+ "[" ++ go False lhs ++ " " ++ go True rhs ++ "]"
103view : Model -> Element Msg
167view : Model -> Element Msg
104view model =
168view model =
105 case model.initiatedSearch of
169 case model.initiatedSearch of
@@ -110,10 +174,65 @@ view model =
112 Just query ->
176 Just query ->
113− column [ width fill, padding 8, spacing 16 ]
177+ column [ width fill, spacing 16 ]
178+ [ row [ spacing 16, width fill ]
114− [ searchView model
179+ [ searchView model |> el [ padding 8 ]
181+ |> List.filter (\( _, state ) -> state /= Failed)
183+ (\( name, state ) ->
189+ ++ (if Set.isEmpty model.selectedEngines then
192+ else if Set.member name model.selectedEngines then
196+ [ Font.color (rgb 0.8 0.8 0.8)
201+ [ Html.Attributes.class "bobbing" |> htmlAttribute
202+ , Font.color (rgb 0.8 0.8 0.8)
207+ [ Font.color (rgb 0.6 0.6 0.6)
210+ , Events.onClick (ToggleSearchEngine name)
115− , Keyed.column [ width fill ]
226+ , Keyed.column [ width fill, padding 8 ]
116 (model.searchResults
227 (model.searchResults
230+ if Set.isEmpty model.selectedEngines then
234+ Set.intersect engines model.selectedEngines |> Set.isEmpty |> not
117 |> List.map
236 |> List.map
118 (\{ title, link } ->
237 (\{ title, link } ->
@@ -172,7 +291,7 @@ view model =
172searchView : { a | search : String } -> Element Msg
291searchView : { a | search : String } -> Element Msg
173searchView model =
292searchView model =
174 row [ spacing 8 ]
293 row [ spacing 8 ]
175− [ Input.text []
294+ [ Input.text [ width (px 250) ]
176 { onChange = UpdateSearch
295 { onChange = UpdateSearch
177 , placeholder = Nothing
296 , placeholder = Nothing
178 , text = model.search
297 , text = model.search
@@ -214,7 +333,7 @@ update msg model =
214 Search query ->
333 Search query ->
215 ( { model | initiatedSearch = Just query }, Ur.Cmd.none )
334 ( { model | initiatedSearch = Just query }, Ur.Cmd.none )
217− UpdateSearchResults results ->
336+ UpdateSearchResults results engines ->
220 List.Extra.zip (List.range 0 (List.length results - 1)) results
339 List.Extra.zip (List.range 0 (List.length results - 1)) results
@@ -231,7 +350,13 @@ update msg model =
232 |> Dict.fromList
351 |> Dict.fromList
354+ | resultTimelines = foo
234− ( { model | resultTimelines = foo, searchResults = results }, Ur.Cmd.none )
355+ , searchResults = results
236 OpenResult url_ ->
361 OpenResult url_ ->
237 ( model, Ur.Cmd.none )
362 ( model, Ur.Cmd.none )
@@ -239,6 +364,13 @@ update msg model =
239 Tick time ->
364 Tick time ->
240 ( Animator.update time (animator model) model, Ur.Cmd.none )
365 ( Animator.update time (animator model) model, Ur.Cmd.none )
367+ ToggleSearchEngine name ->
368+ if Set.member name model.selectedEngines then
369+ ( { model | selectedEngines = Set.remove name model.selectedEngines }, Ur.Cmd.none )
372+ ( { model | selectedEngines = Set.insert name model.selectedEngines }, Ur.Cmd.none )
243animator : Model -> Animator.Animator Model
375animator : Model -> Animator.Animator Model
244animator model =
376animator model =
@@ -273,6 +405,11 @@ result f g res =
408+listsHaveElementsInCommon : List comparable -> List comparable -> Bool
409+listsHaveElementsInCommon a b =
410+ Set.intersect (Set.fromList a) (Set.fromList b) |> Set.isEmpty |> not
276port createEventSource : String -> Cmd msg
413port createEventSource : String -> Cmd msg