on master help sign in
Show search sources with filtering
4841a52iko
dev/elm-live.sh+1−1
@@ -4,4 +4,4 @@ set -e
4
4
5cd "$(dirname "$0")/../frontend"
5cd "$(dirname "$0")/../frontend"
6
6
7−nix run nixpkgs#elmPackages.elm-live -- src/Main.elm --start-page=index.html -- --output=elm.js "$@"
7+nix run nixpkgs#elmPackages.elm-live -- src/Main.elm --start-page=index.html -- --debug --output=elm.js "$@"
frontend/index.html+16
@@ -6,6 +6,22 @@
6 <style>
6 <style>
7 body {
7 body {
8 font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
8 font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
9+ margin: 0;
10+ }
11+ @keyframes bobbing {
12+ from {
13+ transform: translateY(-30px);
14+ }
15+ to {
16+ transform: traslateY(0);
17+ }
18+ }
19+ .bobbing {
20+ animation-duration: 2s;
21+ animation-name: bobbing;
22+ animation-iteration-count: infinite;
23+ animation-direction: alternate;
24+ animation-timing-function: ease-in-out;
9 }
25 }
10 </style>
26 </style>
11 </head>
27 </head>
frontend/src/Main.elm+155−18
@@ -1,26 +1,27 @@
1port module Main exposing (main)
1port module Main exposing (main)
2
2
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
14−import Html.Events
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)
19import Ur
18import Ur
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(..))
24
25
25
26
26url : String
27url : String
@@ -28,18 +29,36 @@ url =
28 "http://localhost:8080"
29 "http://localhost:8080"
29
30
30
31
32+type SearchEngineState
33+ = Loading
34+ | Failed
35+ | Completed
36+
37+
38+deconstructSearchEngineState : D.Deconstructor SearchEngineState
39+deconstructSearchEngineState =
40+ D.oneOf
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)
44+ ]
45+
46+
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
37 }
55 }
38
56
39
57
40type alias SearchResult =
58type alias SearchResult =
41 { title : String
59 { title : String
42 , link : String
60 , link : String
61+ , engines : Set String
43 }
62 }
44
63
45
64
@@ -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
54
74
55
75
56main : Ur.Run.Program Model Msg
76main : Ur.Run.Program Model Msg
@@ -59,9 +79,11 @@ main =
59 { init =
79 { init =
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
85+ , engines = []
86+ , selectedEngines = Set.empty
65 }
87 }
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 =
108+ D.cell
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)))
87 |> D.map
111 |> D.map
112+ (\( engines, results ) ->
113+ UpdateSearchResults
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 }
117+ )
118+ results
91− )
119+ )
92− >> UpdateSearchResults
120+ engines
93 )
121 )
94 }
122 }
95
123
@@ -100,6 +128,42 @@ main =
100 }
128 }
101
129
102
130
131+debug : (String -> String) -> D.Deconstructor a -> D.Deconstructor a
132+debug log f noun =
133+ case f noun of
134+ Just x ->
135+ Just x
136+
137+ Nothing ->
138+ let
139+ _ =
140+ log (prettyNoun noun)
141+ in
142+ Nothing
143+
144+
145+prettyNoun : Noun -> String
146+prettyNoun noun =
147+ let
148+ go isRhs n =
149+ case n of
150+ Atom a ->
151+ if isSig a then
152+ "~"
153+
154+ else
155+ "@"
156+
157+ Cell ( lhs, rhs ) ->
158+ if isRhs then
159+ go False lhs ++ " " ++ go True rhs
160+
161+ else
162+ "[" ++ go False lhs ++ " " ++ go True rhs ++ "]"
163+ in
164+ go False noun
165+
166+
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 =
110 ]
174 ]
111
175
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 ]
180+ , model.engines
181+ |> List.filter (\( _, state ) -> state /= Failed)
182+ |> List.map
183+ (\( name, state ) ->
184+ text ("%" ++ name)
185+ |> el
186+ ([ Font.size 43
187+ , Font.bold
188+ ]
189+ ++ (if Set.isEmpty model.selectedEngines then
190+ []
191+
192+ else if Set.member name model.selectedEngines then
193+ []
194+
195+ else
196+ [ Font.color (rgb 0.8 0.8 0.8)
197+ ]
198+ )
199+ ++ (case state of
200+ Loading ->
201+ [ Html.Attributes.class "bobbing" |> htmlAttribute
202+ , Font.color (rgb 0.8 0.8 0.8)
203+ ]
204+
205+ Completed ->
206+ [ mouseOver
207+ [ Font.color (rgb 0.6 0.6 0.6)
208+ ]
209+ , pointer
210+ , Events.onClick (ToggleSearchEngine name)
211+ ]
212+
213+ Failed ->
214+ []
215+ )
216+ )
217+ )
218+ |> row
219+ [ spacing 16
220+ , clipX
221+ , scrollbarX
222+ , width fill
223+ , height (px 62)
224+ ]
225+ ]
115− , Keyed.column [ width fill ]
226+ , Keyed.column [ width fill, padding 8 ]
116 (model.searchResults
227 (model.searchResults
228+ |> List.filter
229+ (\{ engines } ->
230+ if Set.isEmpty model.selectedEngines then
231+ True
232+
233+ else
234+ Set.intersect engines model.selectedEngines |> Set.isEmpty |> not
235+ )
117 |> List.map
236 |> List.map
118 (\{ title, link } ->
237 (\{ title, link } ->
119 ( link
238 ( 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 )
216
335
217− UpdateSearchResults results ->
336+ UpdateSearchResults results engines ->
218 let
337 let
219 foo =
338 foo =
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 =
231 )
350 )
232 |> Dict.fromList
351 |> Dict.fromList
233 in
352 in
353+ ( { model
354+ | resultTimelines = foo
234− ( { model | resultTimelines = foo, searchResults = results }, Ur.Cmd.none )
355+ , searchResults = results
356+ , engines = engines
357+ }
358+ , Ur.Cmd.none
359+ )
235
360
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 )
241
366
367+ ToggleSearchEngine name ->
368+ if Set.member name model.selectedEngines then
369+ ( { model | selectedEngines = Set.remove name model.selectedEngines }, Ur.Cmd.none )
370+
371+ else
372+ ( { model | selectedEngines = Set.insert name model.selectedEngines }, Ur.Cmd.none )
373+
242
374
243animator : Model -> Animator.Animator Model
375animator : Model -> Animator.Animator Model
244animator model =
376animator model =
@@ -273,6 +405,11 @@ result f g res =
273 f a
405 f a
274
406
275
407
408+listsHaveElementsInCommon : List comparable -> List comparable -> Bool
409+listsHaveElementsInCommon a b =
410+ Set.intersect (Set.fromList a) (Set.fromList b) |> Set.isEmpty |> not
411+
412+
276port createEventSource : String -> Cmd msg
413port createEventSource : String -> Cmd msg
277
414
278
415
zod/seax/app/seax.hoon+16−2
@@ -42,15 +42,29 @@ $:
42--
42--
43
43
44=/ state *state-0
44=/ state *state-0
45+=* current-engines
46+ |= =query
47+ ^- (list [term ?(%loading %failed %completed)])
48+ %+ turn ~(tap in ~(key by engines))
49+ |= engine=term
50+ :- engine
51+ =/ results
52+ (~(get by search-results:(~(got by search-subscriptions.state) query)) engine)
53+ ?~ results %loading
54+ ?~ +.results %failed
55+ %completed
45=* search-results
56=* search-results
46 |= =query
57 |= =query
47 %- rank-results
58 %- rank-results
48 =/ search-state=search-state
59 =/ search-state=search-state
49 (~(got by search-subscriptions.state) query)
60 (~(got by search-subscriptions.state) query)
50 search-results.search-state
61 search-results.search-state
62+=* sink-for-query
63+ |= =query
64+ [(current-engines query) (search-results query)]
51=* sink
65=* sink
52 |= =query
66 |= =query
53− ((sink:^sink ~[~[%search query]]) (search-results query))
67+ ((sink:^sink ~[~[%search query]]) (sink-for-query query))
54
68
55^- agent:gall
69^- agent:gall
56%- agent:dbug
70%- agent:dbug
@@ -130,7 +144,7 @@ $:
130 (~(put by search-results.search-subscription) engine-name results)
144 (~(put by search-results.search-subscription) engine-name results)
131 =. search-subscriptions.state
145 =. search-subscriptions.state
132 (~(put by search-subscriptions.state) query search-subscription)
146 (~(put by search-subscriptions.state) query search-subscription)
133− =^ card sink (sync:sink (search-results query))
147+ =^ card sink (sync:sink (sink-for-query query))
134 [~[card] this]
148 [~[card] this]
135 ==
149 ==
136++ on-fail |=([term tang] `..on-init)
150++ on-fail |=([term tang] `..on-init)
zod/seax/lib/rank.hoon+4−4
@@ -3,7 +3,7 @@
3
3
4=<
4=<
5|= results=(list [engine=term results=(list search-result)])
5|= results=(list [engine=term results=(list search-result)])
6−^- (list search-result)
6+^- (list [engines=(list term) =search-result])
7=/ url-to-engine-map
7=/ url-to-engine-map
8 ^- (list (map @t [engines=(list term) ords=(list @u) result=search-result]))
8 ^- (list (map @t [engines=(list term) ords=(list @u) result=search-result]))
9 %- zing
9 %- zing
@@ -29,7 +29,7 @@
29 ==
29 ==
30 [(weld engines-lhs engines-rhs) (weld ords-lhs ords-rhs) result]
30 [(weld engines-lhs engines-rhs) (weld ords-lhs ords-rhs) result]
31=/ ranking
31=/ ranking
32− ^- (list [ord=@rs result=search-result])
32+ ^- (list [ord=@rs result=[engines=(list term) =search-result]])
33 %+ turn ~(val by url-to-engines-map)
33 %+ turn ~(val by url-to-engines-map)
34 |= [engines=(list term) ords=(list @u) result=search-result]
34 |= [engines=(list term) ords=(list @u) result=search-result]
35 =/ engine-weights
35 =/ engine-weights
@@ -52,13 +52,13 @@
52 =/ rank
52 =/ rank
53 ^- @rs
53 ^- @rs
54 (mul:rs engine-weights position-weights)
54 (mul:rs engine-weights position-weights)
55− [ord=rank result=result]
55+ [ord=rank result=[engines result]]
56=/ sorted-results
56=/ sorted-results
57 %+ sort ranking
57 %+ sort ranking
58 |= [[lhs=@rs *] [rhs=@rs *]]
58 |= [[lhs=@rs *] [rhs=@rs *]]
59 %+ gth lhs rhs
59 %+ gth lhs rhs
60%+ turn sorted-results
60%+ turn sorted-results
61−|= [* result=search-result]
61+|= [* result=[(list term) search-result]]
62result
62result
63
63
64|%
64|%