on main help sign in
improve search estimation, include peers, and document search feature more fully
3174b36John Hyde
desk/app/astrolabe.hoon+5−2
@@ -61,6 +61,9 @@
61 =/ =search-text (trip &3.path)
61 =/ =search-text (trip &3.path)
62 =/ expand-search =(t.t.t.path ~)
62 =/ expand-search =(t.t.t.path ~)
63 ``astrolabe-point-set+!>((search-opoints:hc search-text expand-search))
63 ``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))
64 [%x %doc ^]
67 [%x %doc ^]
65 =/ doc-path=^path t.t.path
68 =/ doc-path=^path t.t.path
66 =/ doc (read-doc:hc doc-path)
69 =/ doc (read-doc:hc doc-path)
@@ -222,7 +225,7 @@
222 ?~ points
225 ?~ points
223 :: ~& "nothing here, going up"
226 :: ~& "nothing here, going up"
224 res
227 res
225− ?: (gth count.res 1.000) ~|(%search-too-broad !!)
228+ :: ?: (gth count.res 1.000) ~|(%search-too-broad !!)
226 =/ n=@q `@`-.n.points
229 =/ n=@q `@`-.n.points
227 =/ lrb=@q ?: (gth n 0) (sub-d n 1) .~zod
230 =/ lrb=@q ?: (gth n 0) (sub-d n 1) .~zod
228 =/ rlb=@q (add-d n 1)
231 =/ rlb=@q (add-d n 1)
@@ -264,7 +267,7 @@
264 (search-text-to-fulls search-text)
267 (search-text-to-fulls search-text)
265 ~[(search-text-to-full search-text)]
268 ~[(search-text-to-full search-text)]
266 :: ~& (turn search-fulls |=(=search-full search-syls.search-full))
269 :: ~& (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)
268 :: ~& (turn mr-searches |=(=mr-search pain.mr-search))
271 :: ~& (turn mr-searches |=(=mr-search pain.mr-search))
269 :: ~& mr-searches
272 :: ~& mr-searches
270 ?~ mr-searches ~|(%search-predicted-too-broad !!)
273 ?~ mr-searches ~|(%search-predicted-too-broad !!)
desk/desk.docket-0+2−2
@@ -2,9 +2,9 @@
2 info+'Your guide to the Urbit constellations'
2 info+'Your guide to the Urbit constellations'
3 color+0x1f.243c
3 color+0x1f.243c
4 image+'https://raw.githubusercontent.com/johnhyde/astrolabe/main/ui/src/assets/astrolabe-tile.svg'
4 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]
6 base+'astrolabe'
6 base+'astrolabe'
7− version+[0 1 0]
7+ version+[0 1 1]
8 website+'https://github.com/johnhyde/astrolabe'
8 website+'https://github.com/johnhyde/astrolabe'
9 license+'MIT'
9 license+'MIT'
10==
10==
desk/doc/usr/astrolabe.udon+28
@@ -1,6 +1,7 @@
1;>
1;>
2# Astrolabe
2# Astrolabe
3
3
4+Astrolabe is your one-stop shop for exploring Urbit address space.
4Currently, Astrolabe lets you type in `@p`s or Azimuth point numbers
5Currently, Astrolabe lets you type in `@p`s or Azimuth point numbers
5to look up ships on the network. You can search your contacts and
6to look up ships on the network. You can search your contacts and
6spawned ships with partial or wildcard searches, e.g. `sampel-***net`.
7spawned 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`.
8There's also Help Documentation if
9There's also Help Documentation if
9you want to learn more about how Urbit IDs work.
10you want to learn more about how Urbit IDs work.
10
11
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+
11## Info
37## Info
12
38
13- Developer: ~midlev-mindyr
39- Developer: ~midlev-mindyr
14- License: MIT
40- License: MIT
15- App link: [~dister-midlev-mindyr/astrolabe](web+urbitgraph://~dister-midlev-mindyr/astrolabe)
41- 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)
16- Source repo: [https://github.com/johnhyde/astrolabe](https://github.com/johnhyde/astrolabe)
44- Source repo: [https://github.com/johnhyde/astrolabe](https://github.com/johnhyde/astrolabe)
17- Bug reports: [https://github.com/johnhyde/astrolabe/issues](https://github.com/johnhyde/astrolabe/issues)
45- Bug reports: [https://github.com/johnhyde/astrolabe/issues](https://github.com/johnhyde/astrolabe/issues)
18
46
desk/lib/search.hoon+29−21
@@ -146,35 +146,43 @@
146 ^- search-full
146 ^- search-full
147 :- (reverse-list (pad-search-syls search-syls.search))
147 :- (reverse-list (pad-search-syls search-syls.search))
148 (reverse-list search-opts.search)
148 (reverse-list search-opts.search)
149+::
149++ estimate-search-pain
150++ estimate-search-pain
150− |= search=search-full
151+ |= [search=search-full reversed=?]
152+ ^- @
151 =/ opts search-opts.search
153 =/ opts search-opts.search
152− =| acc=@
154+ =/ layer-wyt=@rs .255
153− |-
155+ =, rs
154− ?~ opts acc
156+ =/ pain
155− =/ new-acc
157+ %- :(corl abs:si need toi)
156− (add (mul acc 2) (lent i.opts))
158+ |-
157− $(acc new-acc, opts t.opts)
159+ ^- @rs
158−:: ++ estimate-search-pain
160+ ?~ opts .0
159−:: |= search=search-full
161+ =/ layer-dropoff
160−:: =/ max-pain (bex 32)
161−:: =/ opts search-opts.search
162+ ?: &(reversed =(i.opts `search-syl-opts`~[.~zod]))
162−:: =| acc=@
163+ .0.99
163−:: |-
164+ .0.1694
164−:: ?~ opts acc
165+ =/ num-opts (sun (lent i.opts))
165−:: =/ level-pain (div (mul max-pain (lent i.opts)) 256)
166+ =/ cen-opts (div num-opts .255)
166−:: =/ new-acc
167+ =/ num-res (mul cen-opts layer-wyt)
167−:: (add (mul acc 2) (lent i.opts))
168+ =/ ops (mul (sqt cen-opts) layer-wyt)
168−:: $(acc new-acc, opts t.opts)
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
169++ estimate-search-text-pain
177++ estimate-search-text-pain
170 |= =search-text
178 |= =search-text
171− (estimate-search-pain (search-text-to-full search-text))
179+ (estimate-search-pain (search-text-to-full search-text) |)
172++ maybe-reverse-search
180++ maybe-reverse-search
173 |= [search=search-full]
181 |= [search=search-full]
174 ^- mr-search
182 ^- mr-search
175− =/ search-pain (estimate-search-pain search)
183+ =/ search-pain (estimate-search-pain search |)
176 =/ rsearch (reverse-search search)
184 =/ rsearch (reverse-search search)
177− =/ rsearch-pain (estimate-search-pain rsearch)
185+ =/ rsearch-pain (estimate-search-pain rsearch &)
178 ?: (gth search-pain rsearch-pain)
186 ?: (gth search-pain rsearch-pain)
179 [[%.y rsearch-pain] rsearch]
187 [[%.y rsearch-pain] rsearch]
180 [[%.n search-pain] search]
188 [[%.n search-pain] search]
ui/src/components/HomeScreenTiles.svelte+5−5
@@ -17,17 +17,17 @@
17
17
18<div class="pt-10 max-w-md w-full flex flex-wrap gap-5 justify-center">
18<div class="pt-10 max-w-md w-full flex flex-wrap gap-5 justify-center">
19 <div class="rounded-lg bg-white flex-grow max-w-xs">
19 <div class="rounded-lg bg-white flex-grow max-w-xs">
20− <DocLink modal path="">
20+ <DocLink modal path="astrolabe">
21 <div class="p-5 text-xl text-center">
21 <div class="p-5 text-xl text-center">
22− Help Docs
22+ About Astrolabe
23 </div>
23 </div>
24 </DocLink>
24 </DocLink>
25 </div>
25 </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">
27 <DocLink modal path="">
27 <DocLink modal path="">
28 <div class="p-5 text-xl text-center">
28 <div class="p-5 text-xl text-center">
29− Help Docs
29+ About Urbit IDs
30 </div>
30 </div>
31 </DocLink>
31 </DocLink>
32− </div> -->
32+ </div>
33</div>
33</div>
ui/src/components/SearchBar.svelte+1−1
@@ -8,7 +8,7 @@
8 export let search = '';
8 export let search = '';
9 let debouncedSearch = search;
9 let debouncedSearch = search;
10
10
11− const placeholderText = "Search by @p or #. '*' is wildcard";
11+ const placeholderText = "Search by @p or #. '*' matches any character";
12
12
13 $: {
13 $: {
14 analysis = new SearchAnalysis(debouncedSearch);
14 analysis = new SearchAnalysis(debouncedSearch);
ui/src/lib/api.ts+14−20
@@ -10,28 +10,32 @@ const api: UrbitApi = new UrbitApi('', '', window.desk);
10api.ship = window.ship;
10api.ship = window.ship;
11// api.connect();
11// api.connect();
12
12
13−function scry<T = any>(path: string): Promise<T> {
13+export function scry<T = any>(path: string): Promise<T> {
14 return api.scry<T>({ app: 'astrolabe', path });
14 return api.scry<T>({ app: 'astrolabe', path });
15}
15}
16
16
17−function getPoint(patp: string): Promise<any> {
17+export function getPoint(patp: string): Promise<any> {
18 return scry<any>(`/point/${patp}`);
18 return scry<any>(`/point/${patp}`);
19}
19}
20
20
21−function getSpawnedPoints(patp: string): Promise<any> {
21+export function getSpawnedPoints(patp: string): Promise<any> {
22 return scry<any>(`/point/${patp}/spawned`);
22 return scry<any>(`/point/${patp}/spawned`);
23}
23}
24
24
25+export function getPeers(): Promise<any> {
26+ return scry<any>(`/peers`);
27+}
28+
25−async function searchPoints(search: string): Promise<Patp[]> {
29+export async function searchPoints(search: string): Promise<Patp[]> {
26 const { points } = await scry<any>(`/search/${search}`);
30 const { points } = await scry<any>(`/search/${search}`);
27 return points.map(normalizeId);
31 return points.map(normalizeId);
28}
32}
29
33
30−function getDoc(path: string): Promise<string> {
34+export function getDoc(path: string): Promise<string> {
31 return scry<string>(`/doc/${path}`);
35 return scry<string>(`/doc/${path}`);
32}
36}
33
37
34−function initialContacts(json: ContactUpdate, contacts: Rolodex): Rolodex {
38+export function initialContacts(json: ContactUpdate, contacts: Rolodex): Rolodex {
35 const data = _get(json, 'initial', false);
39 const data = _get(json, 'initial', false);
36 if (data) {
40 if (data) {
37 contacts = data.rolodex;
41 contacts = data.rolodex;
@@ -39,7 +43,7 @@ function initialContacts(json: ContactUpdate, contacts: Rolodex): Rolodex {
39 return contacts;
43 return contacts;
40};
44};
41
45
42−function addContact(json: ContactUpdate, contacts: Rolodex): Rolodex {
46+export function addContact(json: ContactUpdate, contacts: Rolodex): Rolodex {
43 const data = _get(json, 'add', false);
47 const data = _get(json, 'add', false);
44 if (data) {
48 if (data) {
45 contacts[data.ship] = data.contact;
49 contacts[data.ship] = data.contact;
@@ -47,7 +51,7 @@ function addContact(json: ContactUpdate, contacts: Rolodex): Rolodex {
47 return contacts;
51 return contacts;
48};
52};
49
53
50−function removeContact(json: ContactUpdate, contacts: Rolodex): Rolodex {
54+export function removeContact(json: ContactUpdate, contacts: Rolodex): Rolodex {
51 const data = _get(json, 'remove', false);
55 const data = _get(json, 'remove', false);
52 if (
56 if (
53 data &&
57 data &&
@@ -58,7 +62,7 @@ function removeContact(json: ContactUpdate, contacts: Rolodex): Rolodex {
58 return contacts;
62 return contacts;
59};
63};
60
64
61−function handleContactUpdateEvent(updateContacts) {
65+export function handleContactUpdateEvent(updateContacts) {
62 return ({ 'contact-update': contactUpdate }) => {
66 return ({ 'contact-update': contactUpdate }) => {
63 console.log(`received contact-update: ${Object.keys(contactUpdate)}`)
67 console.log(`received contact-update: ${Object.keys(contactUpdate)}`)
64 const reducers = [initialContacts, addContact, removeContact];
68 const reducers = [initialContacts, addContact, removeContact];
@@ -67,7 +71,7 @@ function handleContactUpdateEvent(updateContacts) {
67 };
71 };
68}
72}
69
73
70−function subscribeToContacts(e: (data: any) => void): Promise<any> {
74+export function subscribeToContacts(e: (data: any) => void): Promise<any> {
71 return api.subscribe({
75 return api.subscribe({
72 app: 'contact-store',
76 app: 'contact-store',
73 path: '/all',
77 path: '/all',
@@ -80,13 +84,3 @@ function subscribeToContacts(e: (data: any) => void): Promise<any> {
80 }
84 }
81 })
85 })
82}
86}
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 @@
1import { writable, derived } from 'svelte/store';
1import { writable, derived } from 'svelte/store';
2
2
3−import type { Rolodex } from '@urbit/api';
3+import type { Contact, Rolodex } from '@urbit/api';
4import store from './store';
4import store from './store';
5import type { StoreState, SearchedContactsState } from '../types/store';
5import type { StoreState, SearchedContactsState } from '../types/store';
6import { SearchSettings } from '../types/store';
6import { SearchSettings } from '../types/store';
@@ -15,16 +15,25 @@ function deriveSearchedContacts([$store, $searchSettings]): SearchedContactsStat
15 if (!$store.query) {
15 if (!$store.query) {
16 return {};
16 return {};
17 }
17 }
18− const filteredContacts = filterObject($store.contacts, (id) => {
18+ function idMatchesSearch(id) {
19 if (!$store.query.test(id)) return false;
19 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) {
21 return false;
21 return false;
22 }
22 }
23− if (!$searchSettings.includeComets && id.length > 32) {
23+ if (!$searchSettings.includeComets && id.length > 28) {
24 return false;
24 return false;
25 }
25 }
26 return true;
26 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+ })
28 return filteredContacts;
37 return filteredContacts;
29}
38}
30
39
ui/src/stores/store.ts+7−1
@@ -1,12 +1,14 @@
1import { writable } from 'svelte/store';
1import { writable } from 'svelte/store';
2
2
3import type { Rolodex } from '@urbit/api';
3import type { Rolodex } from '@urbit/api';
4−import { subscribeToContacts } from '../lib/api';
4+import { subscribeToContacts, getPeers } from '../lib/api';
5import type { StoreState } from '../types/store';
5import type { StoreState } from '../types/store';
6+import { normalizeId } from '../lib/id';
6import { setStoreKey } from '../lib/utils';
7import { setStoreKey } from '../lib/utils';
7
8
8const initStore: StoreState = {
9const initStore: StoreState = {
9 contacts: {},
10 contacts: {},
11+ peers: {},
10 query: null,
12 query: null,
11 connection: 'disconnected',
13 connection: 'disconnected',
12 ship: window.ship,
14 ship: window.ship,
@@ -34,4 +36,8 @@ function updateContacts(callback: (contacts: Rolodex) => Rolodex): void {
34
36
35subscribeToContacts(updateContacts);
37subscribeToContacts(updateContacts);
36
38
39+getPeers().then(({ points }) => {
40+ setStoreKey(store, 'peers', points.map(normalizeId));
41+});
42+
37export default { subscribe, set, search, reset };
43export 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.
711.gap-1 {
711.gap-1 {
712 gap: 0.25rem;
712 gap: 0.25rem;
713}
713}
714−.gap-x-3 {
715− -moz-column-gap: 0.75rem;
716− column-gap: 0.75rem;
717−}
718.gap-x-2 {
714.gap-x-2 {
719 -moz-column-gap: 0.5rem;
715 -moz-column-gap: 0.5rem;
720 column-gap: 0.5rem;
716 column-gap: 0.5rem;
721}
717}
718+.gap-x-3 {
719+ -moz-column-gap: 0.75rem;
720+ column-gap: 0.75rem;
721+}
722.gap-x-4 {
722.gap-x-4 {
723 -moz-column-gap: 1rem;
723 -moz-column-gap: 1rem;
724 column-gap: 1rem;
724 column-gap: 1rem;
@@ -806,26 +806,18 @@ Ensure the default browser behavior of the `hidden` attribute.
806.border-8 {
806.border-8 {
807 border-width: 8px;
807 border-width: 8px;
808}
808}
809−.border-b {
809+.border-l-8 {
810− border-bottom-width: 1px;
810+ border-left-width: 8px;
811}
811}
812.border-t {
812.border-t {
813 border-top-width: 1px;
813 border-top-width: 1px;
814}
814}
815+.border-b {
816+ border-bottom-width: 1px;
817+}
815.border-l {
818.border-l {
816 border-left-width: 1px;
819 border-left-width: 1px;
817}
820}
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−}
829.border-navy {
821.border-navy {
830 --tw-border-opacity: 1;
822 --tw-border-opacity: 1;
831 border-color: rgb(31 36 60 / var(--tw-border-opacity));
823 border-color: rgb(31 36 60 / var(--tw-border-opacity));
@@ -838,6 +830,14 @@ Ensure the default browser behavior of the `hidden` attribute.
838 --tw-border-opacity: 1;
830 --tw-border-opacity: 1;
839 border-color: rgb(209 213 219 / var(--tw-border-opacity));
831 border-color: rgb(209 213 219 / var(--tw-border-opacity));
840}
832}
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+}
841.border-gray-400 {
841.border-gray-400 {
842 --tw-border-opacity: 1;
842 --tw-border-opacity: 1;
843 border-color: rgb(156 163 175 / var(--tw-border-opacity));
843 border-color: rgb(156 163 175 / var(--tw-border-opacity));
@@ -858,14 +858,14 @@ Ensure the default browser behavior of the `hidden` attribute.
858 --tw-bg-opacity: 1;
858 --tw-bg-opacity: 1;
859 background-color: rgb(0 0 0 / var(--tw-bg-opacity));
859 background-color: rgb(0 0 0 / var(--tw-bg-opacity));
860}
860}
861−.bg-gray-300 {
862− --tw-bg-opacity: 1;
863− background-color: rgb(209 213 219 / var(--tw-bg-opacity));
864−}
865.bg-orange-gold {
861.bg-orange-gold {
866 --tw-bg-opacity: 1;
862 --tw-bg-opacity: 1;
867 background-color: rgb(247 211 146 / var(--tw-bg-opacity));
863 background-color: rgb(247 211 146 / var(--tw-bg-opacity));
868}
864}
865+.bg-gray-300 {
866+ --tw-bg-opacity: 1;
867+ background-color: rgb(209 213 219 / var(--tw-bg-opacity));
868+}
869.bg-amber-300 {
869.bg-amber-300 {
870 --tw-bg-opacity: 1;
870 --tw-bg-opacity: 1;
871 background-color: rgb(252 211 77 / var(--tw-bg-opacity));
871 background-color: rgb(252 211 77 / var(--tw-bg-opacity));
@@ -940,18 +940,18 @@ Ensure the default browser behavior of the `hidden` attribute.
940.p-4 {
940.p-4 {
941 padding: 1rem;
941 padding: 1rem;
942}
942}
943−.p-2 {
944− padding: 0.5rem;
945−}
946−.p-6 {
947− padding: 1.5rem;
948−}
949.p-8 {
943.p-8 {
950 padding: 2rem;
944 padding: 2rem;
951}
945}
952.p-3 {
946.p-3 {
953 padding: 0.75rem;
947 padding: 0.75rem;
954}
948}
949+.p-2 {
950+ padding: 0.5rem;
951+}
952+.p-6 {
953+ padding: 1.5rem;
954+}
955.p-1 {
955.p-1 {
956 padding: 0.25rem;
956 padding: 0.25rem;
957}
957}
@@ -975,6 +975,22 @@ Ensure the default browser behavior of the `hidden` attribute.
975 padding-top: 0.5rem;
975 padding-top: 0.5rem;
976 padding-bottom: 0.5rem;
976 padding-bottom: 0.5rem;
977}
977}
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+}
978.px-0\.5 {
994.px-0\.5 {
979 padding-left: 0.125rem;
995 padding-left: 0.125rem;
980 padding-right: 0.125rem;
996 padding-right: 0.125rem;
@@ -995,22 +1011,6 @@ Ensure the default browser behavior of the `hidden` attribute.
995 padding-left: 0.25rem;
1011 padding-left: 0.25rem;
996 padding-right: 0.25rem;
1012 padding-right: 0.25rem;
997}
1013}
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−}
1014.pt-10 {
1014.pt-10 {
1015 padding-top: 2.5rem;
1015 padding-top: 2.5rem;
1016}
1016}
@@ -1163,17 +1163,6 @@ Ensure the default browser behavior of the `hidden` attribute.
1163 transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1163 transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1164 transition-duration: 150ms;
1164 transition-duration: 150ms;
1165}
1165}
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−}
1177.hover\:bg-gray-200:hover {
1166.hover\:bg-gray-200:hover {
1178 --tw-bg-opacity: 1;
1167 --tw-bg-opacity: 1;
1179 background-color: rgb(229 231 235 / var(--tw-bg-opacity));
1168 background-color: rgb(229 231 235 / var(--tw-bg-opacity));
ui/src/types/store.ts+1
@@ -13,6 +13,7 @@ export interface StoreState {
13 // [group: string]: Group;
13 // [group: string]: Group;
14 // };
14 // };
15 contacts: Rolodex;
15 contacts: Rolodex;
16+ peers: Patp[];
16 query: RegExp;
17 query: RegExp;
17 // contacts: Contacts;
18 // contacts: Contacts;
18 // contactSearchResults: Contacts;
19 // contactSearchResults: Contacts;