on 4841a52 help sign in
.gitignoredeleted−12
since splitsnapshots
unifiedsplit
@@ -1,12 +0,0 @@
1−frontend/elm-stuff
2−frontend/elm.js
3−zod/base
4−zod/.bin
5−zod/.http.ports
6−zod/.run
7−zod/.urb
8−zod/.vere.lock
9−result
10−result-*
11−frontend/assets/fonts
12−frontend/airlock
INSTRUCTIONS.mddeleted−150
@@ -1,150 +0,0 @@
1−# Installation Instructions
2−
3−You need [Nix](https://nixos.org/download/) with flakes enabled to build the desk, and a ship on Urbit OS 408k to run it.
4−If flakes aren't enabled, add `--extra-experimental-features 'nix-command flakes'` after `nix` in each command.
5−The flake builds on x86_64-linux, aarch64-linux and aarch64-darwin; nixpkgs no longer supports Intel Macs.
6−On an Intel Mac, build the desk on one of those systems and copy the files in `result` over, e.g. with `scp -r` or `cp -RL` (a plain `cp -R` copies only the `result` symlink).
7−
8−## On a fake ship
9−
10−To try %seax out, run it on a fake ~zod. This also needs the Urbit runtime (Vere) 4.4 or newer, or Nix to run it. These steps were tested with Vere 4.6.
11−
12−1. Clone the **seax** project and build the desk. The build ends up in `result`.
13−
14−```bash
15−$ git clone https://github.com/iko-soy/seax
16−$ nix build ./seax
17−```
18−
19−2. Download the v4.6 pill (Urbit OS 408k) and start your fake zod using the pill.
20−
21−```bash
22−$ curl -LO https://bootstrap.urbit.org/urbit-v4.6.pill
23−$ urbit -B urbit-v4.6.pill -F zod --http-port 8080
24−```
25−
26−Without Vere installed, you can run the one from the nixpkgs that the flake pins instead:
27−
28−```bash
29−$ nix run --inputs-from ./seax nixpkgs#urbit -- -B urbit-v4.6.pill -F zod --http-port 8080
30−```
31−
32−On an Intel Mac, download [Vere's macOS x86_64 build](https://bootstrap.urbit.org/vere/live/v4.6/vere-v4.6-macos-x86_64), make it executable with `chmod +x vere-v4.6-macos-x86_64`, and run `./vere-v4.6-macos-x86_64` in place of `urbit`.
33−
34−`--http-port 8080` puts the web interface on port 8080. Without it, Vere tries port 80 first, which works on macOS.
35−If port 8080 is taken, Vere stops; choose another port, and use it in step 6.
36−
37−Booting takes about a minute.
38−The fake ship can't download apps such as Landscape, so it prints errors like `http: fail (..., 504)` and `docket: thread failed`; they don't affect %seax.
39−
40−3. Now, inside dojo run the following:
41−```bash
42−dojo> |new-desk %seax
43−dojo> |mount %seax
44−```
45−
46−4. Back in your shell, in the directory where you ran step 1, replace the mounted desk with the build.
47−Don't do this inside `seax`: its own `zod/seax` holds the desk's source.
48−```bash
49−$ rm -r zod/seax/*
50−$ cp -r result/* zod/seax/
51−$ chmod -R u+w zod/seax
52−```
53−
54−The files Nix builds are read-only, and `chmod` makes the copies writable, so that the next update can replace them.
55−
56−5. In the dojo install the seax agent.
57−```bash
58−dojo> |commit %seax
59−dojo> |install our %seax
60−```
61−
62−6. Go to `http://localhost:8080/apps/seax/` in your browser.
63−Log in with the code that `+code` prints in the dojo.
64−If the page only says "bad session auth", your browser still has a cookie from an earlier fake ship; reload the page.
65−
66−To update the desk later, run `git -C seax pull` and `nix build ./seax`, repeat step 4 and run `|commit %seax`.
67−
68−## On your own ship
69−
70−1. Build the desk as in step 1 above.
71−
72−2. In your ship's dojo, create the desk and mount it. `|mount` puts its files in your pier, at `<pier>/seax`.
73−```bash
74−dojo> |new-desk %seax
75−dojo> |mount %seax
76−```
77−
78−3. Replace the mounted desk with the build:
79−```bash
80−$ rm -r <pier>/seax/*
81−$ cp -r result/* <pier>/seax/
82−$ chmod -R u+w <pier>/seax
83−```
84−
85−4. Install it:
86−```bash
87−dojo> |commit %seax
88−dojo> |install our %seax
89−```
90−
91−5. Open `/apps/seax/` at your ship's web address, or click the %seax tile in Landscape.
92−
93−To update it later, run `git -C seax pull` and `nix build ./seax`, then repeat steps 3 and 4.
94−
95−### Upgrading from the 2023 version
96−
97−The 2023 desk was made for Urbit OS 413k, so it was suspended before your ship could update to 408k.
98−Upgrade it with the steps above, without `|new-desk`: run `|mount %seax` if the desk isn't mounted, then replace its files and run `|commit %seax` and `|install our %seax`.
99−
100−The new agent drops the old searches and closes their subscriptions.
101−It also drops the copy of your Tlon notes, if the old version made one, and the list of peers, and calls off the pings that are still waiting for an answer; see [Peers (disabled)](README.md#peers-disabled).
102−
103−Because the desk dates from before Urbit OS 410k, your ship counts it as essential: a later Urbit OS update waits until %seax supports it, instead of suspending %seax.
104−To have it suspended like other apps, run:
105−```bash
106−dojo> |essential-desk %seax |
107−```
108−`+vats %seax` shows which it is, under `essential desk`.
109−
110−### Removing it
111−
112−- `|uninstall %seax` stops the agent and keeps its data. `/apps/seax/` then answers with an error.
113−- `|nuke %seax` deletes the agent's data. It asks for confirmation first.
114−- `|unmount %seax` deletes `<pier>/seax`. The desk itself stays in your ship, because no command deletes desks.
115−
116−# Development
117−
118−With %seax installed on a running ship (see above), run this in another terminal:
119−```bash
120−$ ./seax/dev/elm-live.sh
121−```
122−
123−This serves the frontend at `http://localhost:8000`, with Elm's debugger, and rebuilds it when the code changes.
124−Requests to `/~/` are passed on to the ship at `http://localhost:8080`; set `SHIP_URL` if your ship is elsewhere.
125−Log in at `http://localhost:8000/~/login` first (reload on "bad session auth"), then open `http://localhost:8000/`.
126−
127−With Nix, the script gets Elm, elm-live, the web font and a patched [airlock](https://github.com/iko-soy/airlock) by itself.
128−Without Nix, you need `elm` 0.19.2 and `elm-live`, and airlock at commit 7e85737 (as pinned in `flake.lock`) in `frontend/airlock`, with `frontend/airlock.patch` applied:
129−```bash
130−$ git clone https://github.com/iko-soy/airlock seax/frontend/airlock
131−$ git -C seax/frontend/airlock checkout 7e85737
132−$ git -C seax/frontend/airlock apply ../airlock.patch
133−```
134−There is no `frontend/assets/fonts/` then, so the browser uses another font wherever the page would use Roboto.
135−
136−To build the desk without Nix, set up airlock as above and run `elm make src/Main.elm --optimize --output elm.js` in `seax/frontend`.
137−Then, in step 4, copy `seax/zod/seax/*` instead of `result/*`, and copy `index.html`, `elm.js`, `script.js`, `fetch-event-source.js` and `assets/` from `seax/frontend` into the mounted desk's `web/` directory.
138−
139−The Nix build pins the Elm packages in `frontend/elm-srcs.nix` and `frontend/registry.dat`.
140−After changing `frontend/elm.json`, regenerate them:
141−```bash
142−$ cd seax/frontend
143−$ nix develop .. --command sh -c 'elm2nix convert > elm-srcs.nix && elm2nix snapshot'
144−```
145−
146−# Usage Instructions
147−
148−Type a query, then press Enter or click "search": %seax collects results from its search sources.
149−On the results page, click a source to show only its results; click more sources to add theirs, and click a source again to remove it.
150−The README's [Usage](README.md#usage) section has more.
LICENSEdeleted−7
@@ -1,7 +0,0 @@
1−Copyright 2023 Ilia Kostiuchenko and Evangelos Lamprou
2−
3−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:
4−
5−The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6−
7−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.
NOTICEdeleted−39
@@ -1,39 +0,0 @@
1−%seax is under the MIT License in LICENSE, except for these files from other
2−projects:
3−
4−- zod/seax/lib/dbug.hoon, zod/seax/lib/server.hoon and the marks in
5− zod/seax/mar/ other than docket-0.hoon are copied from pkg/base-dev in
6− urbit/urbit 408k-2 (https://github.com/urbit/urbit).
7− MIT License, Copyright (c) 2015 Urbit.
8−
9−- zod/seax/lib/docket.hoon, zod/seax/sur/docket.hoon and
10− zod/seax/mar/docket-0.hoon are copied from pkg/landscape in urbit/urbit
11− 408k-2. MIT License, Copyright (c) 2015 Urbit.
12−
13−- zod/seax/lib/de-html.hoon is adapted from ++de-xml in
14− pkg/arvo/sys/zuse.hoon of urbit/urbit, as of July 2023.
15− MIT License, Copyright (c) 2017 Urbit.
16−
17−- frontend/fetch-event-source.js is the bundle from airlock's example
18− (https://github.com/iko-soy/airlock), a modified build of
19− @microsoft/fetch-event-source (https://github.com/Azure/fetch-event-source).
20− MIT License, Copyright (c) Microsoft Corporation, and
21− MIT License, Copyright 2023 Ilia Kostiuchenko.
22−
23−- The built frontend includes the Roboto font, which is under the SIL Open
24− Font License 1.1. Its license is next to it in web/assets/fonts/.
25−
26−- The built frontend's web/elm.js includes compiled code from airlock
27− (MIT License, Copyright 2023 Ilia Kostiuchenko) and from some of the Elm
28− packages in frontend/elm.json, all of them under the BSD-3-Clause, MIT or
29− Apache-2.0 license. elm.json also lists MPL-2.0 packages by ianmackenzie,
30− such as elm-geometry and elm-3d-scene, but no code from them is compiled
31− in. Each package's license comes with its source.
32−
33−The MIT License, under the copyright lines above:
34−
35−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:
36−
37−The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
38−
39−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.
README.mddeleted−307
@@ -1,307 +0,0 @@
1−<p align="center">
2− <img src="frontend/assets/img/logo.png" alt="%seax" width="510">
3−</p>
4−
5−The [Urbit](https://urbit.org/) meta-search engine.
6−
7−<p align="center">
8− <img src="docs/screenshot.png" alt="%seax showing results for &quot;urbit&quot; from %wiby and %urbit-dev" width="700">
9−</p>
10−
11−## About
12−
13−It's time Urbit got some searching capabilities.
14−
15−For most people, their computer is a bootloader to the closest search engine. Searching is a central part of everyone's computing workflow.
16−
17−The `%seax` meta-search engine is able to aggregate results from open search APIs and bring them inside the comfort of your Urbit ship. No longer do you have to keep a second tab open.
18−
19−Let's make searching fun again. :)
20−
21−## Contents
22−
23−- [Features](#features)
24−- [Search sources](#search-sources)
25−- [Requirements](#requirements)
26−- [Installation](#installation)
27−- [Usage](#usage)
28−- [How it works](#how-it-works)
29−- [Ranking](#ranking)
30−- [Privacy and network traffic](#privacy-and-network-traffic)
31−- [Peers (disabled)](#peers-disabled)
32−- [Debugging](#debugging)
33−- [Adding a search source](#adding-a-search-source)
34−- [Development](#development)
35−- [Project layout](#project-layout)
36−- [Known issues](#known-issues)
37−- [History](#history)
38−- [Credits](#credits)
39−- [License](#license)
40−
41−## Features
42−
43−- One search box for several search engines. Your ship sends the query to all of them and shows their results on one page.
44−- Results appear as each source answers, without waiting for the slowest.
45−- A page that more than one source returns is shown once and ranked higher.
46−- Click a source's name to see only its results.
47−- Runs on your own ship at `/apps/seax/`, with a tile in Landscape. Only you can search: visitors who aren't logged in are sent to the login page.
48−- Nothing runs in the background: %seax sends requests only when you search.
49−
50−## Search sources
51−
52−| Source | Shown as | What it is | Request your ship sends | Status (October 2026) |
53−|---|---|---|---|---|
54−| [wiby](https://wiby.me/) | `%wiby` | "Search Engine for the Classic Web", an index of simple, older-style pages | `GET https://wiby.me/json/?q=<query>`, wiby's public JSON API | Works |
55−| [Urbit docs](https://docs.urbit.org/) | `%urbit-dev` | The site search of docs.urbit.org, which is built on GitBook | `POST https://docs.urbit.org/~gitbook/search` with `{"query": "<query>", "scope": {"mode": "all"}}` | Works |
56−| [CrowdView](https://crowdview.ai/) | `%crowdview` | A search engine for forum discussions (Reddit, Hacker News, Stack Exchange and others) | `GET https://crowdview-next-js.onrender.com/api/search-v3?query=<query>`, the endpoint crowdview.ai's own page uses | Works, but has no results for many queries, such as "urbit" |
57−
58−The query is URL-encoded in the GET requests, and sent as a JSON string to the Urbit docs, whose request also has a `content-type: application/json` header. Your ship adds no user agent, cookies or other headers: Vere sends only `Host`, and `Content-Length` when there's a body.
59−
60−The page's sources row shows each source that is still loading or found something. A source that fails, or has no results, is hidden.
61−
62−Until October 2026, %seax also searched [Yep](https://yep.com/). By then Yep blocked requests that don't come from a browser, so it returned no results, but every search still sent it your query. Its official API needs an API key and is paid after 1,000 requests.
63−
64−## Requirements
65−
66−- A ship running Urbit OS 408k, which needs Vere 4.4 or newer (4.6 is current). The desk declares `[%zuse 408]` in `zod/seax/sys.kelvin`. When your ship moves to a newer Urbit OS, it suspends %seax until you install a version of the desk that declares the new one. A desk upgraded from the 2023 version is the exception; see [Installation](#installation).
67−- To build the desk: [Nix](https://nixos.org/download/) with flakes enabled, on x86_64-linux, aarch64-linux or aarch64-darwin. Nix fetches Elm and the other build tools already built from cache.nixos.org, so the build only compiles the frontend and copies files.
68−
69−## Installation
70−
71−[INSTRUCTIONS.md](INSTRUCTIONS.md) has the full steps: on a fake ship to try %seax out, on your own ship, upgrading the 2023 version, and removing %seax. In short:
72−
73−1. Build the desk: `nix build` in a clone of this repository puts it in `result/`.
74−2. In your ship's dojo, create and mount a desk: `|new-desk %seax`, then `|mount %seax`.
75−3. Replace the files of the mounted desk, `<pier>/seax/`, with those in `result/`. Nix's files are read-only, so make the copies writable with `chmod -R u+w <pier>/seax`, or the next update can't replace them.
76−4. In the dojo: `|commit %seax`, then `|install our %seax`.
77−
78−If you installed the 2023 version, which was made for Urbit OS 413k, it was suspended before your ship could update to 408k. Upgrade it the same way, without `|new-desk`. The new agent drops the old searches, the list of peers, and the copy of your Tlon notes if the old version made one. Because the desk dates from before Urbit OS 410k, your ship counts it as essential: a later Urbit OS update waits until %seax supports it, instead of suspending %seax. Run `|essential-desk %seax |` to have it suspended like other apps.
79−
80−To remove %seax, run `|uninstall %seax`, which stops it and keeps its data. `|nuke %seax` then deletes the data.
81−
82−## Usage
83−
84−Open `/apps/seax/` at your ship's web address, e.g. `http://localhost:8080/apps/seax/`, or click the %seax tile in Landscape. If you aren't logged in, you'll be asked for your `+code` first.
85−
86−- Type a query, then press Enter or click **search**.
87−- The **Sources** row names each source that is still loading or has results. Sources that are loading pulse between grey and black, and can't be clicked until they finish.
88−- Click a source to show only its results. Click more sources to add theirs, and click a source again to remove it. With no source selected, all results are shown. The selection stays when you search again.
89−- Each result shows an emoji, the title and the address. The emoji is picked from a hash of the title and is only decoration. Titles are shown without HTML tags, and with HTML entities and `%` escapes decoded.
90−- Click a title to open the result in a new tab. The address under it isn't a link.
91−- While no source has results, the page says "No results found... :)". It also says so while the sources are still loading.
92−- The logo on the start page tilts as you move the mouse over it.
93−- The page's address doesn't change when you search, so you can't bookmark a search. The back button leaves the app, and reloading the page brings back the start page.
94−- The **[About]** link at the bottom leads to this README, and **[Code]** to this repository.
95−
96−## How it works
97−
98−%seax is a Gall agent, [`zod/seax/app/seax.hoon`](zod/seax/app/seax.hoon), and an Elm app, [`frontend/`](frontend). The agent serves the built Elm app from the desk's `web/` directory.
99−
100−```mermaid
101−sequenceDiagram
102− participant B as Browser (Elm app)
103− participant E as Eyre
104− participant S as %seax
105− participant I as Iris
106− participant W as wiby, Urbit docs, CrowdView
107− B->>E: subscribe to /search/<query> on %seax
108− E->>S: watch /search/<query>
109− S-->>B: %flush: every source is loading
110− S->>I: one HTTP request per source
111− I->>W: requests from your ship
112− W-->>I: responses
113− I-->>S: a response
114− S-->>B: %drain: what changed in the sources and ranked results
115−```
116−
117−- **Serving the app.** The agent binds `/apps/seax` in Eyre and serves the files in the desk's `web/` directory to logged-in users. Everyone else is redirected to `/~/login`. The Landscape tile comes from `zod/seax/desk.docket-0`.
118−- **Searching.** The Elm app subscribes to `/search/<query>` on the agent through an Eyre channel, using [airlock](https://github.com/iko-soy/airlock). The agent accepts the subscription only from the ship itself. For a query that isn't open yet, it sends one HTTP request per source through Iris. Every new subscriber gets the current state right away.
119−- **Streaming.** Each time a source answers, the agent parses the answer, ranks all the results so far and sends the change. The first message is the whole state (`%flush`), and later ones are diffs of it (`%drain`), made by [`lib/sink.hoon`](zod/seax/lib/sink.hoon) and [`lib/noun-diff.hoon`](zod/seax/lib/noun-diff.hoon). airlock's `Ur.Sub.sink` applies them in the browser.
120−- **Sharing searches.** While a query has subscribers, its results stay in the agent's state. Another tab that searches for the same query gets them right away, without new requests. Queries have to match exactly, including case and spaces. When the last subscriber of a query leaves, the agent forgets the query. A new search in the same tab leaves the old query, but closing or reloading a tab doesn't: Eyre ends the tab's subscriptions when it closes the tab's channel, 12 hours after the tab disconnects. Results aren't refreshed while a query stays open.
121−- **Failures.** A source fails when its response has no body, or the body isn't the JSON the source's parser expects. The agent doesn't look at the HTTP status. When Vere can't make a request (DNS, connection or TLS errors, or a connection that stalls for 5 minutes), Iris returns status 504 with no body, so that source fails too. Iris follows redirects with status 301, 303 and 307; other redirects make the source fail. Failed requests aren't retried.
122−
123−## Ranking
124−
125−Each source has a weight in [`zod/seax/lib/engines.hoon`](zod/seax/lib/engines.hoon), currently `.2` (2.0) for all of them. [`zod/seax/lib/rank.hoon`](zod/seax/lib/rank.hoon) merges the results that have exactly the same URL. Each URL gets a score, which is the product of three numbers:
126−
127−- the weights of the sources that returned it, multiplied together,
128−- the number of times it was returned,
129−- the sum of 1/position for each time, counting positions from 1.
130−
131−Results are shown with the highest score first. For a URL that only one source returns, the score is the source's weight divided by the position: a first result scores 2, a second one 1, a third one 0.67. Since the weights are all the same, the first results of all sources come first, then their second results, and so on. Results with the same score come in no particular order.
132−
133−URLs that differ only by a trailing slash or by `http`/`https` count as different URLs. A merged result keeps the title from one of its sources. See [Known issues](#known-issues) for a bug in the scores of merged results.
134−
135−## Privacy and network traffic
136−
137−- **Searches.** Your query goes from your ship to wiby.me, docs.urbit.org and crowdview-next-js.onrender.com, over HTTPS. Those services see the query and your ship's IP address.
138−- **Nothing else.** %seax makes requests only when you search for a query that isn't already open. It has no timers, and it doesn't contact other ships, except once: an upgrade from an older version calls off that version's pings to its peers that are still waiting for an answer. See [Peers (disabled)](#peers-disabled).
139−- **The page.** It loads nothing from other sites: the fonts, scripts and images all come from your ship. airlock normally pokes ~zod when it opens a channel, and [`frontend/airlock.patch`](frontend/airlock.patch) removes that poke. The bug is reported at [iko-soy/airlock#15](https://github.com/iko-soy/airlock/issues/15). Eyre's login page, which comes with Urbit, loads its fonts from rsms.me and storage.googleapis.com.
140−- **Stored data.** Search results stay in the agent's state while their query is open, which can be up to 12 hours after you close its tab. As with everything your ship does, the requests and responses are also recorded in your pier's event log. The agent also prints your queries and their results in the dojo.
141−- **Other ships.** Only you can search. %seax refuses other ships' pokes and searches, so they can't use it through your ship.
142−
143−## Peers (disabled)
144−
145−From August 2023 until this version, %seax kept a list of peers: other ships that might run %seax. The list started with ~zod and ~racfer-hattes. Every 30 seconds, %seax pinged its peers by subscribing to `/ping` on their %seax, and told them which peers had answered, so that ships running %seax could find each other.
146−
147−Nothing ever used the list, and neither the code nor the commit messages say what it was for. The 2023 code didn't work well either: a peer was dropped after missing one ping, every other check emptied the list, and from October 2023 ships refused the peer lists that other ships sent them, because `+on-poke` couldn't read pokes from other ships.
148−
149−This version disables the peer network:
150−
151−- It doesn't ping other ships or send them anything, and it has no timers.
152−- An upgrade drops the list and calls off the pings that are still waiting for an answer.
153−- It refuses pings and peer lists from other ships.
154−
155−## Debugging
156−
157−%seax prints a lot to the dojo:
158−
159−- the wire of every response it gets from Arvo, e.g. `/search-result/<query>/<source>` for each HTTP response,
160−- each source's parsed results, followed by the state of every source,
161−- the path of every search that closes,
162−- the stack trace of every crash while it handles a response, such as the late answers in [Known issues](#known-issues).
163−
164−A few dojo commands help:
165−
166−- `:seax +dbug` prints the agent's state: each open query, with its number of subscribers and the results of each source. A source that is still loading has no entry, `~` means that it failed, and `[~ ~]` that it found nothing.
167−- `:seax +dbug [%incoming ~]` lists the subscriptions to the agent, i.e. open searches.
168−- `:seax +dbug [%outgoing ~]` lists the agent's own subscriptions. It should print "no matching subscriptions", also right after an upgrade from an older version.
169−- `:seax +dbug [%state '~(key by search-subscriptions)']` evaluates Hoon against the state, here listing the open queries.
170−- `+vats %seax` shows whether the desk is running or suspended, e.g. after an Urbit OS update, and which Urbit OS version it declares in `/sys/kelvin`.
171−
172−When Vere can't make a request, it prints `http: fail (<n>, 504): <reason>`, e.g. `unknown node or service` when a DNS lookup fails, and that source counts as failed.
173−
174−## Adding a search source
175−
176−1. Create `zod/seax/lib/engines/<name>.hoon`. It holds an `engine` core: `++request` builds the HTTP request for a query, and `++results` parses the response into a list of `[title url]`, or `~` if it can't. `++get` in [`zod/seax/sur/seax.hoon`](zod/seax/sur/seax.hoon) makes a GET request with the query appended to a URL. It escapes the query twice, because Vere unescapes the URL once before sending it. For example, for an API that returns `[{"title": ..., "url": ...}, ...]`:
177−
178− ```hoon
179− /- *seax
180− ^- engine
181− =<
182− |%
183− ++ request
184− |= query=tape
185− ^- request:http
186− (get "https://example.com/search?q=" query)
187− ++ results
188− |= response=mime-data:iris
189− ^- (unit (list search-result))
190− ;< jon=json _biff (de:json:html q.data.response)
191− (apex jon)
192− --
193− |%
194− ++ apex
195− =, dejs-soft:format
196− (ar (ot ~[title+so url+so]))
197− --
198− ```
199−
200−2. In [`zod/seax/lib/engines.hoon`](zod/seax/lib/engines.hoon), import it (`/+ engines-<name>`) and add `[name=%<name> engine=engines-<name> weight=.2]` to the list. The weight is a floating-point literal: `.2` is 2.0.
201−3. Build and commit the desk again. Nix only sees files that git tracks, so `git add` the new file before building. The Elm app shows every source the agent has, so it doesn't need any change.
202−
203−A source counts as failed if `++results` returns `~` or an empty list. Note that `ar` from `dejs-soft` gives `~` if even one result doesn't parse.
204−
205−## Development
206−
207−[INSTRUCTIONS.md](INSTRUCTIONS.md#development) explains how to work on the frontend. `dev/elm-live.sh` serves it at `http://localhost:8000` with Elm's debugger, rebuilds it when the code changes, and passes requests for `/~/` on to a running ship.
208−
209−The flake has these outputs:
210−
211−- `packages.<system>.desk`, the default: the desk, with the built frontend in `web/`.
212−- `packages.<system>.frontend`: the built frontend.
213−- `packages.<system>.fonts`: the Roboto Light web font, converted to WOFF2 from nixpkgs' Roboto, and its license.
214−- `packages.<system>.airlock-src`: airlock at the commit pinned in `flake.lock`, with `frontend/airlock.patch` applied.
215−- `devShells.<system>.default`: a shell with `elm` 0.19.2, `elm-live` and `elm2nix`.
216−
217−To change the agent, edit `zod/seax`, rebuild the desk and copy it into your mounted desk, as in the installation steps, then `|commit %seax`. The agent reloads with its state. Nix only sees files that git tracks, so `git add` new files before building.
218−
219−## Project layout
220−
221−```
222−README.md, INSTRUCTIONS.md this file, and how to install and develop
223−LICENSE, NOTICE the MIT License, and the files from other projects
224−flake.nix, flake.lock the Nix build: desk, frontend, font and airlock
225−dev/elm-live.sh frontend development server, with a proxy to a ship
226−docs/screenshot.png the screenshot above
227−zod/seax/ the %seax desk
228− app/seax.hoon the agent
229− sur/seax.hoon the search-result and engine types, and ++get
230− lib/engines.hoon the sources and their weights
231− lib/engines/*.hoon one file per source
232− lib/rank.hoon merges and ranks results
233− lib/sink.hoon sends state as a %flush and then %drain diffs
234− lib/noun-diff.hoon diffs of nouns, for lib/sink.hoon
235− lib/dbug.hoon, lib/server.hoon from Urbit's base-dev files
236− lib/docket.hoon, sur/docket.hoon for the Landscape tile
237− lib/de-html.hoon, lib/extract-links.hoon, lib/extract-text.hoon, lib/words.hoon
238− HTML and text helpers from the first, crawler
239− version, which nothing uses now
240− mar/ marks for the desk's files
241− desk.docket-0 the tile: title, description, colour, image, URL,
242− version, website and license
243− desk.bill the agent to start: %seax
244− sys.kelvin [%zuse 408]
245−frontend/ the Elm app
246− src/Main.elm the whole app
247− src/Route.elm, src/Pages/Search.elm unused leftovers of an early version;
248− Pages/Search.elm doesn't compile
249− index.html, script.js the page, and the glue between Elm and the event stream
250− fetch-event-source.js event-stream client, from airlock's example
251− assets/ CSS, the logo and tile images, the logo tilt script
252− elm.json Elm 0.19.2 and its packages; airlock's code comes from airlock/src
253− elm-srcs.nix, registry.dat pinned Elm packages, for the Nix build
254− airlock.patch stops airlock from poking ~zod when it opens a channel
255−```
256−
257−## Known issues
258−
259−The search sources:
260−
261−- **CrowdView often has no results,** and %seax shows a source without results the same way as a failed one: it's hidden.
262−- **One malformed result fails a whole source.** If any result in a source's response doesn't parse, the whole source counts as failed.
263−
264−The agent:
265−
266−- **Scores of merged results are wrong.** `lib/rank.hoon` adds the 1/position terms of a merged URL, and compares scores, as integers instead of floating-point numbers. As a result:
267− - URLs that 2 sources return always come before URLs that only 1 returns. Among themselves, they come roughly in order of the product of their two positions, smallest first, instead of by their scores, and those whose positions multiply to 4 or less tie at the top.
268− - URLs that all 3 sources return come before those.
269− - Single-source results are ranked correctly.
270−- **Late answers crash the agent.** If you search for something else before every source has answered, the late answers to the old query crash the agent's `+on-arvo`, and the dojo prints a stack trace for each one. Nothing else is affected. The agent never cancels its requests.
271−- **Restarting the ship during a search leaves sources loading.** When Vere restarts, Iris cancels the requests that are still open. The agent crashes on each cancellation, and those sources keep loading until the query closes, which can be 12 hours after you close its tab.
272−- **A source that stalls keeps loading** until Vere's HTTP client gives up after 5 minutes.
273−- **Results are never refreshed.** A query that is open in any tab, or in a tab closed less than 12 hours ago, gives the results it already has.
274−
275−The page:
276−
277−- **A `%` in a title can hide it.** Titles are `%`-decoded, and when that fails, as on a lone `%`, the title disappears and the result can't be clicked. Escapes that do decode are decoded even when they are part of the real title.
278−- **Narrow screens are cut off.** The logo and the search box have fixed widths, so on a phone the search box and the search button are cut off, and on the start page the logo too. Titles and addresses never wrap, so long ones are cut off as well. The page can't scroll sideways.
279−- **The source filter stays for the next search.** If the selected sources have no results for the new query, the list is empty without the "No results found" message, and a selected source that is hidden can't be unselected.
280−- **"Come back later" doesn't help.** The no-results message says "We'll try and find you something. Just come back later.", but nothing searches again.
281−- **Searching for the same query again does nothing,** so the page can't refresh its results.
282−- **Searches can't be bookmarked,** because the page's address doesn't change.
283−- **Smaller things:** only the title of a result is a link, although the whole result looks clickable; the logo tilts only on the start page; the page has no favicon; the search box isn't focused when the page opens; and the page logs every event from the ship to the browser console.
284−
285−## History
286−
287−- **July 2023.** iko created the repository on 23 July and started %seax on 27 July, as a web crawler with a reverse word index. On 29 July it switched to searching other search engines, with Yep, CrowdView, wiby and Alexandria as sources, and from 31 July it sent each query to all of them. Streaming results through `%sink` and noun diffs, ranking and the first Elm frontend were in place by 2 August, and filtering by source and animations by 5 August.
288−- **August 2023.** iko added liveness checks between %seax ships on 9 August, and "bad peer discovery" (gossip) on 27 August.
289−- **October 2023.** Evangelos Lamprou added the logo and its tilt effect, improvements to the results page including the emoji, the Urbit developer docs as a source, and the first INSTRUCTIONS.md. He also added a `%get-notes` poke that copied the ship's Tlon notebook notes into the agent's state. The notes were never searched.
290−- **Upstream.** iko also contributed `%sink` and the noun-diff library to urbit/urbit in July 2023 ([#6712](https://github.com/urbit/urbit/pull/6712), [#6681](https://github.com/urbit/urbit/pull/6681)), where they live in `pkg/base-dev`.
291−- **2026.** %seax was ported to Urbit OS 408k:
292− - It is built with a Nix flake, served from the ship, and has a Landscape tile.
293− - The docs.urbit.org search replaced the old urbit.org developer search.
294− - Alexandria, whose API is gone, and the Tlon notes were removed.
295− - The peer network, which nothing used, was disabled.
296− - Yep, which blocks requests that don't come from a browser, was dropped.
297−
298−## Credits
299−
300−Created by [~racfer-hattes](https://iko.soy) and [~dopnyr-figbud](https://vagos.lamprou.xyz/).
301−
302−- Results come from [wiby](https://wiby.me/), the [Urbit docs](https://docs.urbit.org/) and [CrowdView](https://crowdview.ai/).
303−- The frontend is written in [Elm](https://elm-lang.org/) with [airlock](https://github.com/iko-soy/airlock), [elm-ui](https://github.com/mdgriffith/elm-ui) and [elm-animator](https://github.com/mdgriffith/elm-animator), and includes the [Roboto](https://github.com/googlefonts/roboto-3-classic) font.
304−
305−## License
306−
307−MIT, see [LICENSE](LICENSE). Files from other projects are listed in [NOTICE](NOTICE).
dev/elm-live.sh+2−19
@@ -1,24 +1,7 @@
1−#!/usr/bin/env bash
1+#!/bin/sh
2−# Serves the frontend at http://localhost:8000 and rebuilds it on changes.
3−# Requests to /~/ are passed on to the ship at $SHIP_URL, so log in at
4−# http://localhost:8000/~/login first. Extra arguments go to `elm make`.
5
2
6set -e
3set -e
7
4
8cd "$(dirname "$0")/../frontend"
5cd "$(dirname "$0")/../frontend"
9
6
10−ship=${SHIP_URL:-http://localhost:8080}
11−args=(src/Main.elm --start-page=index.html --proxy-prefix=/~ "--proxy-host=$ship/~" -- --debug --output=elm.js "$@")
7+nix run nixpkgs#elmPackages.elm-live -- src/Main.elm --start-page=index.html -- --debug --output=elm.js "$@"
12−
13−if command -v nix >/dev/null; then
14− nix build ..#airlock-src --out-link airlock
15− nix build ..#fonts --out-link assets/fonts
16− exec nix develop .. --command elm-live "${args[@]}"
17−elif command -v elm-live >/dev/null; then
18− if [ ! -d airlock ]; then
19− echo "Please clone https://github.com/iko-soy/airlock into frontend/airlock and apply frontend/airlock.patch" && exit 1
20− fi
21− exec elm-live "${args[@]}"
22−else
23− echo "Please install either 'nix' or 'elm-live'" && exit 1
24−fi
docs/screenshot.pngdeleted

Binary file not shown.

flake.lockdeleted−45
@@ -1,45 +0,0 @@
1−{
2− "nodes": {
3− "airlock": {
4− "flake": false,
5− "locked": {
6− "lastModified": 1692952961,
7− "narHash": "sha256-xz4esBLKyzcBp0rNkne26lrhIB993gLRJqxg217d8Yk=",
8− "owner": "iko-soy",
9− "repo": "airlock",
10− "rev": "7e857379f10b362a9ee9e271312a4deb1fe65b66",
11− "type": "github"
12− },
13− "original": {
14− "owner": "iko-soy",
15− "repo": "airlock",
16− "rev": "7e857379f10b362a9ee9e271312a4deb1fe65b66",
17− "type": "github"
18− }
19− },
20− "nixpkgs": {
21− "locked": {
22− "lastModified": 1792210252,
23− "narHash": "sha256-Mh0PUaIv4nFY+8wAGonaEh8iBzwsmdNlwjVHmFg1Biw=",
24− "owner": "NixOS",
25− "repo": "nixpkgs",
26− "rev": "09063bd1f2b36fd9fd793c36dfade7bd40a93dfc",
27− "type": "github"
28− },
29− "original": {
30− "owner": "NixOS",
31− "ref": "nixos-unstable",
32− "repo": "nixpkgs",
33− "type": "github"
34− }
35− },
36− "root": {
37− "inputs": {
38− "airlock": "airlock",
39− "nixpkgs": "nixpkgs"
40− }
41− }
42− },
43− "root": "root",
44− "version": 7
45−}
flake.nixdeleted−114
@@ -1,114 +0,0 @@
1−{
2− description = "seax: an Urbit search agent";
3−
4− inputs = {
5− nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
6− # The Elm library the frontend talks to the ship with.
7− airlock = {
8− url = "github:iko-soy/airlock/7e857379f10b362a9ee9e271312a4deb1fe65b66";
9− flake = false;
10− };
11− };
12−
13− outputs =
14− {
15− self,
16− nixpkgs,
17− airlock,
18− }:
19− let
20− # nixpkgs 26.11 dropped x86_64-darwin.
21− forAllSystems = nixpkgs.lib.genAttrs [
22− "x86_64-linux"
23− "aarch64-linux"
24− "aarch64-darwin"
25− ];
26− in
27− {
28− packages = forAllSystems (
29− system:
30− let
31− pkgs = nixpkgs.legacyPackages.${system};
32− inherit (self.packages.${system}) airlock-src fonts frontend;
33− in
34− {
35− # The %seax desk, ready to be copied into a mounted desk directory
36− # and committed with `|commit %seax`. The agent serves the frontend
37− # from web/ at /apps/seax/.
38− desk = pkgs.runCommand "seax-desk" { } ''
39− cp -r --no-preserve=mode ${./zod/seax} $out
40− cp -r --no-preserve=mode ${frontend} $out/web
41− '';
42−
43− frontend = pkgs.stdenv.mkDerivation {
44− name = "seax-frontend";
45− src = ./frontend;
46− nativeBuildInputs = [ pkgs.elmPackages.elm ];
47− # elm-srcs.nix and registry.dat come from `elm2nix convert` and
48− # `elm2nix snapshot`; regenerate them when elm.json changes.
49− configurePhase =
50− pkgs.elmPackages.fetchElmDeps {
51− elmPackages = import ./frontend/elm-srcs.nix;
52− elmVersion = "0.19.2";
53− registryDat = ./frontend/registry.dat;
54− }
55− + ''
56− # A checkout without .git can still contain dev/elm-live.sh's link.
57− rm -rf airlock
58− ln -s ${airlock-src} airlock
59− '';
60− buildPhase = ''
61− elm make src/Main.elm --optimize --output elm.js
62− '';
63− installPhase = ''
64− mkdir -p $out/assets
65− cp index.html elm.js script.js fetch-event-source.js $out/
66− cp -r assets/css assets/img assets/js $out/assets/
67− cp -r ${fonts} $out/assets/fonts
68− '';
69− };
70−
71− # airlock, patched to not poke ~zod when it opens a channel.
72− airlock-src = pkgs.applyPatches {
73− name = "airlock";
74− src = airlock;
75− patches = [ ./frontend/airlock.patch ];
76− };
77−
78− # The web font used by the frontend, served from assets/fonts/.
79− fonts =
80− let
81− robotoLicense = pkgs.fetchurl {
82− url = "https://raw.githubusercontent.com/googlefonts/roboto-3-classic/v3.016/OFL.txt";
83− hash = "sha256-BhQCMnqWqtsL+2lKlg7SiezTjTg+OWJDgxq4H+sQnEE=";
84− };
85− in
86− pkgs.runCommand "seax-fonts" { nativeBuildInputs = [ pkgs.woff2 ]; } ''
87− mkdir -p $out
88− cp ${pkgs.roboto.src}/web/static/Roboto-Light.ttf roboto-light.ttf
89− woff2_compress roboto-light.ttf
90− cp roboto-light.woff2 $out/roboto-light.woff2
91− cp ${robotoLicense} $out/roboto-license.txt
92− '';
93−
94− default = self.packages.${system}.desk;
95− }
96− );
97−
98− devShells = forAllSystems (
99− system:
100− let
101− pkgs = nixpkgs.legacyPackages.${system};
102− in
103− {
104− default = pkgs.mkShell {
105− packages = [
106− pkgs.elmPackages.elm
107− pkgs.elmPackages.elm-live
108− pkgs.elm2nix
109− ];
110− };
111− }
112− );
113− };
114−}
frontend/airlock.patchdeleted−16
@@ -1,16 +0,0 @@
1−diff --git a/src/Ur/Run.elm b/src/Ur/Run.elm
2−index 4802e86..fe67d11 100644
3−--- a/src/Ur/Run.elm
4−+++ b/src/Ur/Run.elm
5−@@ -312,8 +312,10 @@ update inp msg model =
6−
7− NeedsActivation ->
8− let
9−+ -- Any PUT creates the channel. An ack of nothing does so
10−+ -- without poking ~zod (`ship = ""`) over the network.
11− ( eventId, reqs ) =
12−- [ Poke { ship = "", agent = "hood", mark = "helm-hi", noun = C.cord "Opening airlock!" } ]
13−+ [ Ack 0 ]
14− |> tag model.eventId
15− in
16− ( { model | eventId = eventId }
frontend/assets/css/fonts.cssdeleted−10
@@ -1,10 +0,0 @@
1−/* The font file is taken from nixpkgs at build time (`nix build .#fonts`),
2− see flake.nix. Its license is next to it in ../fonts/. */
3−
4−@font-face {
5− font-family: 'Roboto';
6− font-style: normal;
7− font-weight: 300;
8− font-display: swap;
9− src: url('../fonts/roboto-light.woff2') format('woff2');
10−}
frontend/assets/css/main.cssdeleted−63
@@ -1,63 +0,0 @@
1−@import url('fonts.css');
2−
3−.button {
4− border: none;
5− color: white;
6− padding: 15px 32px;
7− text-align: center;
8− text-decoration: none;
9− font-size: 16px;
10− display: inline-block;
11− margin: 4px 2px;
12− cursor: pointer;
13− border-radius: 0;
14− box-shadow: 0 2px 2px #888888;
15−}
16−
17−.shadow {
18− shadow: 0 2px 2px #888888;
19−}
20−
21−.result {
22−}
23−
24−.result:hover {
25− text-decoration: underline;
26−}
27−
28−.search:hover {
29− text-decoration: underline;
30−}
31−
32−.wavy {
33− animation-name: wavy;
34− animation-duration: 4.2s;
35− animation-timing-function: ease-in-out;
36− animation-iteration-count: infinite;
37− position: relative;
38− top: 0;
39− left: 0;
40−}
41−@keyframes wavy {
42− 0% {
43− top: 0px;
44− }
45− 50% {
46− top: -15px;
47− }
48− 100% {
49− top: 0px;
50− }
51−}
52−
53−.result-link {
54− color: #000;
55− text-decoration: none;
56− font-size: 12px !important;
57−}
58−
59−.result-title {
60− color: #000;
61− text-decoration: none;
62− font-size: 16px;
63−}
frontend/assets/img/logo.pngdeleted

Binary file not shown.

frontend/assets/img/tile.pngdeleted

Binary file not shown.

frontend/assets/js/tilt.jsdeleted−51
@@ -1,51 +0,0 @@
1−/* Store the element in el */
2−let el = document.getElementById('tilt')
3−
4−/* Get the height and width of the element */
5−const height = el.clientHeight
6−const width = el.clientWidth
7−
8−/* Define function a */
9−function handleMove(e) {
10−
11− if (el.id != 'tilt') return; /* TODO: Fix this hack. */
12−
13− /*
14− * Get position of mouse cursor
15− * With respect to the element
16− * On mouseover
17− */
18− /* Store the x position */
19− const xVal = e.layerX
20− /* Store the y position */
21− const yVal = e.layerY
22−
23− /*
24− * Calculate rotation valuee along the Y-axis
25− * Here the multiplier 20 is to
26− * Control the rotation
27− * You can change the value and see the results
28− */
29− const yRotation = 20 * ((xVal - width / 2) / width)
30−
31− /* Calculate the rotation along the X-axis */
32− const xRotation = -20 * ((yVal - height / 2) / height)
33−
34− /* Generate string for CSS transform property */
35− const string = 'perspective(500px) scale(1.0) rotateX(' + xRotation + 'deg) rotateY(' + yRotation + 'deg)'
36−
37− /* Apply the calculated transformation */
38− el.style.transform = string
39−}
40−
41−/* Add listener for mouseout event, remove the rotation */
42−el.addEventListener('mouseout', function() {
43− el.style.transform = 'perspective(500px) scale(1) rotateX(0) rotateY(0)'
44−})
45−
46−/*
47− * Add a listener for mousemove event
48− * Which will trigger function 'handleMove'
49− * On mousemove
50− */
51−let mouseMoveEL = el.addEventListener('mousemove', handleMove)
frontend/elm-srcs.nixdeleted−267
@@ -1,267 +0,0 @@
1−{
2−
3− "Evelios/elm-hash" = {
4− sha256 = "0xiypbcqd8bfv0lzpna6is5a63vf91jy8xa2350asi5y5c5fzrkq";
5− version = "1.0.0";
6− };
7−
8− "TSFoster/elm-bytes-extra" = {
9− sha256 = "1ip5br0kszhr8kmk19jsqqqhgna89drn3cwb6zn8ncwfrxc8xvs9";
10− version = "1.3.0";
11− };
12−
13− "avh4/elm-color" = {
14− sha256 = "0n16wnvp87x9az3m5qjrl6smsg7051m719xn5d244painx8xmpzq";
15− version = "1.0.0";
16− };
17−
18− "cmditch/elm-bigint" = {
19− sha256 = "00wfcvl3kg8l85cyljvgal51qksnqfqkc4g4diljggz6dcl155w2";
20− version = "2.0.1";
21− };
22−
23− "elm-community/list-extra" = {
24− sha256 = "02grd0p5hc2gvdy4n723d1s28pm1grn95jrzic6jcgb26qh16vcc";
25− version = "8.7.0";
26− };
27−
28− "elm-community/maybe-extra" = {
29− sha256 = "185jy9jxx3bqf0xl3rmdxfqqmxzcr084llf32glr6hgl5agshywk";
30− version = "5.3.0";
31− };
32−
33− "elm-community/string-extra" = {
34− sha256 = "014l3lkglaniizwvr5pqbi4z7bb0piq0pp3fdifyd4rdp53ac23f";
35− version = "4.0.1";
36− };
37−
38− "elm/browser" = {
39− sha256 = "0nagb9ajacxbbg985r4k9h0jadqpp0gp84nm94kcgbr5sf8i9x13";
40− version = "1.0.2";
41− };
42−
43− "elm/bytes" = {
44− sha256 = "02ywbf52akvxclpxwj9n04jydajcbsbcbsnjs53yjc5lwck3abwj";
45− version = "1.0.8";
46− };
47−
48− "elm/core" = {
49− sha256 = "19w0iisdd66ywjayyga4kv2p1v9rxzqjaxhckp8ni6n8i0fb2dvf";
50− version = "1.0.5";
51− };
52−
53− "elm/html" = {
54− sha256 = "1n3gpzmpqqdsldys4ipgyl1zacn0kbpc3g4v3hdpiyfjlgh8bf3k";
55− version = "1.0.0";
56− };
57−
58− "elm/http" = {
59− sha256 = "008bs76mnp48b4dw8qwjj4fyvzbxvlrl4xpa2qh1gg2kfwyw56v1";
60− version = "2.0.0";
61− };
62−
63− "elm/json" = {
64− sha256 = "0kjwrz195z84kwywaxhhlnpl3p251qlbm5iz6byd6jky2crmyqyh";
65− version = "1.1.3";
66− };
67−
68− "elm/svg" = {
69− sha256 = "1cwcj73p61q45wqwgqvrvz3aypjyy3fw732xyxdyj6s256hwkn0k";
70− version = "1.0.1";
71− };
72−
73− "elm/time" = {
74− sha256 = "0vch7i86vn0x8b850w1p69vplll1bnbkp8s383z7pinyg94cm2z1";
75− version = "1.0.0";
76− };
77−
78− "elm/url" = {
79− sha256 = "0av8x5syid40sgpl5vd7pry2rq0q4pga28b4yykn9gd9v12rs3l4";
80− version = "1.0.0";
81− };
82−
83− "figbus/elm-urbit-api" = {
84− sha256 = "1qpb8gv2giqp5rysmh0ar66j341bra6wzqka8qcr6xk82ad6afch";
85− version = "4.0.1";
86− };
87−
88− "hecrj/html-parser" = {
89− sha256 = "19rr5w9lca7akwyvq1fbi5vrib4x2b9sf1p76dxhf6vmiw71xrxr";
90− version = "2.4.0";
91− };
92−
93− "ianmackenzie/elm-1d-parameter" = {
94− sha256 = "08r1q80k7w2kyasqk54k1xzd5d1607wj4ahx66jdh7gkrni9m8n5";
95− version = "1.0.1";
96− };
97−
98− "ianmackenzie/elm-3d-camera" = {
99− sha256 = "1cp7ws6kn4sakzk0hpwy83qyz4mr6xfsk2jb4vbf97wjc1fs1gf7";
100− version = "3.1.0";
101− };
102−
103− "ianmackenzie/elm-3d-scene" = {
104− sha256 = "0h1yhxdycyayxp31g7dsj2iarhfzb6gls2rbzqydmd1v4px4rafr";
105− version = "1.0.1";
106− };
107−
108− "ianmackenzie/elm-geometry" = {
109− sha256 = "11vjnr9h1742pn9z55bclsxm5hal1f1v4dd055zd6z14fr91pysn";
110− version = "3.11.0";
111− };
112−
113− "ianmackenzie/elm-triangular-mesh" = {
114− sha256 = "00n08cc3qj610g2f7f77qpl6b6pwr456czf3prkz6iddmi52h3gp";
115− version = "1.1.0";
116− };
117−
118− "ianmackenzie/elm-units" = {
119− sha256 = "053nibhpkf906c7g14z48pvinzzfhnrnk5hkj4mjvjnv3r457h97";
120− version = "2.10.0";
121− };
122−
123− "jasonliang-dev/elm-heroicons" = {
124− sha256 = "038j32fniz3q9kpm7mcjz053fycs2nl7pnishgrrs5djjc6djw1z";
125− version = "2.0.0";
126− };
127−
128− "jxxcarlson/hex" = {
129− sha256 = "0dd0f2lfny4qwzkqbvixkcgprza2n289y46y1y2mgiay8iisfkx9";
130− version = "4.0.0";
131− };
132−
133− "marcosh/elm-html-to-unicode" = {
134− sha256 = "1f1b32qkqxl9lqc4g5737kw4yrz32jxcylgbsbmq871jwv4gwdp8";
135− version = "1.0.4";
136− };
137−
138− "mdgriffith/elm-animator" = {
139− sha256 = "0rbsycykmcjgjlm5r48ycbr9halv2big52ifhs7wpslz0df3hhmh";
140− version = "1.1.1";
141− };
142−
143− "mdgriffith/elm-ui" = {
144− sha256 = "0ffcqv4a4ad400hwp824m3qq4jy82cqp5ghmhp1m0q7n004z6kgv";
145− version = "1.1.8";
146− };
147−
148− "rtfeldman/elm-css" = {
149− sha256 = "1gwhgqwclc7clk1ns4qqzyn7b4wvcmccg9qavvb8m694qmwlkzjy";
150− version = "18.0.0";
151− };
152−
153− "rtfeldman/elm-hex" = {
154− sha256 = "1y0aa16asvwdqmgbskh5iba6psp43lkcjjw9mgzj3gsrg33lp00d";
155− version = "1.0.0";
156− };
157−
158− "toastal/either" = {
159− sha256 = "1yfgvw81szsipibafnllik65vhki9n1lxdylbyy6fsmj6406g1yv";
160− version = "3.6.3";
161− };
162−
163− "zwilias/elm-html-string" = {
164− sha256 = "0pfqbva3rawly0cr448lvajgx2lzqq6avb8wgrp67i6sh9zd06f7";
165− version = "2.0.2";
166− };
167−
168− "AdrianRibao/elm-derberos-date" = {
169− sha256 = "0w4ld89akhhr0zr17vnwcwdiwygmnqdf9nb2g2f3w1b2p3p73acp";
170− version = "1.2.3";
171− };
172−
173− "NoRedInk/elm-string-conversions" = {
174− sha256 = "0xhjyypf5jmnjih9m3rr2ccyf15i403yvpv5xmknqmhmnnhrkf63";
175− version = "1.0.1";
176− };
177−
178− "bitsoflogic/elm-radixint" = {
179− sha256 = "0625xbi9bw3xdixhids82gcx7bhmrcd9q0cxpc6rpiy8br7d2lfn";
180− version = "2.0.0";
181− };
182−
183− "bonzaico/murmur3" = {
184− sha256 = "1ccyaz8n2c9szv25y5pfdp62pdmm81n9h6yhr1r26hab9c15gcxx";
185− version = "1.0.0";
186− };
187−
188− "chain-partners/elm-bignum" = {
189− sha256 = "17fqqmm83r905zw03gjc0dif3ach4d1aar3cv7dlcn2szp0qpgfs";
190− version = "1.0.1";
191− };
192−
193− "elm-explorations/linear-algebra" = {
194− sha256 = "19zf3dgrx5bvhybjwq3bp171wzsb4bfk5rrfjv234b2f8q3yi59g";
195− version = "1.0.3";
196− };
197−
198− "elm-explorations/webgl" = {
199− sha256 = "0ma7laa3ax8zd41s838px1y5s1dsmlqzp4j321lwngxjzvxkm3zy";
200− version = "1.1.3";
201− };
202−
203− "elm/file" = {
204− sha256 = "1rljcb41dl97myidyjih2yliyzddkr2m7n74x7gg46rcw4jl0ny8";
205− version = "1.0.5";
206− };
207−
208− "elm/parser" = {
209− sha256 = "0a3cxrvbm7mwg9ykynhp7vjid58zsw03r63qxipxp3z09qks7512";
210− version = "1.1.0";
211− };
212−
213− "elm/random" = {
214− sha256 = "138n2455wdjwa657w6sjq18wx2r0k60ibpc4frhbqr50sncxrfdl";
215− version = "1.0.0";
216− };
217−
218− "elm/regex" = {
219− sha256 = "0lijsp50w7n1n57mjg6clpn9phly8vvs07h0qh2rqcs0f1jqvsa2";
220− version = "1.0.0";
221− };
222−
223− "elm/virtual-dom" = {
224− sha256 = "1yvb8px2z62xd578ag2q0r5hd1vkz9y7dfkx05355iiy1d7jwq4v";
225− version = "1.0.3";
226− };
227−
228− "ianmackenzie/elm-float-extra" = {
229− sha256 = "0l499d14iabyigdysvx3xa572izmqqki7b1ym2gc7kv4sprrq194";
230− version = "1.1.0";
231− };
232−
233− "ianmackenzie/elm-geometry-linear-algebra-interop" = {
234− sha256 = "15gjd1h0jv3d35p0hyv4k8z1gpag23dip8yacclajjrnkdxmcpgj";
235− version = "2.0.2";
236− };
237−
238− "ianmackenzie/elm-interval" = {
239− sha256 = "086x40s9gnl2r7700ff75w0p3wfrxh144ks24ni5cwhirxwwf0pq";
240− version = "3.1.0";
241− };
242−
243− "ianmackenzie/elm-units-interval" = {
244− sha256 = "0xwb5vj8jcnn88cnwniibgncfvyaxnmwwkrbk1n7ji3aj6w8f8dj";
245− version = "3.2.0";
246− };
247−
248− "ktonon/elm-word" = {
249− sha256 = "055rs4aacvhl4m25lj70wlk71f4yinwgd74wa4nx36mpj2m743b6";
250− version = "2.1.2";
251− };
252−
253− "myrho/elm-round" = {
254− sha256 = "0zv0a60wzkx4xib7h07ijcg72mcyb3vb914hk5pjp6rf4k2lv9kj";
255− version = "1.0.5";
256− };
257−
258− "robinheghan/murmur3" = {
259− sha256 = "15asmgr2zqh7rkywrg5647rpdqkpzxk02v5qc6ndj60jza3gsmjk";
260− version = "1.0.0";
261− };
262−
263− "chelovek0v/bbase64" = {
264− sha256 = "01fj6lf2xpc3byw89zivh11i6kl5bimlsdjd6m3gsi1v88nfj17d";
265− version = "1.0.1";
266− };
267−}
frontend/elm.json+5−26
@@ -2,14 +2,12 @@
2 "type": "application",
2 "type": "application",
3 "source-directories": [
3 "source-directories": [
4 "src",
4 "src",
5− "airlock/src"
5+ "../../airlock/src"
6 ],
6 ],
7− "elm-version": "0.19.2",
7+ "elm-version": "0.19.1",
8 "dependencies": {
8 "dependencies": {
9 "direct": {
9 "direct": {
10− "Evelios/elm-hash": "1.0.0",
11 "TSFoster/elm-bytes-extra": "1.3.0",
10 "TSFoster/elm-bytes-extra": "1.3.0",
12− "avh4/elm-color": "1.0.0",
13 "cmditch/elm-bigint": "2.0.1",
11 "cmditch/elm-bigint": "2.0.1",
14 "elm/browser": "1.0.2",
12 "elm/browser": "1.0.2",
15 "elm/bytes": "1.0.8",
13 "elm/bytes": "1.0.8",
@@ -22,44 +20,25 @@
22 "elm/url": "1.0.0",
20 "elm/url": "1.0.0",
23 "elm-community/list-extra": "8.7.0",
21 "elm-community/list-extra": "8.7.0",
24 "elm-community/maybe-extra": "5.3.0",
22 "elm-community/maybe-extra": "5.3.0",
25− "elm-community/string-extra": "4.0.1",
26 "figbus/elm-urbit-api": "4.0.1",
23 "figbus/elm-urbit-api": "4.0.1",
27− "hecrj/html-parser": "2.4.0",
28− "ianmackenzie/elm-1d-parameter": "1.0.1",
29− "ianmackenzie/elm-3d-camera": "3.1.0",
30− "ianmackenzie/elm-3d-scene": "1.0.1",
31− "ianmackenzie/elm-geometry": "3.11.0",
32− "ianmackenzie/elm-triangular-mesh": "1.1.0",
33− "ianmackenzie/elm-units": "2.10.0",
34 "jasonliang-dev/elm-heroicons": "2.0.0",
24 "jasonliang-dev/elm-heroicons": "2.0.0",
35 "jxxcarlson/hex": "4.0.0",
25 "jxxcarlson/hex": "4.0.0",
36− "marcosh/elm-html-to-unicode": "1.0.4",
37 "mdgriffith/elm-animator": "1.1.1",
26 "mdgriffith/elm-animator": "1.1.1",
38 "mdgriffith/elm-ui": "1.1.8",
27 "mdgriffith/elm-ui": "1.1.8",
39− "rtfeldman/elm-css": "18.0.0",
40 "rtfeldman/elm-hex": "1.0.0",
28 "rtfeldman/elm-hex": "1.0.0",
41− "toastal/either": "3.6.3",
29+ "toastal/either": "3.6.3"
42− "zwilias/elm-html-string": "2.0.2"
43 },
30 },
44 "indirect": {
31 "indirect": {
45 "AdrianRibao/elm-derberos-date": "1.2.3",
32 "AdrianRibao/elm-derberos-date": "1.2.3",
46− "NoRedInk/elm-string-conversions": "1.0.1",
33+ "avh4/elm-color": "1.0.0",
47 "bitsoflogic/elm-radixint": "2.0.0",
34 "bitsoflogic/elm-radixint": "2.0.0",
48− "bonzaico/murmur3": "1.0.0",
49− "chain-partners/elm-bignum": "1.0.1",
50 "elm/file": "1.0.5",
35 "elm/file": "1.0.5",
51 "elm/parser": "1.1.0",
36 "elm/parser": "1.1.0",
52 "elm/random": "1.0.0",
37 "elm/random": "1.0.0",
53 "elm/regex": "1.0.0",
38 "elm/regex": "1.0.0",
54 "elm/virtual-dom": "1.0.3",
39 "elm/virtual-dom": "1.0.3",
55− "elm-explorations/linear-algebra": "1.0.3",
56− "elm-explorations/webgl": "1.1.3",
57− "ianmackenzie/elm-float-extra": "1.1.0",
58− "ianmackenzie/elm-geometry-linear-algebra-interop": "2.0.2",
59− "ianmackenzie/elm-interval": "3.1.0",
60− "ianmackenzie/elm-units-interval": "3.2.0",
40+ "ianmackenzie/elm-units": "2.10.0",
61 "ktonon/elm-word": "2.1.2",
41 "ktonon/elm-word": "2.1.2",
62− "myrho/elm-round": "1.0.5",
63 "robinheghan/murmur3": "1.0.0"
42 "robinheghan/murmur3": "1.0.0"
64 }
43 }
65 },
44 },
frontend/index.html+9−11
@@ -2,24 +2,23 @@
2<html lang="en">
2<html lang="en">
3 <head>
3 <head>
4 <meta charset="UTF-8" />
4 <meta charset="UTF-8" />
5− <link rel="stylesheet" href="assets/css/main.css" >
6 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7 <style>
6 <style>
8 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";
9 margin: 0;
9 margin: 0;
10 }
10 }
11− @keyframes shimmer {
11+ @keyframes bobbing {
12 from {
12 from {
13− color: rgb(204, 204, 204)
13+ transform: translateY(-30px);
14 }
14 }
15 to {
15 to {
16− color: rgb(50, 50, 50)
16+ transform: traslateY(0);
17 }
17 }
18 }
18 }
19−
19+ .bobbing {
20− .shimmer {
21− animation-duration: 0.6s;
20+ animation-duration: 2s;
22− animation-name: shimmer;
21+ animation-name: bobbing;
23 animation-iteration-count: infinite;
22 animation-iteration-count: infinite;
24 animation-direction: alternate;
23 animation-direction: alternate;
25 animation-timing-function: ease-in-out;
24 animation-timing-function: ease-in-out;
@@ -28,9 +27,8 @@
28 </head>
27 </head>
29 <body>
28 <body>
30 <div id="elm"></div>
29 <div id="elm"></div>
31− <script src="elm.js"></script>
30+ <script src="/elm.js"></script>
32− <script type="module" src="script.js"></script>
31+ <script type="module" src="/script.js"></script>
33 </body>
32 </body>
34− <script type="text/javascript" src="assets/js/tilt.js" defer></script>
35</html>
33</html>
36
34
frontend/registry.datdeleted

Binary file not shown.

frontend/script.js+2−3
@@ -1,7 +1,6 @@
1−import { fetchEventSource } from "./fetch-event-source.js"
1+import { fetchEventSource } from "/fetch-event-source.js"
2
2
3−// No dots: Eyre would read anything after a dot as a file extension.
4−const uid = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
3+const uid = `${Math.floor(Date.now() / 1000)}-${Math.random()}`;
5
4
6function searchForInit(obj) {
5function searchForInit(obj) {
7 if (obj.init) {
6 if (obj.init) {
frontend/src/Main.elm+113−186
@@ -10,20 +10,10 @@ import Element.Events as Events exposing (onClick)
10import Element.Font as Font
10import Element.Font as Font
11import Element.Input as Input
11import Element.Input as Input
12import Element.Keyed as Keyed
12import Element.Keyed as Keyed
13−import Html exposing (Html, hr)
14import Html.Attributes
13import Html.Attributes
15−import Html.Parser
16−import Html.String
17−import Html.String
18−import Html.Styled.Attributes exposing (css)
19−import Html.Styled.Attributes exposing (css)
20−import Html.Events exposing (on)
21−import Http
22import Json.Decode as JD
14import Json.Decode as JD
23−import List
24import List.Extra
15import List.Extra
25import Set exposing (Set)
16import Set exposing (Set)
26−import String.Extra exposing (stripTags)
27import Time exposing (Posix)
17import Time exposing (Posix)
28import Ur
18import Ur
29import Ur.Cmd
19import Ur.Cmd
@@ -32,16 +22,11 @@ import Ur.Jam exposing (isSig)
32import Ur.Run
22import Ur.Run
33import Ur.Sub
23import Ur.Sub
34import Ur.Types exposing (Noun(..))
24import Ur.Types exposing (Noun(..))
35−import Url
36−import ElmEscapeHtml
37−import Json.Encode
38−import Hash
39
25
40−{-| The frontend is served by the ship itself, so Eyre is on the same origin.
26+
41−-}
42url : String
27url : String
43url =
28url =
44− ""
29+ "http://localhost:8080"
45
30
46
31
47type SearchEngineState
32type SearchEngineState
@@ -76,6 +61,7 @@ type alias SearchResult =
76 , engines : Set String
61 , engines : Set String
77 }
62 }
78
63
64+
79type Msg
65type Msg
80 = Noop
66 = Noop
81 | GotShipName String
67 | GotShipName String
@@ -93,40 +79,21 @@ main =
93 { init =
79 { init =
94 ( { ship = Nothing
80 ( { ship = Nothing
95 , search = ""
81 , search = ""
96− , initiatedSearch = Nothing
82+ , initiatedSearch = Just "urbit"
97 , searchResults = []
83 , searchResults = []
98 , resultTimelines = Dict.empty
84 , resultTimelines = Dict.empty
99 , engines = []
85 , engines = []
100 , selectedEngines = Set.empty
86 , selectedEngines = Set.empty
101 }
87 }
102− , Http.get
88+ , Cmd.batch
103− { url = url ++ "/~/host"
89+ [ Ur.logIn url "lidlut-tabwed-pillex-ridrup"
90+ |> Cmd.map (always Noop)
104− , expect = Http.expectString (result (always Noop) GotShipName)
91+ , Ur.getShipName url |> Cmd.map (result (always Noop) GotShipName)
105− }
92+ ]
106 |> Ur.Cmd.cmd
93 |> Ur.Cmd.cmd
107 )
94 )
108 , update = update
95 , update = update
109− , view =
96+ , view = \model -> { title = "%seax", body = [ view model |> layout [] ] }
110− \model ->
111− { title = "%seax"
112− , body =
113− [ view model
114− |> layout
115− [ Font.family
116− [ Font.typeface "monospace"
117− , Font.typeface "system-ui"
118− , Font.typeface "-apple-system"
119− , Font.typeface "BlinkMacSystemFont"
120− , Font.typeface "Segoe UI"
121− , Font.typeface "Roboto"
122− , Font.typeface "Helvetica"
123− , Font.typeface "Arial"
124− , Font.sansSerif
125− ]
126− , clipX
127− ]
128− ]
129− }
130 , subscriptions = \model -> Animator.toSubscription Tick model (animator model)
97 , subscriptions = \model -> Animator.toSubscription Tick model (animator model)
131 , createEventSource = createEventSource
98 , createEventSource = createEventSource
132 , urbitSubscriptions =
99 , urbitSubscriptions =
@@ -161,51 +128,40 @@ main =
161 }
128 }
162
129
163
130
164−logoView : List (Attribute Msg) -> Element Msg
131+debug : (String -> String) -> D.Deconstructor a -> D.Deconstructor a
165−logoView attributes =
132+debug log f noun =
166− Element.image (List.append [ Html.Attributes.id "tilt" |> htmlAttribute ] attributes)
133+ case f noun of
167− { src = "assets/img/logo.png", description = "%seax logo" }
134+ Just x ->
135+ Just x
168
136
169−noSearchResultsView : Element Msg
137+ Nothing ->
170−noSearchResultsView = Element.el
138+ let
171− [ Html.Attributes.style "padding" "1em" |> htmlAttribute
139+ _ =
172− , width (px 500)
140+ log (prettyNoun noun)
173− , Font.size 15
141+ in
174− , Html.Attributes.class "wavy" |> htmlAttribute
142+ Nothing
175− ]
176− (Element.text """No results found... :)
177−We'll try and find you something.
178−Just come back later.""")
179−
180−subtleLinkView : String -> String -> Element Msg
181−subtleLinkView text href =
182− Element.el
183− [ Font.size 15
184− , pointer
185− , mouseOver
186− [ Font.color (rgb 0.1 0.7 0.7)
187− ]
188− ]
189− (Element.html (Html.a [ Html.Attributes.href href ] [ Html.text text ]))
190
143
191−aboutView : Element Msg
192−aboutView = row [ centerX, padding 10, spacing 10]
193− [ subtleLinkView "[About]" "https://github.com/iko-soy/seax#readme"
194− , subtleLinkView "[Code]" "https://github.com/iko-soy/seax"
195− ]
196
144
197−titleEmoji : String -> String
145+prettyNoun : Noun -> String
198−titleEmoji title =
146+prettyNoun noun =
199− let maxUnicode = 0x1F3F0
147+ let
200− minUnicode = 0x1F300
148+ go isRhs n =
201− in title |> Hash.fromString
149+ case n of
202− |> Hash.toString
150+ Atom a ->
203− |> String.toInt
151+ if isSig a then
204− |> Maybe.withDefault 0
152+ "~"
205− |> (\x -> ((modBy (maxUnicode - minUnicode) x) + minUnicode))
153+
206− |> Char.fromCode
154+ else
207− |> String.fromChar
155+ "@"
208− |> (\x -> x ++ " ")
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
209
165
210
166
211view : Model -> Element Msg
167view : Model -> Element Msg
@@ -213,66 +169,61 @@ view model =
213 case model.initiatedSearch of
169 case model.initiatedSearch of
214 Nothing ->
170 Nothing ->
215 column [ centerX, centerY, padding 8, spacing 16 ]
171 column [ centerX, centerY, padding 8, spacing 16 ]
216− [ logoView [ centerX, width (px 510), height (px 161) ]
172+ [ el [ centerX, Font.size 67, Font.heavy ] (text "%seax")
217 , searchView model
173 , searchView model
218− , aboutView
219 ]
174 ]
220
175
221− Just _ ->
176+ Just query ->
222 column [ width fill, spacing 16 ]
177 column [ width fill, spacing 16 ]
223− [ column [ spacing 16, width (minimum 0 fill), padding 8]
178+ [ row [ spacing 16, width fill ]
224− [ row [] [ logoView [height (px 40)], searchView model |> el [] ]
179+ [ searchView model |> el [ padding 8 ]
225− , row [ spacing 8, paddingEach { top = 16, bottom = 16, left = 0, right = 0 } ]
226− (if List.isEmpty model.engines then
180+ , model.engines
227− []
228− else
229− [ text "Sources: "
230− , model.engines
231− |> List.filter (\( _, state ) -> state /= Failed)
181+ |> List.filter (\( _, state ) -> state /= Failed)
232− |> List.map
182+ |> List.map
233− (\( name, state ) ->
183+ (\( name, state ) ->
234− text ("%" ++ name)
184+ text ("%" ++ name)
235− |> el
185+ |> el
186+ ([ Font.size 43
236− (Font.bold
187+ , Font.bold
237− :: (if Set.isEmpty model.selectedEngines then
188+ ]
238− []
189+ ++ (if Set.isEmpty model.selectedEngines then
239−
190+ []
191+
240− else if Set.member name model.selectedEngines then
192+ else if Set.member name model.selectedEngines then
241− []
193+ []
242
194
243− else
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
244− [ Font.color (rgb 0.8 0.8 0.8)
202+ , Font.color (rgb 0.8 0.8 0.8)
245 ]
203 ]
246− )
247− ++ (case state of
248− Loading ->
249− [ Html.Attributes.class "shimmer" |> htmlAttribute
250− ]
251
204
252− Completed ->
205+ Completed ->
253− [ mouseOver
206+ [ mouseOver
254− [ Font.color (rgb 0.6 0.6 0.6)
207+ [ Font.color (rgb 0.6 0.6 0.6)
255− ]
256− , pointer
257− , Events.onClick (ToggleSearchEngine name)
258 ]
208 ]
209+ , pointer
210+ , Events.onClick (ToggleSearchEngine name)
211+ ]
259
212
260− Failed ->
213+ Failed ->
261− []
214+ []
262− )
215+ )
263− )
216+ )
264− )
217+ )
265− |> wrappedRow
218+ |> row
266− [ spacing 8
219+ [ spacing 16
220+ , clipX
221+ , scrollbarX
267− , width fill
222+ , width fill
268− ]
223+ , height (px 62)
269− ]
224+ ]
270− )
271 ]
225 ]
272− , (if List.isEmpty model.searchResults then
273− noSearchResultsView
274− else
275− Keyed.column [ width (minimum 0 fill), padding 8 ]
226+ , Keyed.column [ width fill, padding 8 ]
276 (model.searchResults
227 (model.searchResults
277 |> List.filter
228 |> List.filter
278 (\{ engines } ->
229 (\{ engines } ->
@@ -287,7 +238,7 @@ view model =
287 ( link
238 ( link
288 , column
239 , column
289 [ spacing 4
240 [ spacing 4
290− , width (minimum 0 fill)
241+ , width fill
291 , pointer
242 , pointer
292 , alpha
243 , alpha
293 (Animator.move
244 (Animator.move
@@ -309,70 +260,38 @@ view model =
309 |> Maybe.map Tuple.first
260 |> Maybe.map Tuple.first
310 |> Maybe.withDefault (Animator.init 0)
261 |> Maybe.withDefault (Animator.init 0)
311 )
262 )
312− (\i -> toFloat i * 0 |> Animator.at)
263+ (\i -> toFloat i * 47 |> Animator.at)
313 |> String.fromFloat
264 |> String.fromFloat
314 )
265 )
315 ++ "px"
266 ++ "px"
316 )
267 )
317 |> htmlAttribute
268 |> htmlAttribute
318− , Html.Attributes.style "margin-bottom" "1em" |> htmlAttribute
269+ , Html.Attributes.style "position" "absolute" |> htmlAttribute
319− , Html.Attributes.style "padding" "0.5em" |> htmlAttribute
320− , Html.Attributes.class "result" |> htmlAttribute
321 ]
270 ]
322− [ (Element.html
271+ [ [ text title ]
323− (Html.a
272+ |> paragraph
324− [ Html.Attributes.href link
273+ [ Font.size 16
325− , Html.Attributes.class "result-title"
274+ , width fill
326− , Html.Attributes.target "_blank"
275+ , Html.Attributes.style "text-overflow" "ellipsis" |> htmlAttribute
327 ]
276 ]
328− [ Html.text
277+ , [ text link ]
329− (Maybe.withDefault ""
278+ |> paragraph
330− (Url.percentDecode
279+ [ Font.size 12
331− (ElmEscapeHtml.unescape
332− (stripTags
333− ((titleEmoji title) ++ title)
334− ))))
335− ]
336− )
337− )
338− , text link
339− |> el
340− [ Font.color (rgb 0.7 0.7 0.7)
341− , Html.Attributes.class "result-link" |> htmlAttribute
280+ , Html.Attributes.style "text-overflow" "ellipsis" |> htmlAttribute
281+ , width fill
282+ , Font.color (rgb 0.7 0.7 0.7)
342 ]
283 ]
343 ]
284 ]
344 )
285 )
345 )
286 )
346− ))
347− , Element.html (Html.hr [Html.Attributes.style "width" "95%"] [])
348− , aboutView
349− ]
350−
351−
352−onEnter : msg -> Element.Attribute msg
353−onEnter msg =
354− Element.htmlAttribute
355− (Html.Events.on "keyup"
356− (JD.field "key" JD.string
357− |> JD.andThen
358− (\key ->
359− if key == "Enter" then
360− JD.succeed msg
361−
362− else
363− JD.fail "Not the enter key"
364 )
287 )
365− )
288+ ]
366− )
367
289
368
290
369searchView : { a | search : String } -> Element Msg
291searchView : { a | search : String } -> Element Msg
370searchView model =
292searchView model =
371 row [ spacing 8 ]
293 row [ spacing 8 ]
372− [ Input.text
294+ [ Input.text [ width (px 250) ]
373− [ Html.Attributes.class "shadow" |> htmlAttribute, width (px 400)
374− , onEnter (Search model.search)
375− ]
376 { onChange = UpdateSearch
295 { onChange = UpdateSearch
377 , placeholder = Nothing
296 , placeholder = Nothing
378 , text = model.search
297 , text = model.search
@@ -382,15 +301,23 @@ searchView model =
382 { label =
301 { label =
383 text "search"
302 text "search"
384 |> el
303 |> el
385− [ Html.Attributes.class "search" |> htmlAttribute
386− , Border.width 2
304+ [ Border.width 4
387− , padding 6
305+ , padding 4
388 , Font.size 29
306 , Font.size 29
307+ , Font.bold
308+ , Border.color (rgb 0 0 0)
309+ , Background.color (rgb 0 0 0)
310+ , Font.color (rgb 1 1 1)
311+ , mouseOver
312+ [ Font.color (rgb 0 0 0)
313+ , Background.color (rgb 1 1 1)
314+ ]
389 ]
315 ]
390 , onPress = Just (Search model.search)
316 , onPress = Just (Search model.search)
391 }
317 }
392 ]
318 ]
393
319
320+
394update : Msg -> Model -> ( Model, Ur.Cmd.Cmd Msg )
321update : Msg -> Model -> ( Model, Ur.Cmd.Cmd Msg )
395update msg model =
322update msg model =
396 case msg of
323 case msg of
zod/seax/app/seax.hoon+35−112
@@ -1,5 +1,6 @@
1/- *seax
1/- *seax
2−/+ dbug, engines, rank, server, sink
2+/+ dbug, engines, rank, sink
3+
3|%
4|%
4+$ query cord
5+$ query cord
5+$ search-state
6+$ search-state
@@ -7,16 +8,14 @@ $:
7 number-of-subscriptions=@u
8 number-of-subscriptions=@u
8 search-results=(map term (unit (list search-result)))
9 search-results=(map term (unit (list search-result)))
9==
10==
10−:: state-0 (2023), and state-1 and state-2 (only in development builds of
11−:: the 408k port), also kept peers: other ships that may run %seax. They
12−:: were pinged and told about each other, but never searched, so they are
13−:: dropped on load. state-0 also kept notes from Tlon's %diary agent.
14−::
15−+$ state-3
11++$ state-0
16$:
12$:
17− %3
18 search-subscriptions=(map query search-state)
13 search-subscriptions=(map query search-state)
19==
14==
15++$ poke
16+ $%
17+ [%search query=tape]
18+ ==
20++ rank-results
19++ rank-results
21 |= search-results=(map term (unit (list search-result)))
20 |= search-results=(map term (unit (list search-result)))
22 %- rank
21 %- rank
@@ -38,41 +37,11 @@ $:
38 |= [engine-name=term engine=engine *]
37 |= [engine-name=term engine=engine *]
39 =/ req
38 =/ req
40 ^- task:iris
39 ^- task:iris
41− [%request (request.engine (trip query)) *outbound-config:iris]
40+ [%request [%'GET' (crip (url.engine (trip query))) ~ ~] *outbound-config:iris]
42 [%pass ~[%search-result query engine-name] %arvo %i req]
41 [%pass ~[%search-result query engine-name] %arvo %i req]
43−:: +serve: the frontend, served from /web in this desk at /apps/seax/
44−::
45−++ serve
46− |= [=bowl:gall url=@t]
47− ^- simple-payload:http
48− =/ [[ext=(unit @ta) site=(list @t)] *] (parse-request-line:server url)
49− ?+ site not-found:gen:server
50− [%apps %seax ~]
51− (redirect:gen:server '/apps/seax/')
52− ::
53− [%apps %seax %$ ~]
54− (serve-file bowl /index/html)
55− ::
56− [%apps %seax *]
57− ?~ ext not-found:gen:server
58− =/ pax=(list @t) (snoc t.t.site u.ext)
59− ?. (levy pax (sane %ta)) not-found:gen:server
60− (serve-file bowl ;;(path pax))
61− ==
62−++ serve-file
63− |= [=bowl:gall pax=path]
64− ^- simple-payload:http
65− =/ root=path /(scot %p our.bowl)/[q.byk.bowl]/(scot %da now.bowl)
66− =/ file=path (weld root (welp /web pax))
67− ?. .^(? %cu file) not-found:gen:server
68− =/ =mark ;;(mark (rear pax))
69− =/ =dais:clay .^(dais:clay %cb (snoc root mark))
70− =/ =tube:clay .^(tube:clay %cc (weld root /[mark]/mime))
71− =/ =mime !<(mime (tube (vale:dais .^(* %cx file))))
72− [[200 ['content-type' (rsh [3 1] (spat p.mime))]~] `q.mime]
73--
42--
74
43
75−=/ state *state-3
44+=/ state *state-0
76=* current-engines
45=* current-engines
77 |= =query
46 |= =query
78 ^- (list [term ?(%loading %failed %completed)])
47 ^- (list [term ?(%loading %failed %completed)])
@@ -83,7 +52,6 @@ $:
83 (~(get by search-results:(~(got by search-subscriptions.state) query)) engine)
52 (~(get by search-results:(~(got by search-subscriptions.state) query)) engine)
84 ?~ results %loading
53 ?~ results %loading
85 ?~ +.results %failed
54 ?~ +.results %failed
86− ?~ +>.results %failed
87 %completed
55 %completed
88=* search-results
56=* search-results
89 |= =query
57 |= =query
@@ -102,59 +70,24 @@ $:
102%- agent:dbug
70%- agent:dbug
103|_ =bowl:gall
71|_ =bowl:gall
104+* this .
72+* this .
105− eyre-connect-card [%pass /eyre/connect %arvo %e %connect [~ /apps/seax] dap.bowl]
106−++ on-init
73+++ on-init `..on-init
107− [~[eyre-connect-card] this]
108++ on-save !>(state)
74++ on-save !>(state)
109−++ on-load
75+++ on-load
110 |= =vase
76 |= =vase
77+ =/ state !<(state-0 vase)
78+ :- ~
79+ %= this
80+ state state
81+ ==
82+++ on-poke
83+ |= =cage
111 ^- (quip card:agent:gall _this)
84 ^- (quip card:agent:gall _this)
112− ?: ?=([%3 *] q.vase)
113− [~ this(state !<(state-3 vase))]
114− :: an older state. its searches are dropped, but gall still has their
115− :: subscribers, so kick them, or they would leave a new search for the
116− :: same query. a state-0 agent stayed suspended until this upgrade, so
117− :: its searches are stale anyway
118− ::
119− =/ stale=(list path)
120− %~ tap in
121− %- silt
122− %+ murn ~(val by sup.bowl)
123− |= [ship =path]
124− ?.(?=([%search *] path) ~ `path)
125− =/ kick=(list card:agent:gall)
126− ?~(stale ~ [%give %kick stale ~]~)
127− :: call off the pings to peers that are still waiting for an answer
128− ::
129− =/ leaves=(list card:agent:gall)
130− %+ murn ~(tap by wex.bowl)
131− |= [[=wire =ship =term] *]
132− ^- (unit card:agent:gall)
133− ?. ?=([%alive ~] wire) ~
134− `[%pass wire %agent [ship term] %leave ~]
135− :_ this(state [%3 ~])
136− [eyre-connect-card (weld kick leaves)]
137−++ on-poke
138− |= [=mark =vase]
139− ^- (quip card:agent:gall _this)
140− :: only eyre pokes us. older versions also took peers from other ships
141− ::
142− ?> ?=(%handle-http-request mark)
143− =/ [eyre-id=@ta =inbound-request:eyre] !<([@ta inbound-request:eyre] vase)
144− :_ this
145− %+ give-simple-payload:app:server eyre-id
146− %+ require-authorization:app:server inbound-request
147− |= =inbound-request:eyre
148− (serve bowl url.request.inbound-request)
149−++ on-watch
150− |= =path
151− ?+ path !!
152− [%http-response *]
153 `this
85 `this
86+
87+++ on-watch
88+ |= =path
89+ ?+ path !!
154 [%search query ~]
90 [%search query ~]
155− :: searching makes requests to every engine, so only we can do it
156− ::
157− ?> =(our src):bowl
158 =/ query +<.path
91 =/ query +<.path
159 =/ subscription=(unit search-state)
92 =/ subscription=(unit search-state)
160 (~(get by search-subscriptions.state) query)
93 (~(get by search-subscriptions.state) query)
@@ -171,36 +104,28 @@ $:
171 [~[flush:(sink query)] this]
104 [~[flush:(sink query)] this]
172 ==
105 ==
173
106
107+:: This was not tested because it requires waiting 12 hours.
174++ on-leave
108++ on-leave
175 |= =path
109 |= =path
176− ?+ path `this
110+ ?+ path !!
177− [%search query ~]
111+ [%search query ~]
178 =/ query +<.path
112 =/ query +<.path
179 ~& path
113 ~& path
180− =/ search-state (~(get by search-subscriptions.state) query)
114+ =/ search-state (~(got by search-subscriptions.state) query)
181− :: searches from before an upgrade are dropped in +on-load
182− ::
183− ?~ search-state `this
184− =/ number-of-subscriptions (dec number-of-subscriptions.u.search-state)
115+ =/ number-of-subscriptions (dec number-of-subscriptions.search-state)
185 =. search-subscriptions.state
116 =. search-subscriptions.state
186− ?~ number-of-subscriptions
117+ ?~ number-of-subscriptions
187 (~(del by search-subscriptions.state) query)
118 (~(del by search-subscriptions.state) query)
188− (~(put by search-subscriptions.state) query u.search-state(number-of-subscriptions number-of-subscriptions))
119+ (~(put by search-subscriptions.state) query search-state(number-of-subscriptions number-of-subscriptions))
189 `this
120 `this
190 ==
121 ==
191
122
192−++ on-peek
123+++ on-peek
193 |= path
124 |= path
194 ~
125 ~
195−:: older versions poked their peers with peer lists, and the acks to
196−:: those pokes can still arrive after an upgrade
197−::
198−++ on-agent
199− |= [=wire =sign:agent:gall]
126+++ on-agent |=([wire sign:agent:gall] !!)
200− ^- (quip card:agent:gall _this)
201− `this
202++ on-arvo
127++ on-arvo
203− |= [=wire =sign-arvo]
128+ |= [=wire =sign-arvo]
204 ^- (quip card:agent:gall _this)
129 ^- (quip card:agent:gall _this)
205 ~& wire
130 ~& wire
206 ?+ wire `this
131 ?+ wire `this
@@ -218,12 +143,10 @@ $:
218 =. search-results.search-subscription
143 =. search-results.search-subscription
219 (~(put by search-results.search-subscription) engine-name results)
144 (~(put by search-results.search-subscription) engine-name results)
220 =. search-subscriptions.state
145 =. search-subscriptions.state
221− (~(put by search-subscriptions.state) query search-subscription)
146+ (~(put by search-subscriptions.state) query search-subscription)
222 =^ card sink (sync:sink (sink-for-query query))
147 =^ card sink (sync:sink (sink-for-query query))
223− ~& (current-engines query)
224 [~[card] this]
148 [~[card] this]
225 ==
149 ==
226−++ on-fail
227− |= [term =tang]
150+++ on-fail |=([term tang] `..on-init)
228− ((slog tang) `this)
229--
151--
152+
zod/seax/desk.docket-0deleted−10
@@ -1,10 +0,0 @@
1−:~
2− title+'%seax'
3− info+'The Urbit meta-search engine. Sends your query to several search engines from your ship and merges their results.'
4− color+0xff.ffff
5− image+'/apps/seax/assets/img/tile.png'
6− site+/apps/seax
7− version+[0 1 0]
8− website+'https://github.com/iko-soy/seax'
9− license+'MIT'
10−==
zod/seax/lib/dbug.hoon+3−9
@@ -4,8 +4,7 @@
4::
4::
5|%
5|%
6+$ poke
6+$ poke
7− $% [%skip depth=@ud =poke]
8− [%bowl ~]
7+ $% [%bowl ~]
9 [%state grab=cord]
8 [%state grab=cord]
10 [%incoming =about]
9 [%incoming =about]
11 [%outgoing =about]
10 [%outgoing =about]
@@ -33,13 +32,8 @@
33 ?. ?=(%dbug mark)
32 ?. ?=(%dbug mark)
34 =^ cards agent (on-poke:ag mark vase)
33 =^ cards agent (on-poke:ag mark vase)
35 [cards this]
34 [cards this]
35+ =/ dbug
36− =/ dbug !<(poke vase)
36+ !<(poke vase)
37− =? dbug ?=([%skip %0 *] dbug) poke.dbug
38− ?: ?=(%skip -.dbug)
39− =^ cards agent
40− %+ on-poke:ag %dbug
41− !>(`poke`dbug(depth (dec depth.dbug)))
42− [cards this]
43 =; =tang
37 =; =tang
44 ((%*(. slog pri 1) tang) [~ this])
38 ((%*(. slog pri 1) tang) [~ this])
45 ?- -.dbug
39 ?- -.dbug
zod/seax/lib/docket.hoondeleted−223
@@ -1,223 +0,0 @@
1−/- *docket
2−|%
3−::
4−++ mime
5− |%
6− +$ draft
7− $: title=(unit @t)
8− info=(unit @t)
9− color=(unit @ux)
10− glob-http=(unit [=url hash=@uvH])
11− glob-ames=(unit [=ship hash=@uvH])
12− base=(unit term)
13− site=(unit path)
14− image=(unit url)
15− version=(unit version)
16− website=(unit url)
17− license=(unit cord)
18− ==
19− ::
20− ++ finalize
21− |= =draft
22− ^- (unit docket)
23− ?~ title.draft ~
24− ?~ info.draft ~
25− ?~ color.draft ~
26− ?~ version.draft ~
27− ?~ website.draft ~
28− ?~ license.draft ~
29− =/ href=(unit href)
30− ?^ site.draft `[%site u.site.draft]
31− ?~ base.draft ~
32− ?^ glob-http.draft
33− `[%glob u.base hash.u.glob-http %http url.u.glob-http]:draft
34− ?~ glob-ames.draft
35− ~
36− `[%glob u.base hash.u.glob-ames %ames ship.u.glob-ames]:draft
37− ?~ href ~
38− =, draft
39− :- ~
40− :* %1
41− u.title
42− u.info
43− u.color
44− u.href
45− image
46− u.version
47− u.website
48− u.license
49− ==
50− ::
51− ++ from-clauses
52− =| =draft
53− |= cls=(list clause)
54− ^- (unit docket)
55− =* loop $
56− ?~ cls (finalize draft)
57− =* clause i.cls
58− =. draft
59− ?- -.clause
60− %title draft(title `title.clause)
61− %info draft(info `info.clause)
62− %color draft(color `color.clause)
63− %glob-http draft(glob-http `[url hash]:clause)
64− %glob-ames draft(glob-ames `[ship hash]:clause)
65− %base draft(base `base.clause)
66− %site draft(site `path.clause)
67− %image draft(image `url.clause)
68− %version draft(version `version.clause)
69− %website draft(website `website.clause)
70− %license draft(license `license.clause)
71− ==
72− loop(cls t.cls)
73− ::
74− ++ to-clauses
75− |= d=docket
76− ^- (list clause)
77− %- zing
78− :~ :~ title+title.d
79− info+info.d
80− color+color.d
81− version+version.d
82− website+website.d
83− license+license.d
84− ==
85− ?~ image.d ~ ~[image+u.image.d]
86− ?: ?=(%site -.href.d) ~[site+path.href.d]
87− =/ ref=glob-reference glob-reference.href.d
88− :~ base+base.href.d
89− ?- -.location.ref
90− %http [%glob-http url.location.ref hash.ref]
91− %ames [%glob-ames ship.location.ref hash.ref]
92− == == ==
93− ::
94− ++ spit-clause
95− |= =clause
96− ^- tape
97− %+ weld " {(trip -.clause)}+"
98− ?+ -.clause "'{(trip +.clause)}'"
99− %color (scow %ux color.clause)
100− %site (spud path.clause)
101− ::
102− %glob-http
103− "['{(trip url.clause)}' {(scow %uv hash.clause)}]"
104− ::
105− %glob-ames
106− "[{(scow %p ship.clause)} {(scow %uv hash.clause)}]"
107− ::
108− %version
109− =, version.clause
110− "[{(scow %ud major)} {(scow %ud minor)} {(scow %ud patch)}]"
111− ==
112− ::
113− ++ spit-docket
114− |= dock=docket
115− ^- tape
116− ;: welp
117− ":~\0a"
118− `tape`(zing (join "\0a" (turn (to-clauses dock) spit-clause)))
119− "\0a=="
120− ==
121− --
122−::
123−++ enjs
124− =, enjs:format
125− |%
126− ::
127− ++ charge-update
128− |= u=^charge-update
129− ^- json
130− %+ frond -.u
131− ^- json
132− ?- -.u
133− %del-charge s+desk.u
134− ::
135− %initial
136− %- pairs
137− %+ turn ~(tap by initial.u)
138− |=([=desk c=^charge] [desk (charge c)])
139− ::
140− %add-charge
141− %- pairs
142− :~ desk+s+desk.u
143− charge+(charge charge.u)
144− ==
145− ==
146− ::
147− ++ num
148− |= a=@u
149− ^- ^tape
150− =/ p=json (numb a)
151− ?> ?=(%n -.p)
152− (trip p.p)
153− ::
154− ++ version
155− |= v=^version
156− ^- json
157− :- %s
158− %- crip
159− "{(num major.v)}.{(num minor.v)}.{(num patch.v)}"
160− ::
161− ++ merge
162− |= [a=json b=json]
163− ^- json
164− ?> &(?=(%o -.a) ?=(%o -.b))
165− [%o (~(uni by p.a) p.b)]
166− ::
167− ++ href
168− |= h=^href
169− %+ frond -.h
170− ?- -.h
171− %site s+(spat path.h)
172− %glob
173− %- pairs
174− :~ base+s+base.h
175− glob-reference+(glob-reference glob-reference.h)
176− ==
177− ==
178− ::
179− ++ glob-reference
180− |= ref=^glob-reference
181− %- pairs
182− :~ hash+s+(scot %uv hash.ref)
183− location+(glob-location location.ref)
184− ==
185− ::
186− ++ glob-location
187− |= loc=^glob-location
188− ^- json
189− %+ frond -.loc
190− ?- -.loc
191− %http s+url.loc
192− %ames s+(scot %p ship.loc)
193− ==
194− ::
195− ++ charge
196− |= c=^charge
197− %+ merge (docket docket.c)
198− %- pairs
199− :~ chad+(chad chad.c)
200− ==
201− ::
202− ++ docket
203− |= d=^docket
204− ^- json
205− %- pairs
206− :~ title+s+title.d
207− info+s+info.d
208− color+s+(scot %ux color.d)
209− href+(href href.d)
210− image+?~(image.d ~ s+u.image.d)
211− version+(version version.d)
212− license+s+license.d
213− website+s+website.d
214− ==
215− ::
216− ++ chad
217− |= c=^chad
218− %+ frond -.c
219− ?+ -.c ~
220− %hung s+err.c
221− ==
222− --
223−--
zod/seax/lib/engines.hoon+3−2
@@ -1,13 +1,14 @@
1/- *seax
1/- *seax
2−/+ engines-crowdview, engines-wiby, engines-urbit-dev
2+/+ engines-yep, engines-crowdview, engines-wiby, engines-alexandria
3
3
4^- engines
4^- engines
5
5
6%- malt
6%- malt
7%- limo
7%- limo
8:~
8:~
9+ [name=%yep engine=engines-yep weight=.1]
9 [name=%crowdview engine=engines-crowdview weight=.2]
10 [name=%crowdview engine=engines-crowdview weight=.2]
10 [name=%wiby engine=engines-wiby weight=.2]
11 [name=%wiby engine=engines-wiby weight=.2]
11− [name=%urbit-dev engine=engines-urbit-dev weight=.2]
12+ [name=%alexandria engine=engines-alexandria weight=.2]
12==
13==
13
14
zod/seax/lib/engines/alexandria.hoonnew file+32
@@ -0,0 +1,32 @@
1+/- *seax
2+
3+^- engine
4+=<
5+|%
6+++ url
7+ |= query=tape
8+ ^- tape
9+ %+ weld
10+ "https://api.alexandria.org/?a=1&q="
11+ (en-urlt:html query)
12+++ results
13+ |= response=mime-data:iris
14+ ^- (unit (list search-result))
15+ =/ body q.data.response
16+ ;< jon=json _biff (de:json:html body)
17+ (apex jon)
18+--
19+::
20+|%
21+++ apex
22+ =, dejs-soft:format
23+ %- ot
24+ ~[results+(ar parse-search-result)]
25+++ parse-search-result
26+ =, dejs-soft:format
27+ %- ot
28+ ~[title+so url+so]
29+--
30+
31+
32+
zod/seax/lib/engines/crowdview.hoon+5−3
@@ -3,10 +3,12 @@
3^- engine
3^- engine
4=<
4=<
5|%
5|%
6−++ request
6+++ url
7 |= query=tape
7 |= query=tape
8− ^- request:http
8+ ^- tape
9+ %+ weld
9− (get "https://crowdview-next-js.onrender.com/api/search-v3?query=" query)
10+ "https://crowdview-next-js.onrender.com/api/search-v3?query="
11+ (en-urlt:html query)
10++ results
12++ results
11 |= response=mime-data:iris
13 |= response=mime-data:iris
12 ^- (unit (list search-result))
14 ^- (unit (list search-result))
zod/seax/lib/engines/urbit-dev.hoondeleted−36
@@ -1,36 +0,0 @@
1−/- *seax
2−
3−^- engine
4−=<
5−|%
6−++ request
7− |= query=tape
8− ^- request:http
9− :^ %'POST' 'https://docs.urbit.org/~gitbook/search'
10− ~[['content-type' 'application/json']]
11− %- some
12− %- as-octs:mimes:html
13− %- en:json:html
14− %- pairs:enjs:format
15− :~ query+s+(crip query)
16− scope+(pairs:enjs:format ~[mode+s+'all'])
17− ==
18−++ results
19− |= response=mime-data:iris
20− ^- (unit (list search-result))
21− =/ body q.data.response
22− ;< jon=json _biff (de:json:html body)
23− (apex jon)
24−--
25−::
26−|%
27−++ apex
28− =, dejs-soft:format
29− (ar parse-search-result)
30−++ parse-search-result
31− |= jon=json
32− ^- (unit search-result)
33− =, dejs-soft:format
34− ;< [title=@t href=@t] _biff ((ot ~[title+so href+so]) jon)
35− `[title (cat 3 'https://docs.urbit.org' href)]
36−--
zod/seax/lib/engines/wiby.hoon+5−3
@@ -3,10 +3,12 @@
3^- engine
3^- engine
4=<
4=<
5|%
5|%
6−++ request
6+++ url
7 |= query=tape
7 |= query=tape
8− ^- request:http
8+ ^- tape
9+ %+ weld
9− (get "https://wiby.me/json/?q=" query)
10+ "https://wiby.me/json/?q="
11+ (en-urlt:html query)
10++ results
12++ results
11 |= response=mime-data:iris
13 |= response=mime-data:iris
12 ^- (unit (list search-result))
14 ^- (unit (list search-result))
zod/seax/lib/engines/yep.hoonnew file+30
@@ -0,0 +1,30 @@
1+/- *seax
2+
3+^- engine
4+=<
5+|%
6+++ url
7+ |= query=tape
8+ ^- tape
9+ %+ weld
10+ "https://api.yep.com/fs/1/?type=web&no_correct=false&limit=100&q="
11+ (en-urlt:html query)
12+++ results
13+ |= response=mime-data:iris
14+ =/ body q.data.response
15+ ;< jon=json _biff (de:json:html body)
16+ (apex jon)
17+--
18+::
19+|%
20+++ apex
21+ |= jon=json
22+ ^- (unit (list search-result))
23+ =, dejs-soft:format
24+ ;< [* results=(list search-result)] _biff %- (at ~[so (ot ~[results+(ar parse-search-result)])]) jon
25+ `results
26+++ parse-search-result
27+ =, dejs-soft:format
28+ %- ot
29+ ~[title+so url+so]
30+--
zod/seax/lib/rank.hoon+2−2
@@ -9,9 +9,9 @@
9 %- zing
9 %- zing
10 %+ turn results
10 %+ turn results
11 |= [engine=term results=(list search-result)]
11 |= [engine=term results=(list search-result)]
12− %+ turn (zip (gulf 0 (lent results)) results)
12+ %+ turn (zip (gulf 1 (lent results)) results)
13 |= [ord=@u result=search-result]
13 |= [ord=@u result=search-result]
14− %- malt ~[[url.result [~[engine] ~[+(ord)] result]]]
14+ %- malt ~[[url.result [~[engine] ~[ord] result]]]
15=/ url-to-engines-map
15=/ url-to-engines-map
16 ^- (map @t [engines=(list term) ords=(list @u) result=search-result])
16 ^- (map @t [engines=(list term) ords=(list @u) result=search-result])
17 %+ roll url-to-engine-map
17 %+ roll url-to-engine-map
zod/seax/lib/server.hoondeleted−159
@@ -1,159 +0,0 @@
1−=, eyre
2−|%
3−+$ request-line
4− $: [ext=(unit @ta) site=(list @t)]
5− args=(list [key=@t value=@t])
6− ==
7−:: +parse-request-line: take a cord and parse out a url
8−::
9−++ parse-request-line
10− |= url=@t
11− ^- request-line
12− (fall (rush url ;~(plug apat:de-purl:html yque:de-purl:html)) [[~ ~] ~])
13−::
14−++ manx-to-octs
15− |= man=manx
16− ^- octs
17− (as-octt:mimes:html (en-xml:html man))
18−::
19−++ json-to-octs
20− |= jon=json
21− ^- octs
22− (as-octs:mimes:html (en:json:html jon))
23−::
24−++ app
25− |%
26− ::
27− :: +require-authorization:
28− :: redirect to the login page when unauthenticated
29− :: otherwise call handler on inbound request
30− ::
31− ++ require-authorization
32− |= $: =inbound-request:eyre
33− handler=$-(inbound-request:eyre simple-payload:http)
34− ==
35− ^- simple-payload:http
36− ::
37− ?: authenticated.inbound-request
38− ~! this
39− ~! +:*handler
40− (handler inbound-request)
41− ::
42− =- [[307 ['location' -]~] ~]
43− %^ cat 3
44− '/~/login?redirect='
45− url.request.inbound-request
46− ::
47− :: +require-authorization-simple:
48− :: redirect to the login page when unauthenticated
49− :: otherwise pass through simple-paylod
50− ::
51− ++ require-authorization-simple
52− |= [=inbound-request:eyre =simple-payload:http]
53− ^- simple-payload:http
54− ::
55− ?: authenticated.inbound-request
56− ~! this
57− simple-payload
58− ::
59− =- [[307 ['location' -]~] ~]
60− %^ cat 3
61− '/~/login?redirect='
62− url.request.inbound-request
63− ::
64− ++ give-simple-payload
65− |= [eyre-id=@ta =simple-payload:http]
66− ^- (list card:agent:gall)
67− =/ header-cage
68− [%http-response-header !>(response-header.simple-payload)]
69− =/ data-cage
70− [%http-response-data !>(data.simple-payload)]
71− :~ [%give %fact ~[/http-response/[eyre-id]] header-cage]
72− [%give %fact ~[/http-response/[eyre-id]] data-cage]
73− [%give %kick ~[/http-response/[eyre-id]] ~]
74− ==
75− --
76−++ gen
77− |%
78− ::
79− ++ max-1-da ['cache-control' 'max-age=86400']
80− ++ max-1-wk ['cache-control' 'max-age=604800']
81− ::
82− ++ html-response
83− =| cache=?
84− |= =octs
85− ^- simple-payload:http
86− :_ `octs
87− [200 [['content-type' 'text/html'] ?:(cache [max-1-wk ~] ~)]]
88− ::
89− ++ css-response
90− =| cache=?
91− |= =octs
92− ^- simple-payload:http
93− :_ `octs
94− [200 [['content-type' 'text/css'] ?:(cache [max-1-wk ~] ~)]]
95− ::
96− ++ js-response
97− =| cache=?
98− |= =octs
99− ^- simple-payload:http
100− :_ `octs
101− [200 [['content-type' 'text/javascript'] ?:(cache [max-1-wk ~] ~)]]
102− ::
103− ++ png-response
104− =| cache=?
105− |= =octs
106− ^- simple-payload:http
107− :_ `octs
108− [200 [['content-type' 'image/png'] ?:(cache [max-1-wk ~] ~)]]
109− ::
110− ++ svg-response
111− =| cache=?
112− |= =octs
113− ^- simple-payload:http
114− :_ `octs
115− [200 [['content-type' 'image/svg+xml'] ?:(cache [max-1-wk ~] ~)]]
116− ::
117− ++ ico-response
118− |= =octs
119− ^- simple-payload:http
120− [[200 [['content-type' 'image/x-icon'] max-1-wk ~]] `octs]
121− ::
122− ++ woff2-response
123− =| cache=?
124− |= =octs
125− ^- simple-payload:http
126− [[200 [['content-type' 'font/woff2'] max-1-wk ~]] `octs]
127− ::
128− ++ json-response
129− =| cache=_|
130− |= =json
131− ^- simple-payload:http
132− :_ `(json-to-octs json)
133− [200 [['content-type' 'application/json'] ?:(cache [max-1-da ~] ~)]]
134− ::
135− ++ manx-response
136− =| cache=_|
137− |= man=manx
138− ^- simple-payload:http
139− :_ `(manx-to-octs man)
140− [200 [['content-type' 'text/html'] ?:(cache [max-1-da ~] ~)]]
141− ::
142− ++ not-found
143− ^- simple-payload:http
144− [[404 ~] ~]
145− ::
146− ++ login-redirect
147− |= =request:http
148− ^- simple-payload:http
149− =- [[307 ['location' -]~] ~]
150− %^ cat 3
151− '/~/login?redirect='
152− url.request
153− ::
154− ++ redirect
155− |= redirect=cord
156− ^- simple-payload:http
157− [[307 ['location' redirect]~] ~]
158− --
159−--
zod/seax/mar/css.hoondeleted−25
@@ -1,25 +0,0 @@
1−::
2−:::: /hoon/css/mar
3− ::
4−/? 310
5−=, eyre
6−=, mimes:html
7−|_ mud=@t
8−++ grow :: convert to
9− |%
10− ++ mime [/text/css (as-octs mud)] :: convert to %mime
11− ++ hymn :: convert to %hymn
12− |^ html
13− ++ style ;style
14− ;- (trip mud)
15− ==
16− ++ html ;html:(head:"{style}" body)
17− --
18− --
19−++ grab
20− |% :: convert from
21− ++ mime |=([p=mite q=octs] (@t q.q))
22− ++ noun @t :: clam from %noun
23− --
24−++ grad %mime
25−--
zod/seax/mar/docket-0.hoondeleted−25
@@ -1,25 +0,0 @@
1−/+ dock=docket
2−|_ =docket:dock
3−++ grow
4− |%
5− ++ mime
6− ^- ^mime
7− [/text/x-docket (as-octt:mimes:html (spit-docket:mime:dock docket))]
8− ++ noun docket
9− ++ json (docket:enjs:dock docket)
10− --
11−++ grab
12− |%
13− ::
14− ++ mime
15− |= [=mite len=@ud tex=@]
16− ^- docket:dock
17− %- need
18− %- from-clauses:mime:dock
19− !<((list clause:dock) (slap !>(~) (ream tex)))
20−
21− ::
22− ++ noun docket:dock
23− --
24−++ grad %noun
25−--
zod/seax/mar/hoon.hoon+31−285
@@ -1,295 +1,41 @@
1+:::: /hoon/hoon/mar
2+ ::
3+/? 310
4+::
5+=, eyre
1|_ own=@t
6|_ own=@t
2::
7::
3++ grow :: convert to
8++ grow :: convert to
4 |%
9 |%
5 ++ mime `^mime`[/text/x-hoon (as-octs:mimes:html own)] :: convert to %mime
10 ++ mime `^mime`[/text/x-hoon (as-octs:mimes:html own)] :: convert to %mime
11+ ++ elem :: convert to %html
12+ ;div:pre(urb_codemirror "", mode "hoon"):"{(trip own)}"
13+ :: =+ gen-id="src-{<`@ui`(mug own)>}"
14+ :: ;div
15+ :: ;textarea(id "{gen-id}"):"{(trip own)}"
16+ :: ;script:"""
17+ :: CodeMirror.fromTextArea(
18+ :: window[{<gen-id>}],
19+ :: \{lineNumbers:true, readOnly:true}
20+ :: )
21+ :: """
22+ :: ==
6 ++ hymn
23 ++ hymn
7− ^- manx
24+ :: ;html:(head:title:"Source" "+{elem}")
8− =/ to-wall
25+ ;html
9− |= =tape
26+ ;head
10− ^- wall
27+ ;title:"Source"
11− %+ roll (flop tape)
28+ ;script@"//cdnjs.cloudflare.com/ajax/libs/codemirror/4.3.0/codemirror.js";
12− |= [char=@tD =wall]
29+ ;script@"/lib/syntax/hoon.js";
13− ?~ wall
30+ ;link(rel "stylesheet", href "//cdnjs.cloudflare.com/ajax/libs/".
14− [[char ~] ~]
31+ "codemirror/4.3.0/codemirror.min.css");
15− ?: =('\0a' char)
32+ ;link/"/lib/syntax/codemirror.css"(rel "stylesheet");
16− [~ wall]
33+ ==
17− [[char i.wall] t.wall]
34+ ;body
18− ::
19− =/ src=tape (trip own)
35+ ;textarea#src:"{(trip own)}"
20− =/ count=@ud (lent (to-wall src))
36+ ;script:'CodeMirror.fromTextArea(src, {lineNumbers:true, readOnly:true})'
21− =; [style=tape script=tape]
22− ;html
23− ;head
24− ;title: source
25− ;meta(charset "utf-8");
26− ;meta(name "viewport", content "width=device-width, initial-scale=1");
27− ;style: {style}
28− ==
29− ;body
30− ;div.viewer
31− :: ;div.viewer-bar
32− :: ;span.viewer-name: {(scow %ud count)} lines
33− :: ;button.copy-link(id "copy-link", hidden ""): copy link
34− :: ==
35− ;div.code
36− ;pre.gutter(id "gutter");
37− ;div.code-body
38− ;div(id "hl");
39− ;pre.code-pre(id "code"): {src}
40− ==
41− ==
42− ==
43− ;script: {script}
44− ==
45 ==
37 ==
46− ::
38+ ==
47− :-
48− ^~ ^= style
49− ^- tape
50− %- trip
51− '''
52− :root {
53− --bg: #fbfbf9;
54− --panel: #ffffff;
55− --gutter-bg: #f3f3ef;
56− --line: #e4e4dd;
57− --fg: #2b2b2b;
58− --fg-dim: #9a9a90;
59− --sel: #fff7d6;
60− --sel-edge: #f1e4a8;
61− --accent: #4a6da7;
62− --lh: 20px;
63− --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
64− "Liberation Mono", "Courier New", monospace;
65− }
66− * { box-sizing: border-box; }
67− html, body {
68− margin: 0;
69− height: 100%;
70− background: var(--bg);
71− color: var(--fg);
72− font-family: var(--mono);
73− -webkit-font-smoothing: antialiased;
74− }
75− .viewer {
76− height: 100vh;
77− display: flex;
78− flex-direction: column;
79− padding: 16px;
80− gap: 12px;
81− }
82− .viewer-bar {
83− flex: 0 0 auto;
84− display: flex;
85− align-items: center;
86− gap: 12px;
87− font-size: 13px;
88− color: var(--fg-dim);
89− }
90− .viewer-name {
91− flex: 1 1 auto;
92− min-width: 0;
93− overflow: hidden;
94− text-overflow: ellipsis;
95− white-space: nowrap;
96− color: var(--fg);
97− }
98− .copy-link {
99− flex: 0 0 auto;
100− padding: 4px 12px;
101− border: 1px solid var(--line);
102− border-radius: 6px;
103− background: var(--panel);
104− color: var(--fg-dim);
105− font: inherit;
106− font-size: 12px;
107− cursor: pointer;
108− transition: color .12s, border-color .12s;
109− }
110− .copy-link:hover { color: var(--accent); border-color: var(--accent); }
111− .copy-link[hidden] { display: none; }
112− .code {
113− flex: 1 1 auto;
114− min-height: 0;
115− display: flex;
116− align-items: flex-start;
117− overflow: auto;
118− background: var(--panel);
119− border: 1px solid var(--line);
120− border-radius: 8px;
121− }
122− .code-body {
123− position: relative;
124− flex: 1 1 auto;
125− min-width: 0;
126− width: max-content;
127− }
128− #gutter, #code {
129− margin: 0;
130− padding: 14px 0;
131− font-family: var(--mono);
132− font-size: 13px;
133− line-height: var(--lh);
134− white-space: pre;
135− tab-size: 2;
136− }
137− #gutter {
138− position: sticky;
139− left: 0;
140− z-index: 2;
141− flex: 0 0 auto;
142− padding-left: 16px;
143− padding-right: 12px;
144− text-align: right;
145− color: var(--fg-dim);
146− background: var(--gutter-bg);
147− border-right: 1px solid var(--line);
148− user-select: none;
149− -webkit-user-select: none;
150− cursor: pointer;
151− }
152− #code {
153− display: block;
154− padding-left: 14px;
155− padding-right: 18px;
156− color: var(--fg);
157− }
158− #hl {
159− position: absolute;
160− left: 0;
161− right: 0;
162− z-index: 0; /* under the text */
163− display: none;
164− background: var(--sel);
165− box-shadow: inset 2px 0 0 var(--sel-edge);
166− pointer-events: none;
167− }
168− #code { position: relative; z-index: 1; } /* text above the bar */
169− :focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
170− '''
171− ^~ ^= code
172− ^- tape
173− %- trip
174− '''
175− (function () {
176− "use strict";
177− const panel = document.querySelector(".code"); // the scroll container
178− const code = document.getElementById("code");
179− const gutter = document.getElementById("gutter");
180− const hl = document.getElementById("hl");
181− if (!panel || !code || !gutter || !hl) return;
182− const text = code.textContent;
183− let lineCount = text.length ? text.split("\n").length : 1;
184− if (text.endsWith("\n")) lineCount -= 1;
185− if (lineCount < 1) lineCount = 1;
186− (function fillGutter() {
187− let s = "";
188− for (let i = 1; i <= lineCount; i++) s += i + "\n";
189− gutter.textContent = s;
190− })();
191− let lineH = 0;
192− let codeTop = 0; // code's content-box top padding, the y-origin of line 1
193− function measure() {
194− const cs = getComputedStyle(code);
195− const lh = parseFloat(cs.lineHeight);
196− lineH = isNaN(lh) ? parseFloat(cs.fontSize) * 1.55 : lh;
197− codeTop = parseFloat(cs.paddingTop) || 0;
198− positionHighlight(); // keep the bar correct after a re-measure
199− }
200− let anchor = null;
201− let selLo = 0;
202− let selHi = 0;
203− function positionHighlight() {
204− if (!selLo) { hl.style.display = "none"; return; }
205− hl.style.display = "block";
206− hl.style.top = (codeTop + (selLo - 1) * lineH) + "px";
207− hl.style.height = ((selHi - selLo + 1) * lineH) + "px";
208− }
209− function applySel(lo, hi) {
210− selLo = lo;
211− selHi = hi;
212− positionHighlight();
213− }
214− function deselect() {
215− selLo = selHi = 0;
216− anchor = null;
217− positionHighlight();
218− history.replaceState(null, "", location.pathname + location.search);
219− }
220− function parseHash(hash) {
221− const m = /^#L(\d+)(?:-L(\d+))?$/.exec(hash || "");
222− if (!m) return null;
223− let lo = parseInt(m[1], 10);
224− let hi = m[2] ? parseInt(m[2], 10) : lo;
225− if (hi < lo) { const t = lo; lo = hi; hi = t; }
226− lo = Math.max(1, Math.min(lo, lineCount));
227− hi = Math.max(1, Math.min(hi, lineCount));
228− return [lo, hi];
229− }
230− function setHash(lo, hi) {
231− const frag = lo === hi ? "L" + lo : "L" + lo + "-L" + hi;
232− history.replaceState(null, "", "#" + frag);
233− }
234− function scrollToLine(n) {
235− const y = codeTop + (n - 1) * lineH;
236− if (y < panel.scrollTop || y > panel.scrollTop + panel.clientHeight) {
237− panel.scrollTop = Math.max(0, y - panel.clientHeight / 2);
238− }
239− }
240− function selectFromHash(scroll) {
241− const r = parseHash(location.hash);
242− if (!r) return false;
243− anchor = r[0];
244− applySel(r[0], r[1]);
245− if (scroll) scrollToLine(r[0]);
246− return true;
247− }
248− function lineFromEvent(e) {
249− // y within the code content: pointer relative to the code pre's rendered
250− // top, which already accounts for scroll since getBoundingClientRect is
251− // viewport-relative and the pre moves as the panel scrolls.
252− const rect = code.getBoundingClientRect();
253− const y = e.clientY - rect.top - codeTop;
254− let n = Math.floor(y / lineH) + 1;
255− if (n < 1) n = 1;
256− if (n > lineCount) n = lineCount;
257− return n;
258− }
259− function onClick(e) {
260− const inGutter = e.target === gutter;
261− if (!inGutter && !e.shiftKey) return; // normal text interaction in code
262− const n = lineFromEvent(e);
263− if (!e.shiftKey && selLo === n && selHi === n) {
264− deselect();
265− return;
266− }
267− if (e.shiftKey && anchor !== null) {
268− applySel(Math.min(anchor, n), Math.max(anchor, n));
269− setHash(selLo, selHi);
270− } else {
271− anchor = n;
272− applySel(n, n);
273− setHash(n, n);
274− }
275− }
276− gutter.addEventListener("click", onClick);
277− code.addEventListener("click", onClick);
278− document.addEventListener("keydown", function (e) {
279− if (e.key === "Escape") deselect();
280− });
281− window.addEventListener("hashchange", function () {
282− if (!selectFromHash(true)) deselect();
283− });
284− let rz = null;
285− window.addEventListener("resize", function () {
286− clearTimeout(rz);
287− rz = setTimeout(measure, 100);
288− });
289− measure();
290− selectFromHash(true);
291− })();
292− '''
293 ++ txt
39 ++ txt
294 (to-wain:format own)
40 (to-wain:format own)
295 --
41 --
zod/seax/mar/html.hoondeleted−22
@@ -1,22 +0,0 @@
1−::
2−:::: /hoon/html/mar
3− ::
4−/? 310
5− ::
6−:::: compute
7− ::
8−=, html
9−|_ htm=@t
10−++ grow :: convert to
11− ^?
12− |% ::
13− ++ mime [/text/html (met 3 htm) htm] :: to %mime
14− ++ hymn (need (de-xml htm)) :: to %hymn
15− -- ::
16−++ grab ^?
17− |% :: convert from
18− ++ noun @t :: clam from %noun
19− ++ mime |=([p=mite q=octs] q.q) :: retrieve form %mime
20− --
21−++ grad %mime
22−--
zod/seax/mar/js.hoondeleted−25
@@ -1,25 +0,0 @@
1−::
2−:::: /hoon/js/mar
3− ::
4−/? 310
5−::
6−=, eyre
7−|_ mud=@
8−++ grow
9− |%
10− ++ mime [/application/javascript (as-octs:mimes:html (@t mud))]
11− ++ hymn :: convert to %hymn
12− |^ html
13− ++ script ;script
14− ;- (trip (@t mud))
15− ==
16− ++ html ;html:(head:"{script}" body)
17− --
18− --
19−++ grab
20− |% :: convert from
21− ++ mime |=([p=mite q=octs] (@t q.q))
22− ++ noun cord :: clam from %noun
23− --
24−++ grad %mime
25−--
zod/seax/mar/png.hoondeleted−12
@@ -1,12 +0,0 @@
1−|_ dat=@
2−++ grow
3− |%
4− ++ mime [/image/png (as-octs:mimes:html dat)]
5− --
6−++ grab
7− |%
8− ++ mime |=([p=mite q=octs] q.q)
9− ++ noun @
10− --
11−++ grad %mime
12−--
zod/seax/mar/txt-diff.hoondeleted−16
@@ -1,16 +0,0 @@
1−::
2−:::: /hoon/txt-diff/mar
3− ::
4−/? 310
5−|_ txt-diff=(urge:clay cord)
6−::
7−++ grad %noun
8−++ grow
9− |%
10− ++ noun txt-diff
11− --
12−++ grab :: convert from
13− |%
14− ++ noun (urge:clay cord) :: make from %noun
15− --
16−--
zod/seax/mar/txt.hoon+1
@@ -18,6 +18,7 @@
18 => v=.
18 => v=.
19 |%
19 |%
20 ++ mime => v [/text/plain (as-octs (of-wain txt))]
20 ++ mime => v [/text/plain (as-octs (of-wain txt))]
21+ ++ elem => v ;pre: {(trip (of-wain txt))}
21 --
22 --
22++ grad
23++ grad
23 |%
24 |%
zod/seax/mar/woff2.hoondeleted−12
@@ -1,12 +0,0 @@
1−|_ dat=octs
2−++ grow
3− |%
4− ++ mime [/font/woff2 dat]
5− --
6−++ grab
7− |%
8− ++ mime |=([=mite =octs] octs)
9− ++ noun octs
10− --
11−++ grad %mime
12−--
zod/seax/sur/docket.hoondeleted−82
@@ -1,82 +0,0 @@
1−|%
2−::
3−+$ version
4− [major=@ud minor=@ud patch=@ud]
5−::
6−+$ glob (map path mime)
7−::
8−+$ url cord
9−:: $glob-location: How to retrieve a glob
10−::
11−+$ glob-reference
12− [hash=@uvH location=glob-location]
13−::
14−+$ glob-location
15− $% [%http =url]
16− [%ames =ship]
17− ==
18−:: $href: Where a tile links to
19−::
20−+$ href
21− $% [%glob base=term =glob-reference]
22− [%site =path]
23− ==
24−:: $chad: State of a docket
25−::
26−+$ chad
27− $~ [%install ~]
28− $% :: Done
29− [%glob =glob]
30− [%site ~]
31− :: Waiting
32− [%install ~]
33− [%suspend glob=(unit glob)]
34− :: Error
35− [%hung err=cord]
36− ==
37−::
38−:: $charge: A realized $docket
39−::
40−+$ charge
41− $: =docket
42− =chad
43− ==
44−::
45−:: $clause: A key and value, as part of a docket
46−::
47−:: Only used to parse $docket
48−::
49−+$ clause
50− $% [%title title=@t]
51− [%info info=@t]
52− [%color color=@ux]
53− [%glob-http url=cord hash=@uvH]
54− [%glob-ames =ship hash=@uvH]
55− [%image =url]
56− [%site =path]
57− [%base base=term]
58− [%version =version]
59− [%website website=url]
60− [%license license=cord]
61− ==
62−::
63−:: $docket: A description of JS bundles for a desk
64−::
65−+$ docket
66− $: %1
67− title=@t
68− info=@t
69− color=@ux
70− =href
71− image=(unit url)
72− =version
73− website=url
74− license=cord
75− ==
76−::
77−+$ charge-update
78− $% [%initial initial=(map desk charge)]
79− [%add-charge =desk =charge]
80− [%del-charge =desk]
81− ==
82−--
zod/seax/sur/seax.hoon+1−10
@@ -7,17 +7,8 @@
7++ engine
7++ engine
8 $_ ^|
8 $_ ^|
9 |%
9 |%
10− ++ request |~ tape *request:http
10+ ++ url |~ tape *tape
11 ++ results |~ mime-data:iris *(unit (list search-result))
11 ++ results |~ mime-data:iris *(unit (list search-result))
12 --
12 --
13+$ engines (map name=term [=engine weight=@rs])
13+$ engines (map name=term [=engine weight=@rs])
14−:: +get: a GET request to url with the query appended
15−::
16−:: The runtime unescapes the query string once before sending the
17−:: request, so the query is escaped twice to stay escaped on the wire.
18−::
19−++ get
20− |= [url=tape query=tape]
21− ^- request:http
22− [%'GET' (crip (weld url (en-urlt:html (en-urlt:html query)))) ~ ~]
23--
14--
zod/seax/sys.kelvin+1−1
@@ -1 +1 @@
1−[%zuse 408]
1+[%zuse 413]