on main help sign in
improve search estimation, include peers, and document search feature more fully
commit3174b36791fe9f08d90cb0785379cf1bc9b36149 authorJohn Hyde <johnhydefour@gmail.com> dateFri 17 Jun 2022, 14:34 (4 years ago) parente9137b1
11 files changed+149−111
desk/app/astrolabe.hoon+5−2 desk/desk.docket-0+2−2 desk/doc/usr/astrolabe.udon+28 desk/lib/search.hoon+29−21 ui/src/components/HomeScreenTiles.svelte+5−5 ui/src/components/SearchBar.svelte+1−1 ui/src/lib/api.ts+14−20 ui/src/stores/searchStores.ts+14−5 ui/src/stores/store.ts+7−1 ui/src/styles/tailwind-output.css+43−54 ui/src/types/store.ts+1
desk/app/astrolabe.hoon+5−2
@@ -61,6 +61,9 @@
6161 =/ =search-text (trip &3.path)
6262 =/ expand-search =(t.t.t.path ~)
6363 ``astrolabe-point-set+!>((search-opoints:hc search-text expand-search))
64+ [%x %peers ~]
65+ =/ peers .^((map ship ?(%alien %known)) %ax (scrio:hc %$ /peers))
66+ ``astrolabe-point-set+!>((turn ~(tap in peers) head))
6467 [%x %doc ^]
6568 =/ doc-path=^path t.t.path
6669 =/ doc (read-doc:hc doc-path)
@@ -222,7 +225,7 @@
222225 ?~ points
223226 :: ~& "nothing here, going up"
224227 res
225− ?: (gth count.res 1.000) ~|(%search-too-broad !!)
228+ :: ?: (gth count.res 1.000) ~|(%search-too-broad !!)
226229 =/ n=@q `@`-.n.points
227230 =/ lrb=@q ?: (gth n 0) (sub-d n 1) .~zod
228231 =/ rlb=@q (add-d n 1)
@@ -264,7 +267,7 @@
264267 (search-text-to-fulls search-text)
265268 ~[(search-text-to-full search-text)]
266269 :: ~& (turn search-fulls |=(=search-full search-syls.search-full))
267− =/ mr-searches (maybe-reverse-searches search-fulls 2.820)
270+ =/ mr-searches (maybe-reverse-searches search-fulls 1.000)
268271 :: ~& (turn mr-searches |=(=mr-search pain.mr-search))
269272 :: ~& mr-searches
270273 ?~ mr-searches ~|(%search-predicted-too-broad !!)
desk/desk.docket-0+2−2
@@ -2,9 +2,9 @@
22 info+'Your guide to the Urbit constellations'
33 color+0x1f.243c
44 image+'https://raw.githubusercontent.com/johnhyde/astrolabe/main/ui/src/assets/astrolabe-tile.svg'
5− glob-ames+[~zod 0v0]
5+ glob-ames+[~dister-midlev-mindyr 0v0]
66 base+'astrolabe'
7− version+[0 1 0]
7+ version+[0 1 1]
88 website+'https://github.com/johnhyde/astrolabe'
99 license+'MIT'
1010==
desk/doc/usr/astrolabe.udon+28
@@ -1,6 +1,7 @@
11;>
22# Astrolabe
33
4+Astrolabe is your one-stop shop for exploring Urbit address space.
45Currently, Astrolabe lets you type in `@p`s or Azimuth point numbers
56to look up ships on the network. You can search your contacts and
67spawned ships with partial or wildcard searches, e.g. `sampel-***net`.
@@ -8,11 +9,38 @@ spawned ships with partial or wildcard searches, e.g. `sampel-***net`.
89There's also Help Documentation if
910you want to learn more about how Urbit IDs work.
1011
12+## How to Use Search
13+
14+When you type part of a `@p` into the search bar, all your contacts and peers will be
15+searched for IDs matching that string. You can use an asterisk (`*`) to
16+stand in for any character. If you wanted to search for both `~samnec` and `~sarbec`,
17+you could type `~sa**ec`.
18+Contacts are listed before other ships, with a gold border on the left.
19+
20+You can exclude moons or include comets you've met with the toggles in the gold
21+settings box under the search bar.
22+
23+The records of all spawned ships will also be searched, but if the search is
24+too general it will be narrowed in scope, or not run at all. For example,
25+if you search for `***hec`, it will search for all stars under `~hec`,
26+and all planets that match `~***hec-******`, but planets matching `~******-***hec`
27+will be ignored to prevent the search from taking too long.
28+
29+## Upcoming Features
30+
31+In addition to improving the search feature over time, Astrolabe will one day feature:
32+- a Star Chart, where you can visually see the layout of the network and explore freely.
33+- Sigil Search, with which you can select the shapes of a sigil and search for ships
34+ that match.
35+- Integrations with Groups, `%pals`, `%gora`, and whatever else seems fun and/or useful.
36+
1137## Info
1238
1339- Developer: ~midlev-mindyr
1440- License: MIT
1541- App link: [~dister-midlev-mindyr/astrolabe](web+urbitgraph://~dister-midlev-mindyr/astrolabe)
42+- Discussion Group: [~poster-hoster-midlev-mindyr/astrolabe](web+urbitgraph://group/~poster-hoster-midlev-mindyr/astrolabe)
43+- Grant/Proposal: [https://urbit.org/grants/astrolabe](https://urbit.org/grants/astrolabe)
1644- Source repo: [https://github.com/johnhyde/astrolabe](https://github.com/johnhyde/astrolabe)
1745- Bug reports: [https://github.com/johnhyde/astrolabe/issues](https://github.com/johnhyde/astrolabe/issues)
1846
desk/lib/search.hoon+29−21
@@ -146,35 +146,43 @@
146146 ^- search-full
147147 :- (reverse-list (pad-search-syls search-syls.search))
148148 (reverse-list search-opts.search)
149+::
149150++ estimate-search-pain
150− |= search=search-full
151+ |= [search=search-full reversed=?]
152+ ^- @
151153 =/ opts search-opts.search
152− =| acc=@
153− |-
154− ?~ opts acc
155− =/ new-acc
156− (add (mul acc 2) (lent i.opts))
157− $(acc new-acc, opts t.opts)
158−:: ++ estimate-search-pain
159−:: |= search=search-full
160−:: =/ max-pain (bex 32)
161−:: =/ opts search-opts.search
162−:: =| acc=@
163−:: |-
164−:: ?~ opts acc
165−:: =/ level-pain (div (mul max-pain (lent i.opts)) 256)
166−:: =/ new-acc
167−:: (add (mul acc 2) (lent i.opts))
168−:: $(acc new-acc, opts t.opts)
154+ =/ layer-wyt=@rs .255
155+ =, rs
156+ =/ pain
157+ %- :(corl abs:si need toi)
158+ |-
159+ ^- @rs
160+ ?~ opts .0
161+ =/ layer-dropoff
162+ ?: &(reversed =(i.opts `search-syl-opts`~[.~zod]))
163+ .0.99
164+ .0.1694
165+ =/ num-opts (sun (lent i.opts))
166+ =/ cen-opts (div num-opts .255)
167+ =/ num-res (mul cen-opts layer-wyt)
168+ =/ ops (mul (sqt cen-opts) layer-wyt)
169+ %^ fma num-res
170+ %= $
171+ opts t.opts
172+ layer-wyt (mul layer-wyt layer-dropoff)
173+ ==
174+ ops
175+ :: ~& "pain for {<search-syls.search>}: {<pain>}"
176+ pain
169177++ estimate-search-text-pain
170178 |= =search-text
171− (estimate-search-pain (search-text-to-full search-text))
179+ (estimate-search-pain (search-text-to-full search-text) |)
172180++ maybe-reverse-search
173181 |= [search=search-full]
174182 ^- mr-search
175− =/ search-pain (estimate-search-pain search)
183+ =/ search-pain (estimate-search-pain search |)
176184 =/ rsearch (reverse-search search)
177− =/ rsearch-pain (estimate-search-pain rsearch)
185+ =/ rsearch-pain (estimate-search-pain rsearch &)
178186 ?: (gth search-pain rsearch-pain)
179187 [[%.y rsearch-pain] rsearch]
180188 [[%.n search-pain] search]
ui/src/components/HomeScreenTiles.svelte+5−5
@@ -17,17 +17,17 @@
1717
1818<div class="pt-10 max-w-md w-full flex flex-wrap gap-5 justify-center">
1919 <div class="rounded-lg bg-white flex-grow max-w-xs">
20− <DocLink modal path="">
20+ <DocLink modal path="astrolabe">
2121 <div class="p-5 text-xl text-center">
22− Help Docs
22+ About Astrolabe
2323 </div>
2424 </DocLink>
2525 </div>
26− <!-- <div class="rounded-lg bg-white flex-grow max-w-xs">
26+ <div class="rounded-lg bg-white flex-grow max-w-xs">
2727 <DocLink modal path="">
2828 <div class="p-5 text-xl text-center">
29− Help Docs
29+ About Urbit IDs
3030 </div>
3131 </DocLink>
32− </div> -->
32+ </div>
3333</div>
ui/src/components/SearchBar.svelte+1−1
@@ -8,7 +8,7 @@
88 export let search = '';
99 let debouncedSearch = search;
1010
11− const placeholderText = "Search by @p or #. '*' is wildcard";
11+ const placeholderText = "Search by @p or #. '*' matches any character";
1212
1313 $: {
1414 analysis = new SearchAnalysis(debouncedSearch);
ui/src/lib/api.ts+14−20
@@ -10,28 +10,32 @@ const api: UrbitApi = new UrbitApi('', '', window.desk);
1010api.ship = window.ship;
1111// api.connect();
1212
13−function scry<T = any>(path: string): Promise<T> {
13+export function scry<T = any>(path: string): Promise<T> {
1414 return api.scry<T>({ app: 'astrolabe', path });
1515}
1616
17−function getPoint(patp: string): Promise<any> {
17+export function getPoint(patp: string): Promise<any> {
1818 return scry<any>(`/point/${patp}`);
1919}
2020
21−function getSpawnedPoints(patp: string): Promise<any> {
21+export function getSpawnedPoints(patp: string): Promise<any> {
2222 return scry<any>(`/point/${patp}/spawned`);
2323}
2424
25−async function searchPoints(search: string): Promise<Patp[]> {
25+export function getPeers(): Promise<any> {
26+ return scry<any>(`/peers`);
27+}
28+
29+export async function searchPoints(search: string): Promise<Patp[]> {
2630 const { points } = await scry<any>(`/search/${search}`);
2731 return points.map(normalizeId);
2832}
2933
30−function getDoc(path: string): Promise<string> {
34+export function getDoc(path: string): Promise<string> {
3135 return scry<string>(`/doc/${path}`);
3236}
3337
34−function initialContacts(json: ContactUpdate, contacts: Rolodex): Rolodex {
38+export function initialContacts(json: ContactUpdate, contacts: Rolodex): Rolodex {
3539 const data = _get(json, 'initial', false);
3640 if (data) {
3741 contacts = data.rolodex;
@@ -39,7 +43,7 @@ function initialContacts(json: ContactUpdate, contacts: Rolodex): Rolodex {
3943 return contacts;
4044};
4145
42−function addContact(json: ContactUpdate, contacts: Rolodex): Rolodex {
46+export function addContact(json: ContactUpdate, contacts: Rolodex): Rolodex {
4347 const data = _get(json, 'add', false);
4448 if (data) {
4549 contacts[data.ship] = data.contact;
@@ -47,7 +51,7 @@ function addContact(json: ContactUpdate, contacts: Rolodex): Rolodex {
4751 return contacts;
4852};
4953
50−function removeContact(json: ContactUpdate, contacts: Rolodex): Rolodex {
54+export function removeContact(json: ContactUpdate, contacts: Rolodex): Rolodex {
5155 const data = _get(json, 'remove', false);
5256 if (
5357 data &&
@@ -58,7 +62,7 @@ function removeContact(json: ContactUpdate, contacts: Rolodex): Rolodex {
5862 return contacts;
5963};
6064
61−function handleContactUpdateEvent(updateContacts) {
65+export function handleContactUpdateEvent(updateContacts) {
6266 return ({ 'contact-update': contactUpdate }) => {
6367 console.log(`received contact-update: ${Object.keys(contactUpdate)}`)
6468 const reducers = [initialContacts, addContact, removeContact];
@@ -67,7 +71,7 @@ function handleContactUpdateEvent(updateContacts) {
6771 };
6872}
6973
70−function subscribeToContacts(e: (data: any) => void): Promise<any> {
74+export function subscribeToContacts(e: (data: any) => void): Promise<any> {
7175 return api.subscribe({
7276 app: 'contact-store',
7377 path: '/all',
@@ -80,13 +84,3 @@ function subscribeToContacts(e: (data: any) => void): Promise<any> {
8084 }
8185 })
8286}
83−
84−export {
85− api,
86− scry,
87− getPoint,
88− getSpawnedPoints,
89− searchPoints,
90− getDoc,
91− subscribeToContacts,
92−};
ui/src/stores/searchStores.ts+14−5
@@ -1,6 +1,6 @@
11import { writable, derived } from 'svelte/store';
22
3−import type { Rolodex } from '@urbit/api';
3+import type { Contact, Rolodex } from '@urbit/api';
44import store from './store';
55import type { StoreState, SearchedContactsState } from '../types/store';
66import { SearchSettings } from '../types/store';
@@ -15,16 +15,25 @@ function deriveSearchedContacts([$store, $searchSettings]): SearchedContactsStat
1515 if (!$store.query) {
1616 return {};
1717 }
18− const filteredContacts = filterObject($store.contacts, (id) => {
18+ function idMatchesSearch(id) {
1919 if (!$store.query.test(id)) return false;
20− if (!$searchSettings.includeMoons && id.length > 16 && id.length <= 32) {
20+ if (!$searchSettings.includeMoons && id.length > 14 && id.length <= 28) {
2121 return false;
2222 }
23− if (!$searchSettings.includeComets && id.length > 32) {
23+ if (!$searchSettings.includeComets && id.length > 28) {
2424 return false;
2525 }
2626 return true;
27− }) as Rolodex;
27+ }
28+ const filteredContacts = filterObject($store.contacts, idMatchesSearch) as Rolodex;
29+ $store.peers.forEach((id) => {
30+ if (idMatchesSearch(id) && !filteredContacts[id]) {
31+ filteredContacts[id] = {
32+ nickname: null, bio: null, status: null, color: null, avatar: null,
33+ cover: null, groups: [], 'last-updated': 0,
34+ } as Contact
35+ }
36+ })
2837 return filteredContacts;
2938}
3039
ui/src/stores/store.ts+7−1
@@ -1,12 +1,14 @@
11import { writable } from 'svelte/store';
22
33import type { Rolodex } from '@urbit/api';
4−import { subscribeToContacts } from '../lib/api';
4+import { subscribeToContacts, getPeers } from '../lib/api';
55import type { StoreState } from '../types/store';
6+import { normalizeId } from '../lib/id';
67import { setStoreKey } from '../lib/utils';
78
89const initStore: StoreState = {
910 contacts: {},
11+ peers: {},
1012 query: null,
1113 connection: 'disconnected',
1214 ship: window.ship,
@@ -34,4 +36,8 @@ function updateContacts(callback: (contacts: Rolodex) => Rolodex): void {
3436
3537subscribeToContacts(updateContacts);
3638
39+getPeers().then(({ points }) => {
40+ setStoreKey(store, 'peers', points.map(normalizeId));
41+});
42+
3743export default { subscribe, set, search, reset };
ui/src/styles/tailwind-output.css+43−54
@@ -711,14 +711,14 @@ Ensure the default browser behavior of the `hidden` attribute.
711711.gap-1 {
712712 gap: 0.25rem;
713713}
714−.gap-x-3 {
715− -moz-column-gap: 0.75rem;
716− column-gap: 0.75rem;
717−}
718714.gap-x-2 {
719715 -moz-column-gap: 0.5rem;
720716 column-gap: 0.5rem;
721717}
718+.gap-x-3 {
719+ -moz-column-gap: 0.75rem;
720+ column-gap: 0.75rem;
721+}
722722.gap-x-4 {
723723 -moz-column-gap: 1rem;
724724 column-gap: 1rem;
@@ -806,26 +806,18 @@ Ensure the default browser behavior of the `hidden` attribute.
806806.border-8 {
807807 border-width: 8px;
808808}
809−.border-b {
810− border-bottom-width: 1px;
809+.border-l-8 {
810+ border-left-width: 8px;
811811}
812812.border-t {
813813 border-top-width: 1px;
814814}
815+.border-b {
816+ border-bottom-width: 1px;
817+}
815818.border-l {
816819 border-left-width: 1px;
817820}
818−.border-l-8 {
819− border-left-width: 8px;
820−}
821−.border-white {
822− --tw-border-opacity: 1;
823− border-color: rgb(255 255 255 / var(--tw-border-opacity));
824−}
825−.border-black {
826− --tw-border-opacity: 1;
827− border-color: rgb(0 0 0 / var(--tw-border-opacity));
828−}
829821.border-navy {
830822 --tw-border-opacity: 1;
831823 border-color: rgb(31 36 60 / var(--tw-border-opacity));
@@ -838,6 +830,14 @@ Ensure the default browser behavior of the `hidden` attribute.
838830 --tw-border-opacity: 1;
839831 border-color: rgb(209 213 219 / var(--tw-border-opacity));
840832}
833+.border-white {
834+ --tw-border-opacity: 1;
835+ border-color: rgb(255 255 255 / var(--tw-border-opacity));
836+}
837+.border-black {
838+ --tw-border-opacity: 1;
839+ border-color: rgb(0 0 0 / var(--tw-border-opacity));
840+}
841841.border-gray-400 {
842842 --tw-border-opacity: 1;
843843 border-color: rgb(156 163 175 / var(--tw-border-opacity));
@@ -858,14 +858,14 @@ Ensure the default browser behavior of the `hidden` attribute.
858858 --tw-bg-opacity: 1;
859859 background-color: rgb(0 0 0 / var(--tw-bg-opacity));
860860}
861−.bg-gray-300 {
862− --tw-bg-opacity: 1;
863− background-color: rgb(209 213 219 / var(--tw-bg-opacity));
864−}
865861.bg-orange-gold {
866862 --tw-bg-opacity: 1;
867863 background-color: rgb(247 211 146 / var(--tw-bg-opacity));
868864}
865+.bg-gray-300 {
866+ --tw-bg-opacity: 1;
867+ background-color: rgb(209 213 219 / var(--tw-bg-opacity));
868+}
869869.bg-amber-300 {
870870 --tw-bg-opacity: 1;
871871 background-color: rgb(252 211 77 / var(--tw-bg-opacity));
@@ -940,18 +940,18 @@ Ensure the default browser behavior of the `hidden` attribute.
940940.p-4 {
941941 padding: 1rem;
942942}
943−.p-2 {
944− padding: 0.5rem;
945−}
946−.p-6 {
947− padding: 1.5rem;
948−}
949943.p-8 {
950944 padding: 2rem;
951945}
952946.p-3 {
953947 padding: 0.75rem;
954948}
949+.p-2 {
950+ padding: 0.5rem;
951+}
952+.p-6 {
953+ padding: 1.5rem;
954+}
955955.p-1 {
956956 padding: 0.25rem;
957957}
@@ -975,6 +975,22 @@ Ensure the default browser behavior of the `hidden` attribute.
975975 padding-top: 0.5rem;
976976 padding-bottom: 0.5rem;
977977}
978+.px-6 {
979+ padding-left: 1.5rem;
980+ padding-right: 1.5rem;
981+}
982+.px-4 {
983+ padding-left: 1rem;
984+ padding-right: 1rem;
985+}
986+.py-8 {
987+ padding-top: 2rem;
988+ padding-bottom: 2rem;
989+}
990+.px-8 {
991+ padding-left: 2rem;
992+ padding-right: 2rem;
993+}
978994.px-0\.5 {
979995 padding-left: 0.125rem;
980996 padding-right: 0.125rem;
@@ -995,22 +1011,6 @@ Ensure the default browser behavior of the `hidden` attribute.
9951011 padding-left: 0.25rem;
9961012 padding-right: 0.25rem;
9971013}
998−.px-8 {
999− padding-left: 2rem;
1000− padding-right: 2rem;
1001−}
1002−.px-4 {
1003− padding-left: 1rem;
1004− padding-right: 1rem;
1005−}
1006−.py-8 {
1007− padding-top: 2rem;
1008− padding-bottom: 2rem;
1009−}
1010−.px-6 {
1011− padding-left: 1.5rem;
1012− padding-right: 1.5rem;
1013−}
10141014.pt-10 {
10151015 padding-top: 2.5rem;
10161016}
@@ -1163,17 +1163,6 @@ Ensure the default browser behavior of the `hidden` attribute.
11631163 transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
11641164 transition-duration: 150ms;
11651165}
1166−.last\:border-none:last-child {
1167− border-style: none;
1168−}
1169−.hover\:bg-gray-300:hover {
1170− --tw-bg-opacity: 1;
1171− background-color: rgb(209 213 219 / var(--tw-bg-opacity));
1172−}
1173−.hover\:bg-gray-400:hover {
1174− --tw-bg-opacity: 1;
1175− background-color: rgb(156 163 175 / var(--tw-bg-opacity));
1176−}
11771166.hover\:bg-gray-200:hover {
11781167 --tw-bg-opacity: 1;
11791168 background-color: rgb(229 231 235 / var(--tw-bg-opacity));
ui/src/types/store.ts+1
@@ -13,6 +13,7 @@ export interface StoreState {
1313 // [group: string]: Group;
1414 // };
1515 contacts: Rolodex;
16+ peers: Patp[];
1617 query: RegExp;
1718 // contacts: Contacts;
1819 // contactSearchResults: Contacts;