on main help sign in
getting sigil search closer
commitc2331482a582d65c31d6fb7323646bd8149af5f8 authorJohn Hyde <johnhydefour@gmail.com> dateWed 21 Sep 2022, 11:49 (4 years ago) parente6b3ebd
15 files changed+204−129
desk/app/astrolabe.hoon+3 ui/src/components/search/SearchBar.svelte+3−3 ui/src/components/search/SearchResults.svelte+20−4 ui/src/components/search/sigil/SigilInput.svelte+14−14 ui/src/components/search/sigil/SymbolInput.svelte+9−3 ui/src/components/search/sigil/SymbolInputModeSelector.svelte+35−18 ui/src/lib/api.ts+1−1 ui/src/lib/colors.ts+3 ui/src/lib/id.ts+6−2 ui/src/lib/sigil.ts+39−4 ui/src/routes/Home.svelte+8−3 ui/src/stores/searchStores.ts+31−7 ui/src/stores/store.ts+11−4 ui/src/styles/tailwind-output.css+18−65 ui/src/types/store.ts+3−1
desk/app/astrolabe.hoon+3
@@ -69,6 +69,9 @@
6969 ``astrolabe-point-set+!>((get-spawned-points:hc ship arg))
7070 [%x %search @ ?(~ [%narrow ~])]
7171 =/ =search-text (trip &3.path)
72+ ?~ search-text ~|(%search-predicted-too-broad !!)
73+ ~& "search text"
74+ ~& search-text
7275 =/ expand-search =(t.t.t.path ~)
7376 ``astrolabe-point-set+!>((search-opoints:hc search-text expand-search))
7477 [%x %peers ~]
ui/src/components/search/SearchBar.svelte+3−3
@@ -10,11 +10,11 @@
1010 export let analysis: SearchAnalysis = new SearchAnalysis();
1111 export let search = '';
1212 export let sigilQuery: SigilQuery = new SigilQuery('planet');
13+ export let showSigilInput: boolean = false;
1314 export let pauseAnalysis: boolean = false;
1415 export let placeholderText = "Search by @p or #. '*' matches any character";
1516
1617 let debouncedSearch = search;
17− let showSigilInput: boolean = false;
1818
1919 $: {
2020 if (!pauseAnalysis) {
@@ -34,12 +34,12 @@
3434
3535 $: {
3636 if (showSigilInput && sigilQuery.isDefinitive) {
37− const querySyls = sigilQuery.query.flat();
37+ const querySyls = sigilQuery.querySyls.flat();
3838 search = '~' + querySyls[0];
3939 if (querySyls.length > 1) {
4040 search += querySyls[1];
4141 if (querySyls.length > 2) {
42− search += '-' + querySyls[2] + '-' + querySyls[3];
42+ search += '-' + querySyls[2] + querySyls[3];
4343 }
4444 }
4545 }
ui/src/components/search/SearchResults.svelte+20−4
@@ -2,13 +2,16 @@
22
33 import type { Patp, Rolodex } from '@urbit/api';
44 import { searchPoints } from 'lib/api';
5+ import type { SigilQuery } from 'lib/sigil';
56 import { searchedContacts } from 'stores/searchStores';
67 import ShipListings from '@/ShipListings.svelte';
78 import SearchSettings from './SearchSettings.svelte';
89 import SearchResultsNavButtons from './SearchResultsNavButtons.svelte';
910
10− export let query: RegExp;
11+ export let regexQuery: RegExp;
12+ export let sigilQuery: SigilQuery;
1113 export let search: string;
14+ export let searchMode: ('patp' | 'sigil') = 'patp';
1215 // export let patp: string;
1316 let combinedSearchResults = [];
1417 let searchedPointsP: Promise<any>;
@@ -25,14 +28,27 @@
2528 $: {
2629 clearTimeout(timer);
2730 timer = setTimeout(() => {
28− searchedContacts.search(query);
31+ if (searchMode == 'patp') {
32+ searchedContacts.searchPatp(regexQuery);
33+ } else if (searchMode == 'sigil') {
34+ searchedContacts.searchSigil(sigilQuery.querySyls);
35+ }
2936 }, 1000);
3037 }
38+ // let otherTimer;
39+ // $: {
40+ // clearTimeout(otherTimer);
41+ // if (searchMode === 'sigil') {
42+ // otherTimer = setTimeout(() => {
43+ // searchedContacts.search(regexQuery, searchMode);
44+ // }, 1000);
45+ // }
46+ // }
3147 function doSearch() {
3248 const searchSnap = search;
3349 searchStatus = 'prog';
3450 console.log(`starting search for "${searchSnap}"`);
35− searchedPointsP = searchPoints(searchSnap);
51+ searchedPointsP = searchPoints(searchSnap, searchMode);
3652 searchedPointsP.then((points) => {
3753 if (search === searchSnap) {
3854 searchedPoints = points;
@@ -48,7 +64,7 @@
4864 }
4965 });
5066 }
51− $: search, doSearch();
67+ $: [search, sigilQuery], doSearch();
5268
5369 $: {
5470 const contactSearchResults: Rolodex = JSON.parse(JSON.stringify($searchedContacts));
ui/src/components/search/sigil/SigilInput.svelte+14−14
@@ -68,21 +68,21 @@ import { sigil } from '@tlon/sigil-js';
6868 {/if}
6969 </div>
7070 </div>
71− {#each sigilQuery.activeSymbols as symbol}
72− {@const plausibleSyllables = symbol.plausibleSyllables}
73− {@const plausibleParts = symbol.plausibleParts}
74− <p>
75− {#if plausibleSyllables.length < 20 && plausibleSyllables.length > 0}
76− {plausibleSyllables.join(', ')}:
77− {#if plausibleParts.length < 20 && plausibleParts.length > 0}
78− {plausibleParts.join(', ')}
79− {:else}
80− {plausibleParts.length}
81− {/if}
71+ <!-- {#each sigilQuery.activeSymbols as symbol}
72+ {@const plausibleSyllables = symbol.plausibleSyllables}
73+ {@const plausibleParts = symbol.plausibleParts}
74+ <p>
75+ {#if plausibleSyllables.length < 20 && plausibleSyllables.length > 0}
76+ {plausibleSyllables.join(', ')}:
77+ {#if plausibleParts.length < 20 && plausibleParts.length > 0}
78+ {plausibleParts.join(', ')}
8279 {:else}
83− {plausibleSyllables.length}
80+ {plausibleParts.length}
8481 {/if}
85− </p>
86− {/each}
82+ {:else}
83+ {plausibleSyllables.length}
84+ {/if}
85+ </p>
86+ {/each} -->
8787 <!-- {sigilQuery.string} -->
8888</div>
ui/src/components/search/sigil/SymbolInput.svelte+9−3
@@ -6,6 +6,7 @@
66 import type { PartType } from 'types/sigil';
77 import type { SymbolQuery } from 'lib/sigil';
88 import { filterPartsByGeon } from 'lib/sigil';
9+ import * as colors from 'lib/colors';
910
1011 export let symbolQuery: SymbolQuery;
1112 // export let inputMode: PartType;
@@ -13,6 +14,8 @@
1314 export let index: number = 0;
1415 export let clan: string = 'galaxy';
1516 export let inputComponents: string[] = [];
17+ export let fgColor = 'black';
18+ export let bgColor = 'white';
1619 let size = 128;
1720
1821 let translateClass;
@@ -72,11 +75,14 @@
7275 style:max-width="{size}px"
7376 on:click|stopPropagation
7477>
78+<!-- fgColor={!plausible ? 'red' : (symbolQuery.isPerfectMatch ? colors.GOLDS1 : 'black')} -->
7579 {#if symbolQuery.components.length > 0}
7680 <Symbol components={symbolQuery.components} {size}
7781 inverted={!symbolQuery.geon}
7882 interactive={focused}
79− fgColor={plausible ? 'black' : 'orange'}
83+ fgColor={!plausible ? 'red' : (symbolQuery.isPerfectMatch ? 'black' : '#333')}
84+ bgColor="white"
85+ altBgColor="transparent"
8086 hoverColor="red"
8187 on:partClick={onPartDelete}
8288 />
@@ -89,8 +95,8 @@
8995 <div class="absolute inset-0 pointer-events-none">
9096 <Symbol components={filteredInputComponents} {size}
9197 strokeWidthFactor={inputContainsStrokes ? 2.5 : 1}
92− fgColor={symbolQuery.geon ? "white" : "black"}
93− bgColor={!symbolQuery.geon ? "white" : "black"}
98+ fgColor={symbolQuery.geon ? bgColor : fgColor}
99+ bgColor={!symbolQuery.geon ? bgColor : fgColor}
94100 altBgColor="transparent"
95101 opacity={0.5}
96102 inverted
ui/src/components/search/sigil/SymbolInputModeSelector.svelte+35−18
@@ -16,16 +16,17 @@
1616 export let unfocusSymbol: Function;
1717
1818 let modes: string[] = [];
19+ // let subMode: string = undefined;
1920 let modeDef: ModeDef = undefined;
2021 let subModeDefs: ModeDefs = MODE_DEFS;
2122
2223 let buttonClasses = 'border border-gray-500 rounded-md aspect-square w-[48px]';
2324
24− $: {
25− if (!symbolQuery.geon) {
26− modes = ['geon'];
27− }
28− }
25+ // $: {
26+ // if (!symbolQuery.geon) {
27+ // modes = ['geon'];
28+ // }
29+ // }
2930
3031 function pushMode(mode: string) {
3132 if (modeDef?.modes) {
@@ -75,6 +76,29 @@
7576 }
7677 }
7778
79+ $: plausibleModes = Object.entries(subModeDefs).map(([modeName, def]) => {
80+ const deepParts = getDeepParts(def);
81+ const plausible = $searchSettings.allowFictionalSigils || symbolQuery.arePartsPlausible(deepParts);
82+ if (!plausible && modeName !== 'geon') return null;
83+ const parts = filterPartsByGeon(def.displayParts || def.parts, symbolQuery.geon);
84+ return { parts, modeName };
85+ }).filter(mode => mode !== null);
86+
87+ $: {
88+ const currentMode = modes.slice(-1)[0];
89+ if (currentMode != 'geon') {
90+ if (plausibleModes.length === 0) {
91+ popMode();
92+ } else if (plausibleModes.length >= 1) {
93+ const leafMode = modeDef && !(modeDef.modes);
94+ const plausibleModeNames = plausibleModes.map(({ modeName }) => modeName);
95+ if ((modes.length == 1 && modeDef.modes) || (leafMode && !plausibleModeNames.includes(currentMode))) {
96+ pushMode(plausibleModes[0].modeName);
97+ }
98+ }
99+ }
100+ }
101+
78102 function clear() {
79103 if (modes.length) {
80104 symbolQuery = symbolQuery.clearPartType(modes[0] as PartType);
@@ -90,26 +114,19 @@
90114 }
91115</style>
92116
93−<div class="mx-auto flex items-center justify-center xs:flex-col">
117+<div class="mx-auto flex justify-center xs:items-center xs:flex-col">
94118 <div class="grid grid-cols-custom gap-1 self-start justify-center mr-1 xs:mr-0 xs:mb-1 xs:self-stretch">
95119 <img src={upArrow} alt="done" class={buttonClasses} on:click={popMode}/>
96120 <img src={xIcon} alt="done" class={buttonClasses} on:click={clear}/>
97121 </div>
98− <div class="max-w-[15rem] grid grid-cols-custom gap-1 justify-center xs:max-w-[8rem]">
122+ <div class="max-w-[15rem] h-full grid grid-cols-custom gap-1 justify-center xs:max-w-[8rem]">
99123 {#if modes[0] === 'geon'}
100124 <GeonSelector bind:symbolQuery {popMode} />
101125 {:else}
102− {#each Object.entries(subModeDefs) as [modeName, def]}
103− {@const parts = filterPartsByGeon(def.displayParts || def.parts, symbolQuery.geon)}
104− {@const deepParts = getDeepParts(def)}
105− {@const plausible = $searchSettings.allowFictionalSigils || symbolQuery.arePartsPlausible(deepParts)}
106− {#if plausible || modeName === 'geon'}
107− <SymbolInputModeSelectorButton components={parts} focused={modes.slice(-1)[0] === modeName}
108− on:click={() => pushMode(modeName)}
109− />
110− <!-- {:else}
111− {deepParts.join()} -->
112− {/if}
126+ {#each plausibleModes as { modeName, parts }}
127+ <SymbolInputModeSelectorButton components={parts} focused={modes.slice(-1)[0] === modeName}
128+ on:click={() => pushMode(modeName)}
129+ />
113130 {/each}
114131 {/if}
115132 </div>
ui/src/lib/api.ts+1−1
@@ -26,7 +26,7 @@ export function getPeers(): Promise<any> {
2626 return scry<any>(`/peers`);
2727}
2828
29−export async function searchPoints(search: string): Promise<Patp[]> {
29+export async function searchPoints(search: string, mode: ('patp' | 'sigil')): Promise<Patp[]> {
3030 const { points } = await scry<any>(`/search/${search}`);
3131 return points.map(normalizeId);
3232}
ui/src/lib/colors.ts+3
@@ -1,5 +1,8 @@
1+export const ORANGE_GOLD = '#f7d392';
12export const GOLD = '#fcd34d';
23export const GOLDT2 = '#fddc71';
4+export const GOLDS1 = '#e3be45';
5+export const GOLDS2 = '#caa93e';
36export const WHITE = '#FFF';
47export const LGREY = '#BBB';
58export const DGREY = '#777';
ui/src/lib/id.ts+6−2
@@ -435,8 +435,12 @@ function cite(ship: string) {
435435 return `~${patp}`;
436436}
437437
438−function splitIdIntoWords(str: string): string[] {
439− return str.split(/(?:--|[-_^])/);
438+export function splitIdIntoWords(str: string): string[] {
439+ return desig(str).split(/(?:--|[-_^])/);
440+}
441+
442+export function splitIdIntoSyls(str: string): string[] {
443+ return splitIdIntoWords(str).map(word => [word.slice(0,3), word.slice(3,6)]).flat();
440444}
441445
442446function padWithWild(search: string) {
ui/src/lib/sigil.ts+39−4
@@ -53,16 +53,20 @@ export class SigilQuery {
5353 return this.activeSymbols.every(symbol => !symbol.isPlausible);
5454 }
5555
56− get query(): string[][] {
56+ get querySyls(): string[][] {
5757 return this.activeSymbols.map(symbol => symbol.plausibleSyllables);
5858 }
5959
6060 get isDefinitive() {
61− return this.query.every(syls => syls.length === 1);
61+ return this.querySyls.every(syls => syls.length === 1);
62+ }
63+
64+ get isNotEmpty() {
65+ return this.activeSymbols.some(symbol => symbol.components.length > 0);
6266 }
6367
6468 get string(): string {
65− return this.symbols.map((symbol) => symbol.components.join()).join(' | ')
69+ return this.symbols.map((symbol) => symbol.components.join(',')).join('|')
6670 }
6771
6872 setSymbol(symbol: SymbolQuery, activeSymbolIndex: number) {
@@ -72,6 +76,22 @@ export class SigilQuery {
7276 }
7377}
7478
79+const equivalentParts = {
80+ lfhb: 'lfhbm',
81+ lfhbm: 'lfhb',
82+ atlf2: 'atlf2l',
83+ atlf2l: 'atlf2',
84+ atlf4: 'atlf4l',
85+ atlf4l: 'atlf4',
86+ atlf6: 'atlf6l',
87+ atlf6l: 'atlf6',
88+}
89+
90+function addEquivalentParts(parts: string[]): string[] {
91+ const moreParts = parts.map(partId => equivalentParts[partId]).filter(part => part);
92+ return [...parts, ...moreParts];
93+}
94+
7595export function getPlausibleSyllables(parts: string[], symbolType: SymbolType): string[] {
7696 let plausibleSyllables: string[];
7797 if (symbolType === 'prefix') {
@@ -81,7 +101,14 @@ export function getPlausibleSyllables(parts: string[], symbolType: SymbolType):
81101 } else {
82102 plausibleSyllables = [...prefixes, ...suffixes];
83103 }
84− const possibilitiesByPart = parts.map((partId) => partParents[partId]);
104+ const possibilitiesByPart = parts.map((partId) => {
105+ let parents = partParents[partId];
106+ const otherPart = equivalentParts[partId];
107+ if (otherPart) {
108+ parents = [...parents, ...partParents[otherPart]];
109+ }
110+ return parents;
111+ });
85112 plausibleSyllables = intersection(plausibleSyllables, ...possibilitiesByPart);
86113 return plausibleSyllables;
87114}
@@ -89,6 +116,7 @@ export function getPlausibleSyllables(parts: string[], symbolType: SymbolType):
89116export function getPlausibleNewParts(parts: string[], symbolType: SymbolType): string[] {
90117 const syllables: string[] = getPlausibleSyllables(parts, symbolType);
91118 let plausibleParts = uniq(syllables.map((syllable) => symbolDefs[syllable]).flat());
119+ plausibleParts = addEquivalentParts(plausibleParts);
92120 return without(plausibleParts, ...parts);
93121}
94122
@@ -185,6 +213,13 @@ export class SymbolQuery {
185213 return getPlausibleNewParts(this.components, this.symbolType);
186214 }
187215
216+ get isPerfectMatch() {
217+ const syls = this.plausibleSyllables;
218+ if (syls.length === 1) return true;
219+ const compLength = this.components.length;
220+ return syls.map(syl => symbolDefs[syl].length).some(length => length === compLength);
221+ }
222+
188223 filterPlausibleParts(parts: string[]): string[] {
189224 return filterPlausibleParts(this.components, this.symbolType, parts);
190225 }
ui/src/routes/Home.svelte+8−3
@@ -3,6 +3,7 @@
33 import { link, push, replace, location, querystring } from 'svelte-spa-router'
44 import pageName from 'stores/pageName';
55 import { SearchAnalysis } from 'lib/id';
6+ import { SigilQuery } from 'lib/sigil';
67 import { linkToShip, linkToChart } from 'lib/link';
78 import SearchBar from "@/search/SearchBar.svelte";
89 import SearchResults from "@/search/SearchResults.svelte";
@@ -12,6 +13,9 @@
1213 import HomeScreenTiles from '@/HomeScreenTiles.svelte';
1314 export let params: any = {};
1415 let analysis: SearchAnalysis = new SearchAnalysis();
16+ let sigilQuery: SigilQuery = new SigilQuery('planet');
17+ let showSigilInput: boolean = false;
18+ $: searchMode = showSigilInput ? 'sigil' : 'patp';
1519 let navigating = false;
1620
1721 let search: string = (new URLSearchParams($querystring)).get('search') || params.arg || '';
@@ -136,14 +140,15 @@
136140</script>
137141
138142<div class="p-4 space-y-4 min-h-full flex flex-col items-center 2xs:p-8 2xs:space-y-8">
139− <SearchBar bind:analysis bind:search {placeholderText} />
143+ <SearchBar bind:analysis bind:sigilQuery bind:search bind:showSigilInput {placeholderText} />
140144 <!-- <p class="bg-white">navigating</p> -->
141145 {#if params.mode == "chart"}
142146 <StarChart patp={analysis.patpIsValid ? analysis.patp : null} />
143147 {:else if analysis.patpIsValid}
144148 <ShipView patp={analysis.patp} />
145− {:else if (analysis.queryIsValid)}
146− <SearchResults query={analysis.query} search={analysis.search} />
149+ {:else if (searchMode === 'patp' && analysis.queryIsValid) || (searchMode == 'sigil' && sigilQuery.isNotEmpty)}
150+ <SearchResults regexQuery={analysis.query} sigilQuery={sigilQuery} search={analysis.search}
151+ {searchMode} />
147152 {:else if (analysis.search.length > 0)}
148153 <ValidationProblems problems={analysis.queryProblems} />
149154 {:else}
ui/src/stores/searchStores.ts+31−7
@@ -4,19 +4,29 @@ import type { Contact, Rolodex } from '@urbit/api';
44import store from './store';
55import type { StoreState, SearchedContactsState } from 'types/store';
66import { SearchSettings } from 'types/store';
7+import { splitIdIntoSyls } from 'lib/id';
78import { filterObject } from 'lib/utils';
89
910const searchSettings = writable(new SearchSettings());
1011
1112const searchedContacts = derived([store, searchSettings], deriveSearchedContacts);
12−searchedContacts.search = store.search;
13+searchedContacts.searchPatp = store.searchPatp;
14+searchedContacts.searchSigil = store.searchSigil;
1315
1416function deriveSearchedContacts([$store, $searchSettings]): SearchedContactsState {
15− if (!$store.query) {
16− return {};
17+ let idMatches;
18+ if ($store.searchMode === 'patp') {
19+ if (!$store.patpQuery) {
20+ return {};
21+ }
22+ idMatches = idMatchesRegex;
23+ } else {
24+ if (!$store.sigilQuery) {
25+ return {};
26+ }
27+ idMatches = idMatchesSyls;
1728 }
18− function idMatchesSearch(id) {
19− if (!$store.query.test(id)) return false;
29+ function isIdAllowed(id) {
2030 if (!$searchSettings.includeMoons && id.length > 14 && id.length <= 28) {
2131 return false;
2232 }
@@ -25,9 +35,23 @@ function deriveSearchedContacts([$store, $searchSettings]): SearchedContactsStat
2535 }
2636 return true;
2737 }
28− const filteredContacts = filterObject($store.contacts, idMatchesSearch) as Rolodex;
38+ function idMatchesSyls(id) {
39+ if (!isIdAllowed(id)) return false;
40+ const idSyls = splitIdIntoSyls(id);
41+ if (idSyls.length !== $store.sigilQuery.length) return false;
42+ return idSyls.every((syl, index) => {
43+ const symbolSyls = $store.sigilQuery[index];
44+ if (symbolSyls.length == 256) return true;
45+ return symbolSyls.includes(syl);
46+ });
47+ }
48+ function idMatchesRegex(id) {
49+ if (!$store.patpQuery.test(id)) return false;
50+ return isIdAllowed(id);
51+ }
52+ const filteredContacts = filterObject($store.contacts, idMatches) as Rolodex;
2953 $store.peers.forEach((id) => {
30− if (idMatchesSearch(id) && !filteredContacts[id]) {
54+ if (idMatches(id) && !filteredContacts[id]) {
3155 filteredContacts[id] = {
3256 nickname: null, bio: null, status: null, color: null, avatar: null,
3357 cover: null, groups: [], 'last-updated': 0,
ui/src/stores/store.ts+11−4
@@ -9,7 +9,8 @@ import { setStoreKey } from 'lib/utils';
99const initStore: StoreState = {
1010 contacts: {},
1111 peers: {},
12− query: null,
12+ patpQuery: null,
13+ sigilQuery: null,
1314 connection: 'disconnected',
1415 ship: window.ship,
1516};
@@ -17,8 +18,14 @@ const initStore: StoreState = {
1718const store = writable(initStore);
1819const { subscribe, update, set } = store;
1920
20−function search(idQuery: RegExp) {
21− setStoreKey(store, 'query', idQuery);
21+function searchPatp(patpQuery: RegExp) {
22+ setStoreKey(store, 'patpQuery', patpQuery);
23+ setStoreKey(store, 'searchMode', 'patp');
24+}
25+
26+function searchSigil(syls: string[][]) {
27+ setStoreKey(store, 'sigilQuery', syls);
28+ setStoreKey(store, 'searchMode', 'sigil');
2229}
2330
2431function reset() {
@@ -40,4 +47,4 @@ getPeers().then(({ points }) => {
4047 setStoreKey(store, 'peers', points.map(normalizeId));
4148});
4249
43−export default { subscribe, set, search, reset };
50+export default { subscribe, set, searchPatp, searchSigil, reset };
ui/src/styles/tailwind-output.css+18−65
@@ -690,15 +690,15 @@ Ensure the default browser behavior of the `hidden` attribute.
690690.ml-auto {
691691 margin-left: auto;
692692}
693−.-mt-0\.5 {
694− margin-top: -0.125rem;
695−}
696693.mb-1 {
697694 margin-bottom: 0.25rem;
698695}
699696.mr-1 {
700697 margin-right: 0.25rem;
701698}
699+.-mt-0\.5 {
700+ margin-top: -0.125rem;
701+}
702702.block {
703703 display: block;
704704}
@@ -849,15 +849,9 @@ Ensure the default browser behavior of the `hidden` attribute.
849849.w-16 {
850850 width: 4rem;
851851}
852−.w-\[50\%\] {
853− width: 50%;
854−}
855852.w-\[48px\] {
856853 width: 48px;
857854}
858−.w-\[\] {
859− width: ;
860−}
861855.min-w-0 {
862856 min-width: 0px;
863857}
@@ -879,20 +873,11 @@ Ensure the default browser behavior of the `hidden` attribute.
879873.max-w-2xl {
880874 max-width: 42rem;
881875}
882−.max-w-\[18rem\] {
883− max-width: 18rem;
884−}
885876.max-w-\[10rem\] {
886877 max-width: 10rem;
887878}
888−.max-w-\[21rem\] {
889− max-width: 21rem;
890−}
891−.max-w-\[20rem\] {
892− max-width: 20rem;
893−}
894−.max-w-\[19rem\] {
895− max-width: 19rem;
879+.max-w-\[18rem\] {
880+ max-width: 18rem;
896881}
897882.max-w-\[15rem\] {
898883 max-width: 15rem;
@@ -936,14 +921,6 @@ Ensure the default browser behavior of the `hidden` attribute.
936921.origin-bottom-left {
937922 transform-origin: bottom left;
938923}
939−.translate-x-1\/4 {
940− --tw-translate-x: 25%;
941− transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
942−}
943−.translate-y-1\/4 {
944− --tw-translate-y: 25%;
945− transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
946−}
947924.translate-x-0 {
948925 --tw-translate-x: 0px;
949926 transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
@@ -952,6 +929,14 @@ Ensure the default browser behavior of the `hidden` attribute.
952929 --tw-translate-y: 0px;
953930 transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
954931}
932+.translate-x-1\/4 {
933+ --tw-translate-x: 25%;
934+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
935+}
936+.translate-y-1\/4 {
937+ --tw-translate-y: 25%;
938+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
939+}
955940.-translate-x-1\/4 {
956941 --tw-translate-x: -25%;
957942 transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
@@ -1227,12 +1212,12 @@ Ensure the default browser behavior of the `hidden` attribute.
12271212.border-2 {
12281213 border-width: 2px;
12291214}
1230−.border-\[1\.5px\] {
1231− border-width: 1.5px;
1232−}
12331215.border-0 {
12341216 border-width: 0px;
12351217}
1218+.border-\[1\.5px\] {
1219+ border-width: 1.5px;
1220+}
12361221.border-l-8 {
12371222 border-left-width: 8px;
12381223}
@@ -1447,24 +1432,12 @@ Ensure the default browser behavior of the `hidden` attribute.
14471432.p-0 {
14481433 padding: 0px;
14491434}
1450−.p-0\.5 {
1451− padding: 0.125rem;
1452−}
14531435.p-\[3px\] {
14541436 padding: 3px;
14551437}
14561438.p-\[4px\] {
14571439 padding: 4px;
14581440}
1459−.p-\[2\.5px\] {
1460− padding: 2.5px;
1461−}
1462−.p-\[2\.75px\] {
1463− padding: 2.75px;
1464−}
1465−.p-\[2px\] {
1466− padding: 2px;
1467−}
14681441.px-2 {
14691442 padding-left: 0.5rem;
14701443 padding-right: 0.5rem;
@@ -1783,26 +1756,6 @@ Ensure the default browser behavior of the `hidden` attribute.
17831756 max-width: 8rem;
17841757 }
17851758
1786− .xs\:grid-flow-row {
1787− grid-auto-flow: row;
1788− }
1789−
1790− .xs\:grid-flow-col {
1791− grid-auto-flow: column;
1792− }
1793−
1794− .xs\:grid-flow-row-dense {
1795− grid-auto-flow: row dense;
1796− }
1797−
1798− .xs\:grid-cols-2 {
1799− grid-template-columns: repeat(2, minmax(0, 1fr));
1800− }
1801−
1802− .xs\:grid-rows-3 {
1803− grid-template-rows: repeat(3, minmax(0, 1fr));
1804− }
1805−
18061759 .xs\:flex-row {
18071760 flex-direction: row;
18081761 }
@@ -1811,8 +1764,8 @@ Ensure the default browser behavior of the `hidden` attribute.
18111764 flex-direction: column;
18121765 }
18131766
1814− .xs\:items-stretch {
1815− align-items: stretch;
1767+ .xs\:items-center {
1768+ align-items: center;
18161769 }
18171770
18181771 .xs\:self-stretch {
ui/src/types/store.ts+3−1
@@ -14,7 +14,9 @@ export interface StoreState {
1414 // };
1515 contacts: Rolodex;
1616 peers: Patp[];
17− query: RegExp;
17+ patpQuery: RegExp;
18+ sigilQuery: string[][];
19+ searchMode: ('patp' | 'sigil');
1820 // contacts: Contacts;
1921 // contactSearchResults: Contacts;
2022 // chats: [];