on main help sign in
fix bugs and add keybord shortcuts
commit0eeff2c137cedd582c11c2362895fcc3cfb17a05 authorJohn Hyde <johnhydefour@gmail.com> dateTue 13 Dec 2022, 23:25 (4 years ago) parentf308431
11 files changed+182−18
desk/doc/usr/astrolabe.udon+13−3 ui/src/App.svelte+35−1 ui/src/components/docs/Doc.svelte+10−1 ui/src/components/search/SearchBar.svelte+18−1 ui/src/components/search/sigil/ClanSelect.svelte+10−2 ui/src/components/search/sigil/SigilInput.svelte+13−1 ui/src/components/search/sigil/SymbolInputModeSelector.svelte+7 ui/src/lib/link.ts+3−3 ui/src/routes/Home.svelte+1−6 ui/src/stores/keys.ts+68 ui/src/styles/tailwind-output.css+4
desk/doc/usr/astrolabe.udon+13−3
@@ -9,6 +9,16 @@ spawned ships with partial or wildcard searches, e.g. `sampel-***net`.
99There's also Help Documentation if
1010you want to learn more about how Urbit IDs work.
1111
12+## Keyboard Shortcuts
13+
14+- `/` - focus the search bar
15+- `escape` - leave text fields, close help pages, etc
16+- `?` - open the help docs
17+- `s` - switch to sigil search
18+- `p` - switch to patp search
19+- `backspace` or `x` - clear sigil input info
20+
21+
1222## How to Use Search
1323
1424When you type part of a `@p` into the search bar, all your contacts and peers will be
@@ -71,9 +81,9 @@ The Parent view simply shows each point under the point that spawned it. All spa
7181are shown in this view, with the exception of locked stars.
7282In both views, all galaxies are shown for completeness.
7383
74−## Upcoming Features
75−
76−In addition to improving the search features over time, Astrolabe will one day feature integrations with Groups, `%pals`, `%gora`, and whatever else seems fun and/or useful.
84+The All/Peers/Pals selector lets you filter which points are easily visible in the graph.
85+If you select Peers, only those ships that your ship has talked to will be full-colored.
86+If you select Pals, only ships you've added in `%pals` will be full-colored.
7787
7888## Info
7989
ui/src/App.svelte+35−1
@@ -5,6 +5,8 @@
55 import Home from './routes/Home.svelte';
66 import HelpModal from '@/docs/HelpModal.svelte';
77 import "./styles/styles.scss";
8+ import docs from 'stores/docs';
9+ import keys from 'stores/keys';
810
911 // const routes = {
1012 // '/help/:path?': HelpPage,
@@ -30,6 +32,36 @@
3032 $: {
3133 document.title = title
3234 }
35+
36+ function inputEvent(event) {
37+ if (event.target.tagName == 'INPUT') return true;
38+ if (event.target.tagName == 'TEXTAREA') return true;
39+ return false;
40+ }
41+
42+ function handleKeyDown(event: KeyboardEvent) {
43+ keys.runCallbacks($keys.globalkeydown, event);
44+ if (!inputEvent(event)) {
45+ keys.runCallbacks($keys.keydown, event);
46+ }
47+ }
48+
49+ function handleKeyUp(event: KeyboardEvent) {
50+ keys.runCallbacks($keys.globalkeyup, event);
51+ if (!inputEvent(event)) {
52+ keys.runCallbacks($keys.keyup, event);
53+ }
54+ }
55+
56+ $:{
57+ $keys.keyup.get('?').set('app', () => docs.show(''));
58+ $keys.keyup.get('shift+?').set('app', () => docs.show(''));
59+ $keys.globalkeyup.get('escape').set('app', (event) => {
60+ if (inputEvent(event)) {
61+ event.target.blur();
62+ }
63+ });
64+ }
3365</script>
3466
3567<main class="h-full overflow-y-auto">
@@ -39,4 +71,6 @@
3971
4072<svelte:head>
4173 <title>{title}</title>
42−</svelte:head>
74+</svelte:head>
75+
76+<svelte:body on:keydown={handleKeyDown} on:keyup={handleKeyUp} />
ui/src/components/docs/Doc.svelte+10−1
@@ -1,9 +1,10 @@
11<script lang="ts">
2− import { createEventDispatcher } from 'svelte';
2+ import { createEventDispatcher, onDestroy } from 'svelte';
33 import DocContent from './DocContent.svelte';
44 import DocHome from './DocHome.svelte';
55 import Hamburger from '@/buttons/Hamburger.svelte';
66 import CloseButton from '@/buttons/CloseButton.svelte';
7+ import keys from 'stores/keys';
78
89 export let path: string;
910
@@ -17,6 +18,14 @@
1718 function navigate(path) {
1819 dispatch('navigate', path);
1920 }
21+
22+ $: {
23+ $keys.globalkeyup.get('escape').set('doc', close);
24+ }
25+
26+ onDestroy(() => {
27+ $keys.globalkeyup.get('escape').delete('doc');
28+ });
2029</script>
2130
2231<div class="rounded-lg bg-white">
ui/src/components/search/SearchBar.svelte+18−1
@@ -3,6 +3,7 @@
33 import Symbol from './sigil/Symbol.svelte';
44
55 import docs from 'stores/docs';
6+ import keys from 'stores/keys';
67 import { SearchAnalysis } from 'lib/id';
78 import { SigilQuery } from 'lib/sigil';
89
@@ -78,8 +79,22 @@
7879 }
7980 }
8081
82+ let searchInput;
83+ let searchFocused = false;
84+ function onFocus() { searchFocused = true; }
85+ function onBlur() { searchFocused = false; }
86+ function focus() { if (searchInput) searchInput.focus() }
87+ function blur() { if (searchInput) searchInput.blur() }
88+ $:{
89+ $keys.keyup.get('/').set('searchBar', focus);
90+ // generic binding in App.svelte takes care of blur
91+ $keys.keyup.get('p').set('searchBar', closeSigilInput);
92+ $keys.keyup.get('s').set('searchBar', openSigilInput);
93+ }
94+
8195 function openSigilInput() {
82− searchMode = 'sigil';
96+ if (allowSigilSearch)
97+ searchMode = 'sigil';
8398 }
8499
85100 function closeSigilInput() {
@@ -118,6 +133,8 @@
118133 spellcheck="false"
119134 placeholder={placeholderText}
120135 on:input={(e) => bouncySearch = e.target.value}
136+ on:focus={onFocus} on:blur={onBlur}
137+ bind:this={searchInput}
121138 value={search}
122139 autofocus
123140 />
ui/src/components/search/sigil/ClanSelect.svelte+10−2
@@ -3,20 +3,28 @@
33
44 import { capitalize } from 'lib/utils';
55 import { CLANS, Clan } from 'types/sigil';
6+ import keys from 'stores/keys';
67
78 export let clan: Clan = 'planet';
89
910 let dropdownOpen = false;
1011
12+ function openDropdown() { dropdownOpen = true }
13+ function closeDropdown() { dropdownOpen = false }
14+
1115 function selectClan(itemClan: typeof clan) {
1216 clan = itemClan;
1317 dropdownOpen = false;
1418 }
19+
20+ $: {
21+ $keys.keyup.get('escape').set('clanSelect', closeDropdown);
22+ }
1523</script>
1624
1725<div class="text-xl text-center bg-white">
1826 <p
19− on:click={() => dropdownOpen = true}
27+ on:click={openDropdown}
2028 class=" py-2"
2129 >
2230 {capitalize(clan)}
@@ -24,7 +32,7 @@
2432 </p>
2533 {#if dropdownOpen}
2634 <div class="absolute z-20 top-1 w-full flex flex-col mx-auto divide-y bg-white rounded-2xl border-b">
27− <h3 class="py-2" on:click={() => dropdownOpen = false}>
35+ <h3 class="py-2" on:click={closeDropdown}>
2836 Select Sigil Type
2937 </h3>
3038 {#each CLANS as itemClan}
ui/src/components/search/sigil/SigilInput.svelte+13−1
@@ -6,8 +6,9 @@
66 import ToggleButton from '@/buttons/ToggleButton.svelte';
77
88 import type { SigilQuery } from 'lib/sigil';
9− import { searchSettings } from 'stores/searchStores';
109 import { toggleStoreKey } from 'lib/utils';
10+ import { searchSettings } from 'stores/searchStores';
11+ import keys from 'stores/keys';
1112
1213 export let sigilQuery: SigilQuery;
1314 let focusedSymbolIndex: number = undefined;
@@ -37,6 +38,17 @@
3738 $: {
3839 sigilQuery.allowFictional = $searchSettings.allowFictionalSigils;
3940 }
41+
42+ function keyboardClear() {
43+ if (!focusedSymbol) {
44+ clear();
45+ }
46+ }
47+
48+ $: {
49+ $keys.keyup.get('x').set('sigilInput', keyboardClear);
50+ $keys.keyup.get('backspace').set('sigilInput', keyboardClear);
51+ }
4052</script>
4153
4254<div>
ui/src/components/search/sigil/SymbolInputModeSelector.svelte+7
@@ -5,6 +5,7 @@
55 import SymbolInputModeSelectorButton from './SymbolInputModeSelectorButton.svelte';
66
77 import { searchSettings } from 'stores/searchStores';
8+ import keys from 'stores/keys';
89 import type { PartType, ModeDef, ModeDefs } from 'types/sigil';
910 import type { SymbolQuery } from 'lib/sigil';
1011 import { MODE_DEFS } from 'types/sigil';
@@ -130,6 +131,12 @@
130131 symbolQuery = symbolQuery.clearExceptPartType('geon');
131132 }
132133 }
134+
135+ $: {
136+ $keys.keyup.get('x').set('sigilMode', clear);
137+ $keys.keyup.get('backspace').set('sigilMode', clear);
138+ $keys.keyup.get('escape').set('sigilMode', popMode);
139+ }
133140</script>
134141
135142<style lang="scss">
ui/src/lib/link.ts+3−3
@@ -1,16 +1,16 @@
11import { buildQuerystring } from './utils';
22
33export function linkToShip(patp: string, queryParams = {}): string {
4− return `/ship/${patp}` + buildQuerystring(queryParams);
4+ return `/ship/${encodeURIComponent(patp)}` + buildQuerystring(queryParams);
55}
66
77export function linkToSearch(search: string, queryParams = {}): string {
8− return `/search/${search}` + buildQuerystring(queryParams);
8+ return `/search/${encodeURIComponent(search)}` + buildQuerystring(queryParams);
99}
1010
1111export function linkToChart(patp: string): string {
1212 if (patp) {
13− return `/chart/${patp}`;
13+ return `/chart/${encodeURIComponent(patp)}`;
1414 }
1515 return `/chart`;
1616}
ui/src/routes/Home.svelte+1−6
@@ -12,6 +12,7 @@
1212 import ValidationProblems from '@/ValidationProblems.svelte';
1313 import HomeScreenTiles from '@/HomeScreenTiles.svelte';
1414 export let params: any = {};
15+
1516 let analysis: SearchAnalysis = new SearchAnalysis();
1617 let sigilQuery: SigilQuery = new SigilQuery('planet');
1718 let navigating = false;
@@ -34,21 +35,17 @@
3435 urlSearchMode = getUrlSearchMode(searchParams);
3536 urlChangedSinceShipQuery = true;
3637 navigating = false;
37− console.log('url changed most recently!');
3838 }
3939 }
4040 $: {
4141 urlChangedSinceShipQuery = (search || true) && searchMode && false;
4242 }
4343 function autoNav() {
44− console.log('judgement day, mfers!');
4544 if ((search !== urlSearch || searchMode !== urlSearchMode)) {
4645 if (urlChangedSinceShipQuery) {
4746 search = urlSearch;
4847 searchMode = urlSearchMode;
49− console.log('url wins!');
5048 } else {
51− console.log('search wins!');
5249 navigating = true;
5350 // create a temp one because the real one doesn't always update in time!
5451 let tempAnalysis = new SearchAnalysis(search);
@@ -56,11 +53,9 @@
5653 navToChart(tempAnalysis.patpIsValid);
5754 } else {
5855 if (tempAnalysis.patpIsValid) {
59− console.log('going to ship view!', search, tempAnalysis.patp);
6056 navToShipView(tempAnalysis.patp, true);
6157 } else {
6258 const fromShipMode = params.mode === 'ship';
63− console.log('going to search view!');
6459 navToSearch(fromShipMode);
6560 }
6661 }
ui/src/stores/keys.tsnew file+68
@@ -0,0 +1,68 @@
1+import { writable, get } from 'svelte/store';
2+
3+const docs = {};
4+
5+class MapWithDefault<K, V> extends Map<K, V> {
6+ default: Function;
7+
8+ get(key: K) {
9+ if (!this.has(key)) {
10+ this.set(key, this.default());
11+ }
12+ return super.get(key);
13+ }
14+
15+ constructor(defaultFunction: () => V) {
16+ super();
17+ this.default = defaultFunction;
18+ }
19+}
20+
21+type Bindings = MapWithDefault<string, MapWithDefault<string, Function>>;
22+interface KeysStore {
23+ keydown: Bindings;
24+ keyup: Bindings;
25+ globalkeydown: Bindings;
26+ globalkeyup: Bindings;
27+};
28+function newEmptyBindings(): Bindings {
29+ return new MapWithDefault(() => new MapWithDefault(() => Function));
30+}
31+const initStore: KeysStore = {
32+ keydown: newEmptyBindings(),
33+ keyup: newEmptyBindings(),
34+ globalkeydown: newEmptyBindings(),
35+ globalkeyup: newEmptyBindings(),
36+};
37+
38+const store = writable(initStore);
39+const { subscribe, update, set } = store;
40+
41+function runCallbacks(bindings: Bindings, event: KeyboardEvent) {
42+ let els = [];
43+ if (event.ctrlKey) els.push('ctrl');
44+ if (event.metaKey) els.push('cmd');
45+ if (event.altKey) els.push('alt');
46+ if (event.shiftKey) els.push('shift');
47+ els.push(event.key == ' ' ? 'Space' : event.key.toLowerCase());
48+ let combo = els.join('+');
49+ // console.log(event);
50+ let callbacks = bindings.get(combo);
51+ if (callbacks) {
52+ callbacks.forEach((callback) => {
53+ try {
54+ if (callback)
55+ callback(event);
56+ } catch (e) {
57+ console.error(e);
58+ }
59+ });
60+ }
61+}
62+
63+export default {
64+ subscribe,
65+ update,
66+ set,
67+ runCallbacks,
68+};
ui/src/styles/tailwind-output.css+4
@@ -1871,6 +1871,10 @@ Ensure the default browser behavior of the `hidden` attribute.
18711871 --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);
18721872 box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
18731873}
1874+.blur {
1875+ --tw-blur: blur(8px);
1876+ filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
1877+}
18741878.drop-shadow {
18751879 --tw-drop-shadow: drop-shadow(0 1px 2px rgb(0 0 0 / 0.1)) drop-shadow(0 1px 1px rgb(0 0 0 / 0.06));
18761880 filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);