on main help sign in
i'm the true sigilante
commit262ca0e05fb9a451d74c237c08fb445c2ceaebbe authorJohn Hyde <johnhydefour@gmail.com> dateMon 8 Aug 2022, 20:47 (4 years ago) parent77cb7ed
19 files changed+688−39
ui/src/components/HomeScreenTiles.svelte+3−3 ui/src/components/ShipListing.svelte+1−1 ui/src/components/ShipView.svelte+1−1 ui/src/components/Sigil.svelte+16−6 ui/src/components/docs/Doc.svelte+1−1 ui/src/components/search/SearchBar.svelte+35−11 ui/src/components/search/sigil/ClanSelect.svelte+41 ui/src/components/search/sigil/ClanSelectOption.svelte+17 ui/src/components/search/sigil/GeonSelector.svelte+29 ui/src/components/search/sigil/MiniFakeSigil.svelte+25 ui/src/components/search/sigil/SigilInput.svelte+56 ui/src/components/search/sigil/Symbol.svelte+41 ui/src/components/search/sigil/SymbolInput.svelte+20 ui/src/components/search/sigil/SymbolInputModeSelector.svelte+27 ui/src/components/search/sigil/SymbolInputModeSelectorButton.svelte+38 ui/src/lib/sigil.ts+55 ui/src/lib/utils.ts+4 ui/src/styles/tailwind-output.css+248−16 ui/src/types/sigil.ts+30
ui/src/components/HomeScreenTiles.svelte+3−3
@@ -17,21 +17,21 @@
1717</script>
1818
1919<div class="pt-10 max-w-md w-full flex flex-wrap gap-5 justify-center">
20− <div class="rounded-lg bg-white flex-grow max-w-xs">
20+ <div class="rounded-lg bg-white grow max-w-xs">
2121 <DocLink modal path="astrolabe">
2222 <div class="p-5 text-xl text-center">
2323 About Astrolabe
2424 </div>
2525 </DocLink>
2626 </div>
27− <div class="rounded-lg bg-white flex-grow max-w-xs">
27+ <div class="rounded-lg bg-white grow max-w-xs">
2828 <DocLink modal path="">
2929 <div class="p-5 text-xl text-center">
3030 About Urbit IDs
3131 </div>
3232 </DocLink>
3333 </div>
34− <a class="rounded-lg bg-navy border border-gold flex-grow max-w-s" use:link href="/chart">
34+ <a class="rounded-lg bg-navy border border-gold grow max-w-s" use:link href="/chart">
3535 <p class="p-5 text-xl text-center text-white">
3636 <br/>
3737 Star Chart
ui/src/components/ShipListing.svelte+1−1
@@ -25,7 +25,7 @@ on:click={handleClick}
2525on:click
2626>
2727 <div class="flex flex-wrap p-4 items-center">
28− <Sigil {patp} size={48} />
28+ <Sigil {patp} size={48} useNew />
2929 <!-- {#if ship.avatar}
3030 <img src={ship.avatar} class="h-12 w-12 mr-4" />
3131 {/if} -->
ui/src/components/ShipView.svelte+1−1
@@ -181,7 +181,7 @@
181181<div class="p-8 max-w-md w-full rounded-lg bg-white flex flex-col md:max-w-2xl">
182182 <div class="flex flex-col items-center space-y-4 md:flex-row md:items-start md:space-x-8 md:space-y-0">
183183 <div class="w-full max-w-[256px] md:min-w-[256px] flex-shrink-0">
184− <Sigil patp={patp} size={256} />
184+ <Sigil patp={patp} size={256} useNew />
185185 </div>
186186 <div class="grow w-full min-w-0">
187187 <h2 class="text-2xl text-center">
ui/src/components/Sigil.svelte+16−6
@@ -2,6 +2,7 @@
22 import { clan } from 'urbit-ob';
33 import { sigil as sigil1, stringRenderer as sr1 } from '@tlon/sigil-js';
44 import { sigil as sigil2, stringRenderer as sr2 } from '@johnhyde/sigil-js';
5+ import { sigilScalingFunction } from 'lib/sigil';
56 import moonPng from '../assets/moon.png';
67 import cometSvg from '../assets/comet.svg';
78
@@ -20,15 +21,24 @@
2021 if (displaySigil) {
2122 let sigil = sigil1;
2223 let sr = sr1;
24+ let svgAST;
2325 if (useNew) {
2426 sigil = sigil2;
2527 sr = sr2;
28+ svgAST = sigil({
29+ patp: patp,
30+ size,
31+ colors: [bgColor, fgColor],
32+ autoScaleStrokes: true,
33+ strokeScalingFunctionV2: sigilScalingFunction,
34+ });
35+ } else {
36+ svgAST = sigil({
37+ patp: patp,
38+ size,
39+ colors: [bgColor, fgColor],
40+ });
2641 }
27− const svgAST = sigil({
28− patp: patp,
29− size,
30− colors: [bgColor, fgColor],
31− });
3242 svgAST.attributes.preserveAspectRatio = 'xMidYMin slice';
3343 svgString = sr(svgAST);
3444 if (altBgColor) {
@@ -42,7 +52,7 @@
4252 }
4353</script>
4454
45−<div class="flex-grow" style:max-width="{size}px">
55+<div class="grow" style:max-width="{size}px">
4656 {#if displaySigil}
4757 {@html svgString}
4858 {:else}
ui/src/components/docs/Doc.svelte+1−1
@@ -26,7 +26,7 @@
2626 {:else}
2727 <div class="w-7 h-7" />
2828 {/if}
29− <div class="flex-grow text-center text-xl">
29+ <div class="grow text-center text-xl">
3030 Help
3131 </div>
3232 <CloseButton on:click={close} />
ui/src/components/search/SearchBar.svelte+35−11
@@ -1,15 +1,20 @@
11<script lang="ts">
2+ import SigilInput from './sigil/SigilInput.svelte';
3+
24
35 import { SearchAnalysis } from 'lib/id';
6+ import { SigilQuery } from 'lib/sigil';
47
58 export let patp: string = undefined;
69 export let searchQuery: RegExp = /~der/;
710 export let analysis: SearchAnalysis = new SearchAnalysis();
811 export let search = '';
12+ export let sigilQuery: SigilQuery = new SigilQuery('planet');
913 export let pauseAnalysis: boolean = false;
10− let debouncedSearch = search;
11−
1214 export let placeholderText = "Search by @p or #. '*' matches any character";
15+
16+ let debouncedSearch = search;
17+ let showSigilInput: boolean = false;
1318
1419 $: {
1520 if (!pauseAnalysis) {
@@ -34,16 +39,35 @@
3439 debouncedSearch = search;
3540 }, 300);
3641 }
42+
43+ function openSigilInput() {
44+ showSigilInput = true;
45+ }
46+
47+ function closeSigilInput() {
48+ showSigilInput = false;
49+ }
3750</script>
3851
39−<div class="relative max-w-md rounded-full w-full">
40− <input type="text" class="p-4 py-2 w-full rounded-full"
41− spellcheck="false"
42− placeholder={placeholderText}
43− bind:value={search}
44− autofocus
52+<div class="relative max-w-md w-full">
53+ {#if showSigilInput}
54+ <div class="rounded-2xl bg-white overflow-hidden">
55+ <SigilInput bind:sigilQuery></SigilInput>
56+ </div>
57+ <button
58+ on:click={closeSigilInput}
59+ class="absolute top-2 right-2 w-20 h-6 rounded-full bg-black transition-opacity opacity-10 hover:opacity-40"
60+ />
61+ {:else}
62+ <input type="text" class="p-4 py-2 w-full rounded-full"
63+ spellcheck="false"
64+ placeholder={placeholderText}
65+ bind:value={search}
66+ autofocus
67+ />
68+ <button
69+ on:click={openSigilInput}
70+ class="absolute top-1 right-1 w-8 h-8 rounded-full bg-black transition-opacity opacity-10 hover:opacity-40"
4571 />
46− <button
47− class="absolute right-0 w-10 h-10 rounded-full bg-black transition-opacity opacity-10 hover:opacity-40"
48− />
72+ {/if}
4973</div>
ui/src/components/search/sigil/ClanSelect.sveltenew file+41
@@ -0,0 +1,41 @@
1+<script lang="ts">
2+ import ClanSelectOption from './ClanSelectOption.svelte';
3+
4+ import { capitalize } from 'lib/utils';
5+ import { CLANS, Clan } from 'types/sigil';
6+
7+ // const CLANS = ['galaxy', 'star', 'planet'] as const;
8+ // const ClanTuple = typeof CLANS;
9+ // type Clan = typeof ClanTuple[number];
10+
11+ export let sigilSearch = {};
12+ export let clan: Clan = 'planet';
13+
14+ let dropdownOpen = false;
15+
16+ function selectClan(itemClan: typeof clan) {
17+ clan = itemClan;
18+ dropdownOpen = false;
19+ }
20+</script>
21+
22+<div class="text-xl text-center bg-white">
23+ <p
24+ on:click={() => dropdownOpen = true}
25+ class=" py-2"
26+ >
27+ {capitalize(clan)}
28+ <span class="font-mono">▼</span>
29+ </p>
30+ {#if dropdownOpen}
31+ <div class="absolute top-1 w-full flex flex-col mx-auto divide-y bg-white">
32+ <h3 class="py-2" on:click={() => dropdownOpen = false}>
33+ Select Sigil Type
34+ </h3>
35+ {#each CLANS as itemClan}
36+ <ClanSelectOption clan={itemClan} on:click={() => selectClan(itemClan)} />
37+ {/each}
38+ </div>
39+ {/if}
40+</div>
41+
ui/src/components/search/sigil/ClanSelectOption.sveltenew file+17
@@ -0,0 +1,17 @@
1+<script lang="ts">
2+ import { capitalize } from 'lib/utils';
3+ import MiniFakeSigil from './MiniFakeSigil.svelte';
4+
5+ export let sigilSearch = {};
6+ export let clan: ('galaxy' | 'star' | 'planet') = 'planet';
7+
8+ let dropdownOpen = true;
9+</script>
10+
11+<div class="flex items-center justify-center gap-2 py-2" on:click>
12+ <h3>
13+ {capitalize(clan)}:
14+ </h3>
15+ <MiniFakeSigil {clan} />
16+</div>
17+
ui/src/components/search/sigil/GeonSelector.sveltenew file+29
@@ -0,0 +1,29 @@
1+<script lang="ts">
2+ import Symbol from './Symbol.svelte';
3+
4+ import type { PartType, SymbolQuery } from 'types/sigil';
5+ import { GEONS } from 'types/sigil';
6+
7+ export let symbolQuery: SymbolQuery;
8+ export let inputMode: PartType;
9+
10+ let size = 48;
11+
12+ function setGeon(geon) {
13+ symbolQuery.geon = geon;
14+ inputMode = undefined;
15+ }
16+</script>
17+
18+<div on:click={() => inputMode = undefined}>
19+ done
20+</div>
21+<div on:click={() => setGeon(undefined)}>
22+ clear
23+</div>
24+{#each GEONS as geon}
25+ <div style:width="{size}px" on:click={() => setGeon(geon)}>
26+ <Symbol components={[geon]} {size} />
27+ </div>
28+{/each}
29+
ui/src/components/search/sigil/MiniFakeSigil.sveltenew file+25
@@ -0,0 +1,25 @@
1+<script lang="ts">
2+ import { sigilFromSymbols, stringRenderer as sr, symbolFromDef } from '@johnhyde/sigil-js';
3+
4+ export let clan: ('galaxy' | 'star' | 'planet') = 'planet';
5+
6+ let size = 32;
7+ let squareDef = ['gci'];
8+ let square = symbolFromDef(squareDef);
9+ let sigilProps = {
10+ size,
11+ renderer: sr,
12+ colors: ['white', 'black'],
13+ icon: true,
14+ }
15+ let sigils = {
16+ galaxy: sigilFromSymbols([square], sigilProps),
17+ star: sigilFromSymbols([square, square], sigilProps),
18+ planet: sigilFromSymbols([square, square, square, square], sigilProps),
19+ }
20+ $: sigilWidth = (clan === 'galaxy' ? size / 2 : size);
21+</script>
22+
23+<div style:width="{sigilWidth}px">
24+ {@html sigils[clan]}
25+</div>
ui/src/components/search/sigil/SigilInput.sveltenew file+56
@@ -0,0 +1,56 @@
1+<script lang="ts">
2+ import SymbolInputModeSelector from './SymbolInputModeSelector.svelte';
3+ import SymbolInput from './SymbolInput.svelte';
4+ import ClanSelect from './ClanSelect.svelte';
5+
6+ import type { PartType } from 'types/sigil';
7+ import { SigilQuery } from 'lib/sigil';
8+
9+ export let sigilQuery: SigilQuery;
10+ // export let clan: ('galaxy' | 'star' | 'planet') = 'planet';
11+ let focusedSymbolIndex: number = 0;
12+ let inputMode: PartType = undefined;
13+
14+ // let symbols: SymbolQuery = [];
15+
16+ $: gridClasses = (sigilQuery.clan === 'galaxy' ? 'w-[50%]' : 'grid grid-cols-2 items-center');
17+
18+ $: focusedSymbol = sigilQuery.activeSymbols[focusedSymbolIndex];
19+ $: {
20+ const numActiveSymbols = sigilQuery.activeSymbols.length;
21+ if (focusedSymbolIndex >= numActiveSymbols) {
22+ focusedSymbolIndex = numActiveSymbols - 1;
23+ }
24+ }
25+ $: {
26+ if (!focusedSymbol.geon) {
27+ inputMode = 'geon';
28+ }
29+ }
30+ // function updateSigilQuery() {
31+ // sigilQuery.setSymbol(focusedSymbol, focusedSymbolIndex);
32+ // }
33+ // $: focusedSymbol, updateSigilQuery();
34+</script>
35+
36+<div class="">
37+ <ClanSelect bind:clan={sigilQuery.clan} />
38+ <div class="flex flex-col xs:flex-row">
39+ <div class="grow-[2] mx-auto w-full xs:m-0 xs:max-w-[18rem]">
40+ <div class={`max-w-[18rem] mx-auto p-4 ${gridClasses} xs:rounded-bl-2xl`}>
41+ {#each sigilQuery.activeSymbols as symbol, index}
42+ <SymbolInput
43+ bind:symbolQuery={symbol}
44+ focused={index === focusedSymbolIndex}
45+ on:click={() => focusedSymbolIndex = index}
46+ />
47+ {/each}
48+ </div>
49+ </div>
50+ <div class="bg-green-100 grow rounded-b-2xl xs:rounded-bl-none">
51+ {inputMode}
52+ <SymbolInputModeSelector bind:symbolQuery={focusedSymbol} bind:inputMode />
53+ </div>
54+ </div>
55+ {sigilQuery.string}
56+</div>
ui/src/components/search/sigil/Symbol.sveltenew file+41
@@ -0,0 +1,41 @@
1+<script lang="ts">
2+ import { sigilFromSymbols, stringRenderer as sr, symbolFromDef } from '@johnhyde/sigil-js';
3+ import { sigilScalingFunction } from 'lib/sigil';
4+
5+ export let components: string[];
6+ export let size: number;
7+ export let fgColor: string = 'black';
8+ export let bgColor: string = 'white';
9+ export let altBgColor: string = undefined;
10+ export let inverted: boolean = false;
11+ let svgString: any;
12+
13+ $: {
14+ const symbol = symbolFromDef(components);
15+ let colors = [bgColor, fgColor];
16+ let tempAltBgColor = altBgColor;
17+ if (inverted) {
18+ colors = [fgColor, bgColor];
19+ if (!tempAltBgColor) tempAltBgColor = bgColor;
20+ }
21+ const svgAST = sigilFromSymbols(
22+ [symbol],
23+ {
24+ size,
25+ colors,
26+ margin: false,
27+ autoScaleStrokes: true,
28+ strokeScalingFunctionV2: sigilScalingFunction,
29+ },
30+ );
31+ svgAST.attributes.preserveAspectRatio = 'xMidYMin slice';
32+ svgString = sr(svgAST);
33+ if (tempAltBgColor) {
34+ svgString = svgString.replace(/rect fill="[^"]+"/, `rect fill="${tempAltBgColor}"`);
35+ }
36+ }
37+</script>
38+
39+<div class="aspect-square" style:max-width="{size}px" on:click>
40+ {@html svgString}
41+</div>
ui/src/components/search/sigil/SymbolInput.sveltenew file+20
@@ -0,0 +1,20 @@
1+<script lang="ts">
2+ import Symbol from './Symbol.svelte';
3+
4+ import type { SymbolQuery } from 'types/sigil';
5+
6+ export let symbolQuery: SymbolQuery;
7+ export let focused: boolean = false;
8+ let size = 128;
9+
10+ $: focusedClasses = focused ? 'border border-gold-s1 border-2' : '';
11+</script>
12+
13+<div class="aspect-square flex {focusedClasses}" style:max-width="{size}px" on:click>
14+ {#if symbolQuery.components.length > 0}
15+ <Symbol components={symbolQuery.components} {size} inverted={!symbolQuery.geon} />
16+ {symbolQuery.geon}
17+ {:else}
18+ <div class="m-1 rounded-sm border-4 border-gray-500 grow"></div>
19+ {/if}
20+</div>
ui/src/components/search/sigil/SymbolInputModeSelector.sveltenew file+27
@@ -0,0 +1,27 @@
1+<script lang="ts">
2+ import GeonSelector from './GeonSelector.svelte';
3+ import SymbolInputModeSelectorButton from './SymbolInputModeSelectorButton.svelte';
4+
5+ import Symbol from './Symbol.svelte';
6+
7+ import type { PartType, SymbolQuery } from 'types/sigil';
8+ import { PART_TYPES } from 'types/sigil';
9+
10+ export let symbolQuery: SymbolQuery;
11+ export let inputMode: PartType;
12+
13+ let size = 48;
14+</script>
15+
16+<div class="grid grid-rows-2 grid-flow-col xs:grid-cols-2 xs:grid-flow-row place-content-center justify-center">
17+ {#if inputMode === 'geon'}
18+ <GeonSelector bind:symbolQuery bind:inputMode />
19+ {:else}
20+ {#each PART_TYPES as partType}
21+ <SymbolInputModeSelectorButton {partType} focused={inputMode === partType}
22+ on:click={() => inputMode = partType}
23+ />
24+ {/each}
25+ {/if}
26+</div>
27+
ui/src/components/search/sigil/SymbolInputModeSelectorButton.sveltenew file+38
@@ -0,0 +1,38 @@
1+<script lang="ts">
2+ import Symbol from './Symbol.svelte';
3+
4+ import type { PartType, SymbolQuery } from 'types/sigil';
5+ import { PART_TYPES } from 'types/sigil';
6+
7+ export let partType: PartType;
8+ export let focused: boolean = false;
9+
10+ let size = 48;
11+ $: focusedClasses = focused ? 'border border-gold-s1 border-2' : '';
12+</script>
13+
14+<div class="border-2 w-[48px] {focusedClasses}" on:click>
15+ {#if partType === 'geon'}
16+ <Symbol components={['ghb']} {size} />
17+ {:else if partType === 'line'}
18+ <Symbol components={['lvf', 'lvfl', 'lvfr', 'lhf', 'lhft', 'lhfb', 'lff', 'lbf']} {size} inverted />
19+ {:else if partType === 'arc'}
20+ <Symbol components={['abl202', 'abl402', 'abl602', 'abl802' ]} {size} inverted />
21+ {:else if partType === 'ring'}
22+ <Symbol components={['rm1', 'rm2', 'rm3']} {size} inverted />
23+ {:else if partType === 'circle'}
24+ <Symbol components={[
25+ // 'ctr1mb', 'cbr1mb', 'cbl1mb', 'ctl1mb',
26+ 'cbr3mb', 'ctl3mb',
27+ 'cmmb',
28+ 'ct2mb', 'cl2mb', 'cb2mb', 'cr2mb',
29+ ]} {size} inverted />
30+ {:else if partType === 'donut'}
31+ <Symbol components={['dbl', /*'dl',*/ 'dm', 'dtl', 'dtr']} {size} inverted />
32+ {:else if partType === 'bezier'}
33+ <Symbol components={['b1l1', 'b1l2', 'b1l3']} {size} inverted />
34+ {:else}
35+ not {partType}
36+ {/if}
37+</div>
38+
ui/src/lib/sigil.tsnew file+55
@@ -0,0 +1,55 @@
1+import type { Clan } from 'types/sigil';
2+import { SymbolQuery } from 'types/sigil';
3+
4+export class SigilQuery {
5+ clan: Clan;
6+ symbols: SymbolQuery[];
7+
8+ constructor(clan: Clan = 'planet', symbols = []) {
9+ this.clan = clan;
10+ this.symbols = symbols;
11+ this.padSymbols();
12+ }
13+
14+ padSymbols() {
15+ if (this.symbols.length === 1) {
16+ this.symbols.unshift(new SymbolQuery());
17+ }
18+ while (this.symbols.length < 4) {
19+ this.symbols.push(new SymbolQuery());
20+ }
21+ this.symbols = this.symbols.slice(0, 4);
22+ this.symbols.forEach((symbol, i) => {
23+ if (i % 2 === 0) {
24+ symbol.symbolType = 'prefix';
25+ // symbol.components = ['gci', 'abr622']
26+ } else {
27+ symbol.symbolType = 'suffix';
28+ symbol.components = ['gsq', 'abl202']
29+ }
30+ });
31+ this.symbols = this.symbols;
32+ }
33+
34+ get activeSymbols() {
35+ if (this.clan === 'galaxy')
36+ return [this.symbols[1]];
37+ if (this.clan === 'star')
38+ return this.symbols.slice(0, 2);
39+ return this.symbols;
40+ }
41+
42+ get string(): string {
43+ return this.symbols.map((symbol) => symbol.components.join()).join(' | ')
44+ }
45+
46+ setSymbol(symbol: SymbolQuery, activeSymbolIndex: number) {
47+ if (activeSymbolIndex >= this.activeSymbols.length)
48+ throw new Error(`Symbol index ${activeSymbolIndex} out of range for clan: ${this.clan}`);
49+ this.activeSymbols[activeSymbolIndex] = symbol;
50+ }
51+}
52+
53+export function sigilScalingFunction(size: number): number {
54+ return 3.5 * Math.sqrt(51 / size);
55+}
ui/src/lib/utils.ts+4
@@ -41,3 +41,7 @@ export function toggleStoreKey(store, key) {
4141 [key]: !store[key],
4242 }));
4343}
44+
45+export function capitalize(string: string): string {
46+ return string.charAt(0).toUpperCase() + string.slice(1);
47+}
ui/src/styles/tailwind-output.css+248−16
@@ -532,8 +532,23 @@ Ensure the default browser behavior of the `hidden` attribute.
532532.-bottom-3 {
533533 bottom: -0.75rem;
534534}
535−.z-50 {
536− z-index: 50;
535+.top-1 {
536+ top: 0.25rem;
537+}
538+.right-1 {
539+ right: 0.25rem;
540+}
541+.top-2 {
542+ top: 0.5rem;
543+}
544+.right-2 {
545+ right: 0.5rem;
546+}
547+.right-4 {
548+ right: 1rem;
549+}
550+.right-3 {
551+ right: 0.75rem;
537552}
538553.z-20 {
539554 z-index: 20;
@@ -541,6 +556,9 @@ Ensure the default browser behavior of the `hidden` attribute.
541556.z-10 {
542557 z-index: 10;
543558}
559+.z-50 {
560+ z-index: 50;
561+}
544562.m-4 {
545563 margin: 1rem;
546564}
@@ -553,9 +571,11 @@ Ensure the default browser behavior of the `hidden` attribute.
553571.-m-2 {
554572 margin: -0.5rem;
555573}
556−.mx-4 {
557− margin-left: 1rem;
558− margin-right: 1rem;
574+.m-auto {
575+ margin: auto;
576+}
577+.m-1 {
578+ margin: 0.25rem;
559579}
560580.mx-auto {
561581 margin-left: auto;
@@ -573,6 +593,10 @@ Ensure the default browser behavior of the `hidden` attribute.
573593 margin-top: 0.75rem;
574594 margin-bottom: 0.75rem;
575595}
596+.mx-4 {
597+ margin-left: 1rem;
598+ margin-right: 1rem;
599+}
576600.mx-8 {
577601 margin-left: 2rem;
578602 margin-right: 2rem;
@@ -657,6 +681,9 @@ Ensure the default browser behavior of the `hidden` attribute.
657681.mr-3 {
658682 margin-right: 0.75rem;
659683}
684+.ml-auto {
685+ margin-left: auto;
686+}
660687.-mt-0\.5 {
661688 margin-top: -0.125rem;
662689}
@@ -684,6 +711,9 @@ Ensure the default browser behavior of the `hidden` attribute.
684711.hidden {
685712 display: none;
686713}
714+.aspect-square {
715+ aspect-ratio: 1 / 1;
716+}
687717.h-full {
688718 height: 100%;
689719}
@@ -723,6 +753,24 @@ Ensure the default browser behavior of the `hidden` attribute.
723753.h-24 {
724754 height: 6rem;
725755}
756+.h-20 {
757+ height: 5rem;
758+}
759+.h-48 {
760+ height: 12rem;
761+}
762+.h-64 {
763+ height: 16rem;
764+}
765+.h-72 {
766+ height: 18rem;
767+}
768+.h-16 {
769+ height: 4rem;
770+}
771+.h-auto {
772+ height: auto;
773+}
726774.min-h-\[20px\] {
727775 min-height: 20px;
728776}
@@ -732,6 +780,9 @@ Ensure the default browser behavior of the `hidden` attribute.
732780.min-h-0 {
733781 min-height: 0px;
734782}
783+.min-h-\[20rem\] {
784+ min-height: 20rem;
785+}
735786.w-full {
736787 width: 100%;
737788}
@@ -765,6 +816,36 @@ Ensure the default browser behavior of the `hidden` attribute.
765816.w-6 {
766817 width: 1.5rem;
767818}
819+.w-48 {
820+ width: 12rem;
821+}
822+.w-24 {
823+ width: 6rem;
824+}
825+.w-20 {
826+ width: 5rem;
827+}
828+.w-8 {
829+ width: 2rem;
830+}
831+.w-64 {
832+ width: 16rem;
833+}
834+.w-72 {
835+ width: 18rem;
836+}
837+.w-\[50\%\] {
838+ width: 50%;
839+}
840+.w-\[18rem\] {
841+ width: 18rem;
842+}
843+.w-16 {
844+ width: 4rem;
845+}
846+.w-\[48px\] {
847+ width: 48px;
848+}
768849.min-w-0 {
769850 min-width: 0px;
770851}
@@ -786,6 +867,9 @@ Ensure the default browser behavior of the `hidden` attribute.
786867.max-w-2xl {
787868 max-width: 42rem;
788869}
870+.max-w-\[18rem\] {
871+ max-width: 18rem;
872+}
789873.flex-shrink-0 {
790874 flex-shrink: 0;
791875}
@@ -801,6 +885,9 @@ Ensure the default browser behavior of the `hidden` attribute.
801885.grow {
802886 flex-grow: 1;
803887}
888+.grow-\[2\] {
889+ flex-grow: 2;
890+}
804891.border-collapse {
805892 border-collapse: collapse;
806893}
@@ -837,21 +924,45 @@ Ensure the default browser behavior of the `hidden` attribute.
837924.resize {
838925 resize: both;
839926}
927+.grid-flow-col {
928+ grid-auto-flow: column;
929+}
840930.grid-cols-3 {
841931 grid-template-columns: repeat(3, minmax(0, 1fr));
842932}
933+.grid-cols-2 {
934+ grid-template-columns: repeat(2, minmax(0, 1fr));
935+}
936+.grid-cols-1 {
937+ grid-template-columns: repeat(1, minmax(0, 1fr));
938+}
939+.grid-rows-2 {
940+ grid-template-rows: repeat(2, minmax(0, 1fr));
941+}
942+.grid-rows-none {
943+ grid-template-rows: none;
944+}
843945.flex-col {
844946 flex-direction: column;
845947}
846948.flex-wrap {
847949 flex-wrap: wrap;
848950}
951+.place-content-center {
952+ place-content: center;
953+}
954+.place-items-stretch {
955+ place-items: stretch;
956+}
849957.content-center {
850958 align-content: center;
851959}
852960.items-center {
853961 align-items: center;
854962}
963+.justify-end {
964+ justify-content: flex-end;
965+}
855966.justify-center {
856967 justify-content: center;
857968}
@@ -928,6 +1039,9 @@ Ensure the default browser behavior of the `hidden` attribute.
9281039 --tw-divide-opacity: 1;
9291040 border-color: rgb(252 211 77 / var(--tw-divide-opacity));
9301041}
1042+.self-center {
1043+ align-self: center;
1044+}
9311045.justify-self-start {
9321046 justify-self: start;
9331047}
@@ -970,6 +1084,26 @@ Ensure the default browser behavior of the `hidden` attribute.
9701084.rounded-sm {
9711085 border-radius: 0.125rem;
9721086}
1087+.rounded-2xl {
1088+ border-radius: 1rem;
1089+}
1090+.rounded-3xl {
1091+ border-radius: 1.5rem;
1092+}
1093+.rounded-b-2xl {
1094+ border-bottom-right-radius: 1rem;
1095+ border-bottom-left-radius: 1rem;
1096+}
1097+.rounded-t-2xl {
1098+ border-top-left-radius: 1rem;
1099+ border-top-right-radius: 1rem;
1100+}
1101+.rounded-br-2xl {
1102+ border-bottom-right-radius: 1rem;
1103+}
1104+.rounded-bl-none {
1105+ border-bottom-left-radius: 0px;
1106+}
9731107.border {
9741108 border-width: 1px;
9751109}
@@ -979,6 +1113,9 @@ Ensure the default browser behavior of the `hidden` attribute.
9791113.border-8 {
9801114 border-width: 8px;
9811115}
1116+.border-2 {
1117+ border-width: 2px;
1118+}
9821119.border-l-8 {
9831120 border-left-width: 8px;
9841121}
@@ -1018,6 +1155,30 @@ Ensure the default browser behavior of the `hidden` attribute.
10181155 --tw-border-opacity: 1;
10191156 border-color: rgb(156 163 175 / var(--tw-border-opacity));
10201157}
1158+.border-red-500 {
1159+ --tw-border-opacity: 1;
1160+ border-color: rgb(239 68 68 / var(--tw-border-opacity));
1161+}
1162+.border-gold-s5 {
1163+ --tw-border-opacity: 1;
1164+ border-color: rgb(126 106 39 / var(--tw-border-opacity));
1165+}
1166+.border-gold-s2 {
1167+ --tw-border-opacity: 1;
1168+ border-color: rgb(202 169 62 / var(--tw-border-opacity));
1169+}
1170+.border-gold-s1 {
1171+ --tw-border-opacity: 1;
1172+ border-color: rgb(227 190 69 / var(--tw-border-opacity));
1173+}
1174+.border-gray-800 {
1175+ --tw-border-opacity: 1;
1176+ border-color: rgb(31 41 55 / var(--tw-border-opacity));
1177+}
1178+.border-gray-500 {
1179+ --tw-border-opacity: 1;
1180+ border-color: rgb(107 114 128 / var(--tw-border-opacity));
1181+}
10211182.bg-navy {
10221183 --tw-bg-opacity: 1;
10231184 background-color: rgb(31 36 60 / var(--tw-bg-opacity));
@@ -1122,6 +1283,25 @@ Ensure the default browser behavior of the `hidden` attribute.
11221283 --tw-bg-opacity: 1;
11231284 background-color: rgb(227 190 69 / var(--tw-bg-opacity));
11241285}
1286+.bg-blue-800 {
1287+ --tw-bg-opacity: 1;
1288+ background-color: rgb(30 64 175 / var(--tw-bg-opacity));
1289+}
1290+.bg-green-900 {
1291+ --tw-bg-opacity: 1;
1292+ background-color: rgb(20 83 45 / var(--tw-bg-opacity));
1293+}
1294+.bg-green-100 {
1295+ --tw-bg-opacity: 1;
1296+ background-color: rgb(220 252 231 / var(--tw-bg-opacity));
1297+}
1298+.bg-blue-200 {
1299+ --tw-bg-opacity: 1;
1300+ background-color: rgb(191 219 254 / var(--tw-bg-opacity));
1301+}
1302+.fill-transparent {
1303+ fill: transparent;
1304+}
11251305.p-5 {
11261306 padding: 1.25rem;
11271307}
@@ -1159,10 +1339,6 @@ Ensure the default browser behavior of the `hidden` attribute.
11591339 padding-top: 0px;
11601340 padding-bottom: 0px;
11611341}
1162−.py-4 {
1163− padding-top: 1rem;
1164− padding-bottom: 1rem;
1165−}
11661342.px-4 {
11671343 padding-left: 1rem;
11681344 padding-right: 1rem;
@@ -1175,6 +1351,10 @@ Ensure the default browser behavior of the `hidden` attribute.
11751351 padding-left: 0px;
11761352 padding-right: 0px;
11771353}
1354+.py-4 {
1355+ padding-top: 1rem;
1356+ padding-bottom: 1rem;
1357+}
11781358.py-2 {
11791359 padding-top: 0.5rem;
11801360 padding-bottom: 0.5rem;
@@ -1278,6 +1458,9 @@ Ensure the default browser behavior of the `hidden` attribute.
12781458.font-bold {
12791459 font-weight: 700;
12801460}
1461+.capitalize {
1462+ text-transform: capitalize;
1463+}
12811464.leading-4 {
12821465 line-height: 1rem;
12831466}
@@ -1342,21 +1525,26 @@ Ensure the default browser behavior of the `hidden` attribute.
13421525.underline-offset-2 {
13431526 text-underline-offset: 2px;
13441527}
1345−.opacity-50 {
1346− opacity: 0.5;
1347−}
13481528.opacity-25 {
13491529 opacity: 0.25;
13501530}
13511531.opacity-75 {
13521532 opacity: 0.75;
13531533}
1534+.opacity-50 {
1535+ opacity: 0.5;
1536+}
13541537.opacity-10 {
13551538 opacity: 0.1;
13561539}
13571540.outline {
13581541 outline-style: solid;
13591542}
1543+.ring {
1544+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
1545+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);
1546+ box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
1547+}
13601548.filter {
13611549 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);
13621550}
@@ -1365,6 +1553,16 @@ Ensure the default browser behavior of the `hidden` attribute.
13651553 transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
13661554 transition-duration: 150ms;
13671555}
1556+.transition-transform {
1557+ transition-property: transform;
1558+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1559+ transition-duration: 150ms;
1560+}
1561+.transition-all {
1562+ transition-property: all;
1563+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1564+ transition-duration: 150ms;
1565+}
13681566.hover\:bg-gray-200:hover {
13691567 --tw-bg-opacity: 1;
13701568 background-color: rgb(229 231 235 / var(--tw-bg-opacity));
@@ -1406,6 +1604,40 @@ Ensure the default browser behavior of the `hidden` attribute.
14061604 padding-bottom: 2rem;
14071605 }
14081606}
1607+@media (min-width: 475px) {
1608+
1609+ .xs\:m-0 {
1610+ margin: 0px;
1611+ }
1612+
1613+ .xs\:max-w-\[18rem\] {
1614+ max-width: 18rem;
1615+ }
1616+
1617+ .xs\:grid-flow-row {
1618+ grid-auto-flow: row;
1619+ }
1620+
1621+ .xs\:grid-cols-2 {
1622+ grid-template-columns: repeat(2, minmax(0, 1fr));
1623+ }
1624+
1625+ .xs\:grid-rows-none {
1626+ grid-template-rows: none;
1627+ }
1628+
1629+ .xs\:flex-row {
1630+ flex-direction: row;
1631+ }
1632+
1633+ .xs\:rounded-bl-2xl {
1634+ border-bottom-left-radius: 1rem;
1635+ }
1636+
1637+ .xs\:rounded-bl-none {
1638+ border-bottom-left-radius: 0px;
1639+ }
1640+}
14091641@media (min-width: 768px) {
14101642
14111643 .md\:mx-auto {
@@ -1417,14 +1649,14 @@ Ensure the default browser behavior of the `hidden` attribute.
14171649 min-width: 256px;
14181650 }
14191651
1420− .md\:max-w-xl {
1421− max-width: 36rem;
1422− }
1423−
14241652 .md\:max-w-2xl {
14251653 max-width: 42rem;
14261654 }
14271655
1656+ .md\:max-w-xl {
1657+ max-width: 36rem;
1658+ }
1659+
14281660 .md\:flex-row {
14291661 flex-direction: row;
14301662 }
ui/src/types/sigil.tsnew file+30
@@ -0,0 +1,30 @@
1+export const CLANS = ['galaxy', 'star', 'planet'] as const;
2+export type Clan = typeof CLANS[number];
3+
4+export const PART_TYPES = ['geon', 'line', 'arc', 'ring', 'circle', 'donut', 'bezier'] as const;
5+export type PartType = typeof PART_TYPES[number];
6+
7+export const GEONS = ['gci', 'gsq', 'ghl', 'ghr', 'ght', 'ghb', 'gq1', 'gq2', 'gq3', 'gq4', 'gfs', 'gbs'];
8+export type Geon = typeof GEONS[number];
9+
10+export type SymbolType = ('prefix' | 'suffix');
11+
12+export class SymbolQuery {
13+ symbolType: SymbolType;
14+ components: string[];
15+
16+ constructor(symbolType: SymbolType = 'suffix', components = []) {
17+ this.symbolType = symbolType;
18+ this.components = components;
19+ }
20+
21+ set geon(geon: Geon) {
22+ this.components = this.components.filter(c => c[0] !== 'g');
23+ if (geon)
24+ this.components = [geon, ...this.components];
25+ }
26+
27+ get geon(): Geon {
28+ return this.components.find(c => c[0] === 'g');
29+ }
30+}