on main help sign in
searching and clicking in star chart
a64ee97John Hyde
desk/app/astrolabe.hoon+2−2
@@ -267,7 +267,7 @@
267++ get-chart-data
267++ get-chart-data
268 ^- chart-data
268 ^- chart-data
269 =/ nas get-nas
269 =/ nas get-nas
270− :: =/ galaxies (gulf 0 15) :: todo remove
270+ :: =/ galaxies (gulf 0 30) :: todo remove
271 %+ turn galaxies
271 %+ turn galaxies
272 |= =ship
272 |= =ship
273 =/ point (need (get:orm:naive points.nas ship))
273 =/ point (need (get:orm:naive points.nas ship))
@@ -292,7 +292,7 @@
292 |= [nas=^state:naive =ship]
292 |= [nas=^state:naive =ship]
293 ^- chart-data
293 ^- chart-data
294 :: [*point-meta ~]~
294 :: [*point-meta ~]~
295− %- (run-spawned-points chart-data ship nas %all)
295+ %- (run-spawned-points chart-data ship nas %unlocked)
296 |= [agg=chart-data [spawn=^ship spawn-point=npoint]]
296 |= [agg=chart-data [spawn=^ship spawn-point=npoint]]
297 :_ agg
297 :_ agg
298 (get-point-chart-data nas spawn spawn-point)
298 (get-point-chart-data nas spawn spawn-point)
desk/lib/astrolabe.hoon+5−2
@@ -146,12 +146,15 @@
146 ++ point-meta
146 ++ point-meta
147 |= m=^point-meta
147 |= m=^point-meta
148 ^- (list [@tas json])
148 ^- (list [@tas json])
149− =/ main-pairs
149+ =/ main-pairs=(list [@tas json])
150 :~
150 :~
151 p+(ship ship.m)
151 p+(ship ship.m)
152 :: spa-count+(numb spa-count.m)
152 :: spa-count+(numb spa-count.m)
153− [%d s+dom.m]
153+ :: [%d s+dom.m]
154 ==
154 ==
155+ =? main-pairs !=(dom.m %l1)
156+ :_ main-pairs
157+ d+s+dom.m
155 ?: =(has.spo.m %same) main-pairs
158 ?: =(has.spo.m %same) main-pairs
156 :_ main-pairs
159 :_ main-pairs
157 :- %s
160 :- %s
ui/package-lock.json+102
@@ -13,7 +13,9 @@
13 "@urbit/api": "^2.1.0",
13 "@urbit/api": "^2.1.0",
14 "@urbit/http-api": "^2.1.0",
14 "@urbit/http-api": "^2.1.0",
15 "bn.js": "^5.2.0",
15 "bn.js": "^5.2.0",
16+ "graphology": "^0.24.1",
16 "lodash": "^4.17.21",
17 "lodash": "^4.17.21",
18+ "sigma": "^2.3.1",
17 "svelte-copy-to-clipboard": "^0.2.5",
19 "svelte-copy-to-clipboard": "^0.2.5",
18 "tippy.js": "^6.3.7",
20 "tippy.js": "^6.3.7",
19 "urbit-ob": "^5.0.1"
21 "urbit-ob": "^5.0.1"
@@ -318,6 +320,11 @@
318 "vite-plugin-rewrite-all": "^0.1.2"
320 "vite-plugin-rewrite-all": "^0.1.2"
319 }
321 }
320 },
322 },
323+ "node_modules/@yomguithereal/helpers": {
324+ "version": "1.1.1",
325+ "resolved": "https://registry.npmjs.org/@yomguithereal/helpers/-/helpers-1.1.1.tgz",
326+ "integrity": "sha512-UYvAq/XCA7xoh1juWDYsq3W0WywOB+pz8cgVnE1b45ZfdMhBvHDrgmSFG3jXeZSr2tMTYLGHFHON+ekG05Jebg=="
327+ },
321 "node_modules/acorn": {
328 "node_modules/acorn": {
322 "version": "7.4.1",
329 "version": "7.4.1",
323 "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
330 "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
@@ -1148,6 +1155,14 @@
1148 "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
1155 "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
1149 "integrity": "sha1-teEHm1n7XhuidxwKmTvgYKWMmbo="
1156 "integrity": "sha1-teEHm1n7XhuidxwKmTvgYKWMmbo="
1150 },
1157 },
1158+ "node_modules/events": {
1159+ "version": "3.3.0",
1160+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
1161+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
1162+ "engines": {
1163+ "node": ">=0.8.x"
1164+ }
1165+ },
1151 "node_modules/fast-glob": {
1166 "node_modules/fast-glob": {
1152 "version": "3.2.9",
1167 "version": "3.2.9",
1153 "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.9.tgz",
1168 "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.9.tgz",
@@ -1325,6 +1340,32 @@
1325 "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==",
1340 "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==",
1326 "dev": true
1341 "dev": true
1327 },
1342 },
1343+ "node_modules/graphology": {
1344+ "version": "0.24.1",
1345+ "resolved": "https://registry.npmjs.org/graphology/-/graphology-0.24.1.tgz",
1346+ "integrity": "sha512-6lNz1PNTAe9Q6ioHKrXu0Lp047sgvOoHa4qmP/8mnJWCGv2iIZPQkuHPUb2/OWDWCqHpw2hKgJLJ55X/66xmHg==",
1347+ "dependencies": {
1348+ "events": "^3.3.0",
1349+ "obliterator": "^2.0.2"
1350+ },
1351+ "peerDependencies": {
1352+ "graphology-types": ">=0.24.0"
1353+ }
1354+ },
1355+ "node_modules/graphology-types": {
1356+ "version": "0.24.4",
1357+ "resolved": "https://registry.npmjs.org/graphology-types/-/graphology-types-0.24.4.tgz",
1358+ "integrity": "sha512-CSgmycWiviCctMFO86YoUTJN1t4/PLKC5Pos2Hite+7kCUXTr+mGlDUAOgpcKG1IfFaeL9VDmTjFpzs2rTnPWw==",
1359+ "peer": true
1360+ },
1361+ "node_modules/graphology-utils": {
1362+ "version": "2.5.2",
1363+ "resolved": "https://registry.npmjs.org/graphology-utils/-/graphology-utils-2.5.2.tgz",
1364+ "integrity": "sha512-ckHg8MXrXJkOARk56ZaSCM1g1Wihe2d6iTmz1enGOz4W/l831MBCKSayeFQfowgF8wd+PQ4rlch/56Vs/VZLDQ==",
1365+ "peerDependencies": {
1366+ "graphology-types": ">=0.23.0"
1367+ }
1368+ },
1328 "node_modules/has": {
1369 "node_modules/has": {
1329 "version": "1.0.3",
1370 "version": "1.0.3",
1330 "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
1371 "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@@ -1769,6 +1810,11 @@
1769 "node": ">= 6"
1810 "node": ">= 6"
1770 }
1811 }
1771 },
1812 },
1813+ "node_modules/obliterator": {
1814+ "version": "2.0.4",
1815+ "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz",
1816+ "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ=="
1817+ },
1772 "node_modules/omit-deep": {
1818 "node_modules/omit-deep": {
1773 "version": "0.3.0",
1819 "version": "0.3.0",
1774 "resolved": "https://registry.npmjs.org/omit-deep/-/omit-deep-0.3.0.tgz",
1820 "resolved": "https://registry.npmjs.org/omit-deep/-/omit-deep-0.3.0.tgz",
@@ -2334,6 +2380,16 @@
2334 "node": ">=8"
2380 "node": ">=8"
2335 }
2381 }
2336 },
2382 },
2383+ "node_modules/sigma": {
2384+ "version": "2.3.1",
2385+ "resolved": "https://registry.npmjs.org/sigma/-/sigma-2.3.1.tgz",
2386+ "integrity": "sha512-YfQsVZf78CeIUv8EYLqp/yvhMwwBXGrbpy+rD1N2X9IqtJ11+VfQRjWfVqHDhjM8oPfWPsaJMtpWFQ8pIflvCA==",
2387+ "dependencies": {
2388+ "@yomguithereal/helpers": "^1.1.1",
2389+ "events": "^3.3.0",
2390+ "graphology-utils": "^2.5.0"
2391+ }
2392+ },
2337 "node_modules/slash": {
2393 "node_modules/slash": {
2338 "version": "4.0.0",
2394 "version": "4.0.0",
2339 "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz",
2395 "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz",
@@ -3141,6 +3197,11 @@
3141 "vite-plugin-rewrite-all": "^0.1.2"
3197 "vite-plugin-rewrite-all": "^0.1.2"
3142 }
3198 }
3143 },
3199 },
3200+ "@yomguithereal/helpers": {
3201+ "version": "1.1.1",
3202+ "resolved": "https://registry.npmjs.org/@yomguithereal/helpers/-/helpers-1.1.1.tgz",
3203+ "integrity": "sha512-UYvAq/XCA7xoh1juWDYsq3W0WywOB+pz8cgVnE1b45ZfdMhBvHDrgmSFG3jXeZSr2tMTYLGHFHON+ekG05Jebg=="
3204+ },
3144 "acorn": {
3205 "acorn": {
3145 "version": "7.4.1",
3206 "version": "7.4.1",
3146 "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
3207 "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
@@ -3701,6 +3762,11 @@
3701 "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
3762 "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
3702 "integrity": "sha1-teEHm1n7XhuidxwKmTvgYKWMmbo="
3763 "integrity": "sha1-teEHm1n7XhuidxwKmTvgYKWMmbo="
3703 },
3764 },
3765+ "events": {
3766+ "version": "3.3.0",
3767+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
3768+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
3769+ },
3704 "fast-glob": {
3770 "fast-glob": {
3705 "version": "3.2.9",
3771 "version": "3.2.9",
3706 "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.9.tgz",
3772 "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.9.tgz",
@@ -3828,6 +3894,27 @@
3828 "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==",
3894 "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==",
3829 "dev": true
3895 "dev": true
3830 },
3896 },
3897+ "graphology": {
3898+ "version": "0.24.1",
3899+ "resolved": "https://registry.npmjs.org/graphology/-/graphology-0.24.1.tgz",
3900+ "integrity": "sha512-6lNz1PNTAe9Q6ioHKrXu0Lp047sgvOoHa4qmP/8mnJWCGv2iIZPQkuHPUb2/OWDWCqHpw2hKgJLJ55X/66xmHg==",
3901+ "requires": {
3902+ "events": "^3.3.0",
3903+ "obliterator": "^2.0.2"
3904+ }
3905+ },
3906+ "graphology-types": {
3907+ "version": "0.24.4",
3908+ "resolved": "https://registry.npmjs.org/graphology-types/-/graphology-types-0.24.4.tgz",
3909+ "integrity": "sha512-CSgmycWiviCctMFO86YoUTJN1t4/PLKC5Pos2Hite+7kCUXTr+mGlDUAOgpcKG1IfFaeL9VDmTjFpzs2rTnPWw==",
3910+ "peer": true
3911+ },
3912+ "graphology-utils": {
3913+ "version": "2.5.2",
3914+ "resolved": "https://registry.npmjs.org/graphology-utils/-/graphology-utils-2.5.2.tgz",
3915+ "integrity": "sha512-ckHg8MXrXJkOARk56ZaSCM1g1Wihe2d6iTmz1enGOz4W/l831MBCKSayeFQfowgF8wd+PQ4rlch/56Vs/VZLDQ==",
3916+ "requires": {}
3917+ },
3831 "has": {
3918 "has": {
3832 "version": "1.0.3",
3919 "version": "1.0.3",
3833 "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
3920 "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@@ -4172,6 +4259,11 @@
4172 "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==",
4259 "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==",
4173 "dev": true
4260 "dev": true
4174 },
4261 },
4262+ "obliterator": {
4263+ "version": "2.0.4",
4264+ "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz",
4265+ "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ=="
4266+ },
4175 "omit-deep": {
4267 "omit-deep": {
4176 "version": "0.3.0",
4268 "version": "0.3.0",
4177 "resolved": "https://registry.npmjs.org/omit-deep/-/omit-deep-0.3.0.tgz",
4269 "resolved": "https://registry.npmjs.org/omit-deep/-/omit-deep-0.3.0.tgz",
@@ -4549,6 +4641,16 @@
4549 "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
4641 "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
4550 "dev": true
4642 "dev": true
4551 },
4643 },
4644+ "sigma": {
4645+ "version": "2.3.1",
4646+ "resolved": "https://registry.npmjs.org/sigma/-/sigma-2.3.1.tgz",
4647+ "integrity": "sha512-YfQsVZf78CeIUv8EYLqp/yvhMwwBXGrbpy+rD1N2X9IqtJ11+VfQRjWfVqHDhjM8oPfWPsaJMtpWFQ8pIflvCA==",
4648+ "requires": {
4649+ "@yomguithereal/helpers": "^1.1.1",
4650+ "events": "^3.3.0",
4651+ "graphology-utils": "^2.5.0"
4652+ }
4653+ },
4552 "slash": {
4654 "slash": {
4553 "version": "4.0.0",
4655 "version": "4.0.0",
4554 "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz",
4656 "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz",
ui/package.json+2
@@ -37,7 +37,9 @@
37 "@urbit/api": "^2.1.0",
37 "@urbit/api": "^2.1.0",
38 "@urbit/http-api": "^2.1.0",
38 "@urbit/http-api": "^2.1.0",
39 "bn.js": "^5.2.0",
39 "bn.js": "^5.2.0",
40+ "graphology": "^0.24.1",
40 "lodash": "^4.17.21",
41 "lodash": "^4.17.21",
42+ "sigma": "^2.3.1",
41 "svelte-copy-to-clipboard": "^0.2.5",
43 "svelte-copy-to-clipboard": "^0.2.5",
42 "tippy.js": "^6.3.7",
44 "tippy.js": "^6.3.7",
43 "urbit-ob": "^5.0.1"
45 "urbit-ob": "^5.0.1"
ui/scraps/tirrel-graph-main.jsnew file+198
@@ -0,0 +1,198 @@
1+// Let's first initialize sigma:
2+//
3+
4+let fetchArray = [];
5+for (let i = 0; i < 256; i++) {
6+ fetchArray.push(
7+ fetch(`./galaxies/${i.toString()}.json`).then(r => r.json())
8+ );
9+}
10+
11+let searchButton = document.getElementById('searchButton');
12+
13+Promise.all(fetchArray).then(values => {
14+ sigma.settings.minEdgeSize = 0;
15+ sigma.settings.maxEdgeSize = 0;
16+ sigma.settings.minNodeSize = 0;
17+ sigma.settings.maxNodeSize = 0;
18+ sigma.settings.zoomMin = 0.00001;
19+ sigma.settings.labelThreshold = 20;
20+ sigma.settings.font = "monospace";
21+
22+ let s = new sigma({
23+ renderer: {
24+ container: document.getElementById('container'),
25+ type: 'webgl'
26+ }
27+ });
28+ let i = 0;
29+
30+ let offsetX = 0;
31+ let offsetY = 0;
32+
33+ let a = 120000000;
34+ let b = 8000000;
35+ let theta = 0;
36+ while (i < values.length) {
37+ offsetX = 0 + (a + b * theta) * Math.cos(theta);
38+ offsetY = 0 + (a + b * theta) * Math.sin(theta);
39+
40+ galaxy(values[i], s, offsetX, offsetY);
41+
42+ theta = 0.12 * (i+1);
43+ i++;
44+ }
45+
46+ s.refresh();
47+
48+ let headerDiv = document.getElementById('header');
49+ headerDiv.style = 'opacity:1;';
50+
51+ let search = document.getElementById('header');
52+ let searchInput = document.getElementById('searchInput');
53+
54+ let searchFunc = () => {
55+ let ob = window['urbit-ob'].default;
56+ let point = s.graph.nodes(searchInput.value);
57+ if (!point) {
58+ return;
59+ }
60+
61+ let ratio = 0.05;
62+ if (ob.clan(searchInput.value) === 'star') {
63+ ratio = 0.018;
64+ } else if (ob.clan(searchInput.value) === 'planet') {
65+ ratio = 0.003;
66+ }
67+
68+ s.cameras['0'].goTo({
69+ x: point['read_cam0:x'],
70+ y: point['read_cam0:y'],
71+ ratio: ratio,
72+ angle: 0
73+ });
74+ };
75+
76+ searchButton.addEventListener('click', searchFunc);
77+ searchInput.addEventListener('keyup', (e) => {
78+ if (e.key === 'Enter') {
79+ searchFunc();
80+ }
81+ });
82+});
83+
84+const galaxy = (data, s, offsetX = 0, offsetY = 0) => {
85+ // Then, let's add some data to display:
86+ let ob = window['urbit-ob'].default;
87+
88+ for (let [key, val] of Object.entries(data)) {
89+ s.graph.addNode({
90+ id: ob.patp(key),
91+ label: ob.patp(key),
92+ size: 4,
93+ color: '#999',
94+ x: 0 + offsetX,
95+ y: 0 + offsetY
96+ });
97+
98+ let numOfStars = Object.entries(val).length;
99+ let starIter = 0;
100+ for (let [starKey, starVal] of Object.entries(val)) {
101+ const circle = 2 * Math.PI;
102+ let line = 40000 * 128;
103+ let starX = 0 + Math.cos(circle * starIter / numOfStars) * line;
104+ let starY = 0 + Math.sin(circle * starIter / numOfStars) * line;
105+
106+ s.graph.addNode({
107+ id: ob.patp(starKey),
108+ label: ob.patp(starKey),
109+ size: 1.5,
110+ color: '#444',
111+ x: starX + offsetX,
112+ y: starY + offsetY
113+ }).addEdge({
114+ id: `e-star${starKey}`,
115+ source: ob.patp(key),
116+ target: ob.patp(starKey),
117+ color: '#444',
118+ size: 0.2
119+ });
120+
121+ let starAngle = Math.atan2(starY, starX);
122+ let numOfPlanets = Object.entries(starVal).length;
123+ let planetIter = 0;
124+
125+ for (let [planetKey, planetVal] of Object.entries(starVal)) {
126+ let planetAngle = starAngle + (Math.PI * 2 * (planetIter / numOfPlanets));
127+
128+ let lineLen = 0;
129+ let lineColor = '#000';
130+ let nodeColor = '#000';
131+
132+ if (planetIter % 4 === 0) {
133+ lineLen = 19000;
134+ lineColor = '#000';
135+ nodeColor = '#000';
136+ } else if (planetIter % 4 === 1) {
137+ lineLen = 16000;
138+ lineColor = '#ccc';
139+ nodeColor = '#ccc';
140+ } else if (planetIter % 4 === 2) {
141+ lineLen = 13000;
142+ lineColor = '#555';
143+ nodeColor = '#555';
144+ } else if (planetIter % 4 === 3) {
145+ lineLen = 16000;
146+ lineColor = '#ccc';
147+ nodeColor = '#ccc';
148+ }
149+
150+ if (numOfStars < 5) {
151+ lineLen = lineLen * 144;
152+ } else if (numOfStars < 7) {
153+ lineLen = lineLen * 128;
154+ } else if (numOfStars < 9) {
155+ lineLen = lineLen * 96;
156+ } else if (numOfStars < 11) {
157+ lineLen = lineLen * 80;
158+ } else if (numOfStars < 13) {
159+ lineLen = lineLen * 64;
160+ } else if (numOfStars < 15) {
161+ lineLen = lineLen * 56;
162+ } else if (numOfStars < 17) {
163+ lineLen = lineLen * 48;
164+ } else if (numOfStars < 19) {
165+ lineLen = lineLen * 40;
166+ } else if (numOfStars < 21) {
167+ lineLen = lineLen * 36;
168+ } else if (numOfStars < 23) {
169+ lineLen = lineLen * 32;
170+ } else {
171+ lineLen = lineLen * 24;
172+ }
173+
174+ let planetX = starX + (lineLen * Math.cos(planetAngle));
175+ let planetY = starY + (lineLen * Math.sin(planetAngle));
176+
177+ s.graph.addNode({
178+ id: ob.patp(planetVal),
179+ label: ob.patp(planetVal),
180+ size: 0.2,
181+ color: nodeColor,
182+ x: planetX + offsetX,
183+ y: planetY + offsetY
184+ }).addEdge({
185+ id: `e-planet${planetVal}`,
186+ source: ob.patp(starKey),
187+ target: ob.patp(planetVal),
188+ size: 0.05,
189+ color: lineColor
190+ });
191+
192+ planetIter++;
193+ }
194+
195+ starIter++;
196+ }
197+ }
198+};
ui/src/actions/sigma.tsnew file+73
@@ -0,0 +1,73 @@
1+import tippy from 'tippy.js';
2+// import DocLink from '../components/docs/DocLink.svelte'; // TODO try to add a link in the tooltip somehow or something, like:
3+// const app = new DocLink({
4+// target: document.getElementBySelector('.tippy')
5+// })
6+
7+const defaultParams = {
8+ theme: 'astrolabe',
9+ delay: [500, 0],
10+};
11+
12+function getParams(paramsOrContent:any = {}, overrides = {}) {
13+ let params = paramsOrContent;
14+ if (typeof paramsOrContent === 'string') {
15+ params = { content: paramsOrContent };
16+ }
17+ return {
18+ ...defaultParams,
19+ ...params,
20+ ...overrides,
21+ };
22+}
23+
24+export default function tooltip(node, paramsOrContent: any = {}) {
25+ const onTrigger = () => {
26+ console.log('triggered');
27+ node.dispatchEvent(new CustomEvent("tooltipTriggered"));
28+ };
29+ const onUntrigger = () => {
30+ console.log('untriggered');
31+ node.dispatchEvent(new CustomEvent("tooltipUntriggered"));
32+ };
33+ const onClickOutside = (_, e) => {
34+ e.stopPropagation();
35+ };
36+ const listeners = {
37+ onTrigger,
38+ onUntrigger,
39+ onClickOutside,
40+ }
41+
42+ const params = getParams(paramsOrContent, listeners);
43+ const { content } = params;
44+
45+ // Let's make sure the "aria-label" attribute
46+ // is set so our element is accessible:
47+ // https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-label_attribute
48+ if (!node.getAttribute("aria-label")) node.setAttribute("aria-label", content);
49+
50+
51+ // Support any of the Tippy props by forwarding all "params":
52+ // https://atomiks.github.io/tippyjs/v6/all-props/
53+ const tip: any = tippy(node, params);
54+ const hideTip = (e) => {
55+ if (tip.state.isShown) {
56+ tip.hide();
57+ // e.stopPropagation();
58+ }
59+ }
60+ node.addEventListener('click', hideTip);
61+
62+
63+ return {
64+ // If the props change, let's update the Tippy instance:
65+ update: (newParams) => tip.setProps(getParams(newParams)),
66+
67+ // Clean up the Tippy instance on unmount:
68+ destroy: () => {
69+ node.removeEventListener('click', hideTip);
70+ tip.destroy();
71+ }
72+ };
73+};
ui/src/components/HomeScreenTiles.svelte+9
@@ -1,5 +1,6 @@
1<script lang="ts">
1<script lang="ts">
2 import { createEventDispatcher } from 'svelte';
2 import { createEventDispatcher } from 'svelte';
3+ import { link } from 'svelte-spa-router'
3 import DocLink from './docs/DocLink.svelte';
4 import DocLink from './docs/DocLink.svelte';
4
5
5 const dispatch = createEventDispatcher();
6 const dispatch = createEventDispatcher();
@@ -30,4 +31,12 @@
30 </div>
31 </div>
31 </DocLink>
32 </DocLink>
32 </div>
33 </div>
34+ <a class="rounded-lg bg-navy border border-gold flex-grow max-w-s" use:link href="/chart">
35+ <p class="p-5 text-xl text-center text-white">
36+ <br/>
37+ Star Chart
38+ <br/>
39+ <br/>
40+ </p>
41+ </a>
33</div>
42</div>
ui/src/components/SearchBar.svelte+1−1
@@ -36,7 +36,7 @@
36 }
36 }
37</script>
37</script>
38
38
39−<div class="relative grow max-w-md rounded-full w-full">
39+<div class="relative max-w-md rounded-full w-full">
40 <input type="text" class="p-4 py-2 w-full rounded-full"
40 <input type="text" class="p-4 py-2 w-full rounded-full"
41 spellcheck="false"
41 spellcheck="false"
42 placeholder={placeholderText}
42 placeholder={placeholderText}
ui/src/components/StarChart.sveltenew file+134
@@ -0,0 +1,134 @@
1+<script lang="ts">
2+ import { push } from 'svelte-spa-router'
3+
4+ import Sigma from 'sigma';
5+ import Graph from 'graphology';
6+
7+ import { clan } from 'urbit-ob';
8+ import { cite, normalizeId } from '../lib/id';
9+ import { populateGraph } from '../lib/graph';
10+ import { getChartData } from '../lib/api';
11+ import { linkToShip } from '../lib/link';
12+
13+ export let patp: string;
14+ let s;
15+ let g = new Graph();
16+
17+ function sigma(container: HTMLElement, params: any = {}) {
18+ try {
19+ window['s'] = s = new Sigma(g, container, {
20+ renderLabels: false,
21+ // labelColor: { color: WHITE },
22+ labelFont: 'monospace',
23+ labelRenderedSizeThreshold: 9,
24+ zIndex: true,
25+ maxCameraRatio: 10,
26+ });
27+
28+ s.on('downNode', ({ node }) => {
29+ push(linkToShip(node));
30+ });
31+ } catch(e) {
32+ console.error('error in initializing sigma:', e);
33+ if (!params.retried) {
34+ setTimeout(() => sigma(container, { retried: true }), 50);
35+ }
36+ }
37+
38+ return {
39+ destroy() {
40+ s?.kill();
41+ }
42+ }
43+ }
44+
45+ let chartDataPromise: any;
46+ let rawChartData: any = null;
47+ $: {
48+ rawChartData = null;
49+ chartDataPromise = getChartData();
50+ chartDataPromise.then((info) => {
51+ rawChartData = info;
52+ }).catch((error) => {
53+ rawChartData = { error };
54+ });
55+ }
56+ $: rawChartData, populateGraph(g, rawChartData), s?.scheduleRefresh(), searchFunc();
57+
58+ let highlightedPatp;
59+ function searchFunc() {
60+ if (!s) return;
61+ if (highlightedPatp) {
62+ s.graph.removeNodeAttribute(highlightedPatp, 'highlighted');
63+ highlightedPatp = null;
64+ }
65+ if (!patp) return;
66+ let point = s.getNodeDisplayData(patp);
67+ if (!point) return;
68+ s.graph.setNodeAttribute(patp, 'highlighted', true);
69+ highlightedPatp = patp;
70+
71+ let ratio = 0.05;
72+ if (clan(patp) === 'star') {
73+ ratio = 0.035;
74+ } else if (clan(patp) === 'planet') {
75+ ratio = 0.0025;
76+ }
77+
78+ s.camera.animate({
79+ x: point.x,
80+ y: point.y,
81+ ratio: ratio,
82+ // ratio: 1,
83+ angle: 0
84+ }, {
85+ duration: 500,
86+ });
87+ s.scheduleRefresh();
88+ };
89+
90+ $: s, patp, searchFunc();
91+
92+ let helpBoxExpanded = false;
93+ let helpBoxClasses = '';
94+ $: {
95+ helpBoxClasses = helpBoxExpanded ?
96+ "" :
97+ "absolute border-r";
98+ // refreshGraph();
99+ if (s) {
100+ s.resize();
101+ s.scheduleRefresh();
102+ }
103+ }
104+</script>
105+
106+<div class="border border-gold bg-navy w-full grow flex flex-col">
107+ <div class="flex flex-wrap {helpBoxClasses} border-b border-gold bg-navy text-white z-10 cursor-pointer justify-center px-2 py-1 gap-4"
108+ on:click={() => helpBoxExpanded = !helpBoxExpanded }>
109+ {#if helpBoxExpanded}
110+ <div class="flex flex-wrap items-center gap-2">
111+ <div class="w-3 h-3 rounded-full bg-white"></div>
112+ Galaxy
113+ </div>
114+ <div class="flex flex-wrap items-center gap-2">
115+ <div class="w-3 h-3 rounded-full bg-gold"></div>
116+ Star
117+ </div>
118+ <div class="flex flex-wrap items-center gap-2">
119+ <div class="w-3 h-3 rounded-full bg-l1-blue"></div>
120+ L1 Planet
121+ </div>
122+ <div class="flex flex-wrap items-center gap-2">
123+ <div class="w-3 h-3 rounded-full bg-l2-green"></div>
124+ L2 Planet
125+ </div>
126+ {:else}
127+ Help ({patp})
128+ {/if}
129+ </div>
130+ <div class="grow flex">
131+ <div class="w-full" use:sigma>
132+ </div>
133+ </div>
134+</div>
ui/src/lib/api.ts+4
@@ -31,6 +31,10 @@ export async function searchPoints(search: string): Promise<Patp[]> {
31 return points.map(normalizeId);
31 return points.map(normalizeId);
32}
32}
33
33
34+export function getChartData(): Promise<any> {
35+ return scry<any>(`/chart-data`);
36+}
37+
34export function getDoc(path: string): Promise<string> {
38export function getDoc(path: string): Promise<string> {
35 return scry<string>(`/doc/${path}`);
39 return scry<string>(`/doc/${path}`);
36}
40}
ui/src/lib/colors.tsnew file+7
@@ -0,0 +1,7 @@
1+export const GOLD = '#fcd34d';
2+export const GOLDT2 = '#fddc71';
3+export const WHITE = '#FFF';
4+export const LGREY = '#BBB';
5+export const DGREY = '#777';
6+export const BLUE = '#1792c7';
7+export const GREEN = '#17c76a';
ui/src/lib/graph.tsnew file+213
@@ -0,0 +1,213 @@
1+import Graph from 'graphology';
2+import { clan, sein, patp2dec, patp } from 'urbit-ob';
3+import * as colors from './colors';
4+
5+const circle = 2 * Math.PI;
6+export function arrangePointsInSpirals(
7+ numPoints,
8+ {
9+ maxDistance, minDistance,
10+ startX = 0, startY = 0,
11+ maxArms = 6, maxPerArm = 45,
12+ makeSingleArmCircle = false,
13+ }): { coords: any[], numArms: number } {
14+ const numSmallEnoughArms = Math.ceil(numPoints / maxPerArm);
15+ let eRootNumArms = Math.max(1, Math.floor(Math.pow(numPoints, 1 / Math.E)));
16+ const numArms = Math.max(numSmallEnoughArms, eRootNumArms);
17+ const singleArmCircle = makeSingleArmCircle && numArms === 1;
18+ const numPerArm = numPoints / numArms;
19+ // todo refine theta range, and rename maxPerArm (misnomer)
20+ let armThetaRange = numPerArm * Math.min(Math.PI / 12, 0.25 * circle / numArms);
21+ if (singleArmCircle) {
22+ minDistance = maxDistance;
23+ armThetaRange = 2 * Math.PI;
24+ }
25+ const coords = [];
26+ let lastArm = null;
27+ let indexInArm = null;
28+ for (let i = 0; i < numPoints; i++) {
29+ const overallProgress = i / numPoints;
30+ const arm = Math.floor(numArms * overallProgress);
31+ if (lastArm === arm) {
32+ indexInArm++;
33+ } else {
34+ indexInArm = 0;
35+ }
36+ const interArmProgress = arm / numArms;
37+ // (i - (numarms * i/numpoints) * (numpoints/numarms)) / numperarm
38+ // const intraArmProgress = (i - arm * numPerArm) / numPerArm;
39+ const beginningOfNextArm = Math.floor(numPerArm * (arm + 1));
40+ const numInThisArm = beginningOfNextArm - (i - indexInArm);
41+ const intraArmProgress = indexInArm / numInThisArm;
42+ // const intraArmProgress = (numArms * overallProgress) % 1;
43+ const theta = circle * interArmProgress + armThetaRange * intraArmProgress;
44+ let baseLine = maxDistance - minDistance;
45+ let line = (minDistance + baseLine * intraArmProgress);
46+ let x = 0 + startX + line * Math.cos(theta);
47+ let y = 0 + startY + line * Math.sin(theta);
48+ coords.push({
49+ coords: [x, y],
50+ arm: singleArmCircle ? i : arm,
51+ });
52+ lastArm = arm;
53+ };
54+ return {
55+ coords,
56+ numArms,
57+ };
58+}
59+
60+export
61+
62+function populateGraph(g: Graph, rawChartData: any[]) {
63+ if (!(rawChartData instanceof Array)) return;
64+ if (!g) {
65+ g = new Graph();
66+ }
67+
68+ let sponsorEdges = [];
69+
70+ let offsetX = 0;
71+ let offsetY = 0;
72+
73+ let a = 120000000;
74+ let b = 9000000;
75+ let theta = 0;
76+
77+ rawChartData.forEach((galaxy, gi) => {
78+ offsetX = 0 + (a + b * theta) * Math.cos(theta);
79+ offsetY = 0 + (a + b * theta) * Math.sin(theta);
80+
81+ theta = 0.15 * (gi+1) * (1 - .15 * (gi / 256));
82+ const gPatp = '~' + galaxy.p;
83+ g.addNode(gPatp, {
84+ // id: gPatp,
85+ label: gPatp,
86+ size: 3,
87+ color: colors.WHITE,
88+ // color: colors.'#fcd34d',
89+ x: offsetX,
90+ y: offsetY,
91+ zIndex: 10,
92+ });
93+
94+ const stars = galaxy.c || [];
95+ const numStars = stars.length;
96+ const maxStarDistance = 60000 * 128;
97+ const starSize = 1.2 / Math.pow(numStars, 1 / 7);
98+ const {
99+ coords: starCoords,
100+ numArms: numStarArms,
101+ } = arrangePointsInSpirals(numStars,
102+ {
103+ maxDistance: maxStarDistance,
104+ minDistance: maxStarDistance / 4,
105+ startX: offsetX,
106+ startY: offsetY,
107+ makeSingleArmCircle: true,
108+ });
109+ let lastStarArm = null;
110+ let lastStarPatp = null;
111+ stars.forEach((star, si) => {
112+ const sPatp = '~' + star.p;
113+
114+ const [starX, starY] = starCoords[si].coords;
115+ const arm = starCoords[si].arm;
116+ g.addNode(sPatp, {
117+ label: sPatp,
118+ size: starSize,
119+ // color: colors.arm % 2 == 0 ? LGREY : DGREY,
120+ color: colors.GOLDT2,
121+ x: starX,
122+ y: starY,
123+ zIndex: 8,
124+ });
125+ if (lastStarArm == arm) {
126+ g.addEdge(sPatp, lastStarPatp, {
127+ color: colors.LGREY,
128+ size: 0.1,
129+ zIndex: 7,
130+ });
131+ } else {
132+ g.addEdge(sPatp, gPatp, {
133+ color: colors.LGREY,
134+ size: 0.2,
135+ zIndex: 9,
136+ });
137+ }
138+ lastStarArm = arm;
139+ lastStarPatp = sPatp;
140+ if (star.s) {
141+ sponsorEdges.push([sPatp, '~' + star.s, {
142+ color: colors.GREEN,
143+ size: 0.2,
144+ zIndex: 11,
145+ }]);
146+ }
147+
148+ const planets = star.c || [];
149+ const numPlanets = planets.length;
150+ const maxPlanetDistance = starSize * 1.6 * 4000 * 64;
151+ const {
152+ coords: planetCoords,
153+ numArms: numPlanetArms,
154+ } = arrangePointsInSpirals(numPlanets,
155+ {
156+ maxDistance: maxPlanetDistance,
157+ minDistance: maxPlanetDistance / 4,
158+ startX: starX,
159+ startY: starY,
160+ maxArms: 30,
161+ makeSingleArmCircle: true,
162+ });
163+ let lastPlanetArm = null;
164+ let lastPlanetPatp = null;
165+ planets.forEach((planet, pi) => {
166+ const pPatp = '~' + planet.p;
167+ // const circle = 2 * Math.PI;
168+ // let line = 16000 * 64;
169+ // let planetX = 0 + starX + Math.cos(circle * pi / numPlanets) * line;
170+ // let planetY = 0 + starY + Math.sin(circle * pi / numPlanets) * line;
171+
172+ const [planetX, planetY] = planetCoords[pi].coords;
173+ const arm = planetCoords[pi].arm;
174+ g.addNode(pPatp, {
175+ label: pPatp,
176+ size: .15,
177+ color: planet.d == 'l2' ? colors.GREEN : colors.BLUE,
178+ x: planetX,
179+ y: planetY,
180+ zIndex: 6,
181+ });
182+ if (lastPlanetArm == arm) {
183+ g.addEdge(pPatp, lastPlanetPatp, {
184+ color: colors.DGREY,
185+ size: 0.05,
186+ zIndex: 5,
187+ });
188+ } else {
189+ if (numPlanetArms == 1) {
190+ g.addEdge(pPatp, sPatp, {
191+ color: colors.DGREY,
192+ size: 0.05,
193+ zIndex: 5,
194+ });
195+ }
196+ }
197+ if (planet.s) {
198+ sponsorEdges.push([pPatp, '~' + planet.s, {
199+ color: colors.GREEN,
200+ size: 0.05,
201+ zIndex: 11,
202+ }]);
203+ }
204+ lastPlanetArm = arm;
205+ lastPlanetPatp = pPatp;
206+ });
207+ });
208+ });
209+ sponsorEdges.forEach(([point, sponsor, attrs]) => {
210+ // g.addEdge(point, sponsor, attrs);
211+ });
212+ return g;
213+}
ui/src/routes/Home.svelte+25−20
@@ -7,6 +7,7 @@
7 import SearchBar from "../components/SearchBar.svelte";
7 import SearchBar from "../components/SearchBar.svelte";
8 import SearchResults from "../components/SearchResults.svelte";
8 import SearchResults from "../components/SearchResults.svelte";
9 import ShipView from '../components/ShipView.svelte';
9 import ShipView from '../components/ShipView.svelte';
10+ import StarChart from '../components/StarChart.svelte';
10 import ValidationProblems from '../components/ValidationProblems.svelte';
11 import ValidationProblems from '../components/ValidationProblems.svelte';
11 import HomeScreenTiles from '../components/HomeScreenTiles.svelte';
12 import HomeScreenTiles from '../components/HomeScreenTiles.svelte';
12 export let params: any = {};
13 export let params: any = {};
@@ -70,8 +71,8 @@
70 }
71 }
71 }
72 }
72 // $: urlChangedSinceAnalysisPatp = (urlChanged - analysisPatpChanged) > 1000;
73 // $: urlChangedSinceAnalysisPatp = (urlChanged - analysisPatpChanged) > 1000;
73− function doStuff() {
74+ function autoNav() {
74− if (search !== urlSearch) {
75+ if (params.mode !== 'chart' && search !== urlSearch) {
75 if (urlChangedSinceAnalysisPatp) {
76 if (urlChangedSinceAnalysisPatp) {
76 search = urlSearch;
77 search = urlSearch;
77 } else if (analysis.search === search) {
78 } else if (analysis.search === search) {
@@ -84,18 +85,20 @@
84 }
85 }
85 }
86 }
86 }
87 }
87− $: urlSearch, analysis, doStuff();
88+ $: urlSearch, analysis, autoNav();
88 $: {
89 $: {
89− if (analysis.search === search) {
90− let properMode = null;
90+ let properMode = params.mode;
91+ if (properMode === 'chart') {
92+ // nice
93+ } else if (analysis.search === search) {
91 if (analysis.patpIsValid) {
94 if (analysis.patpIsValid) {
92 properMode = 'ship';
95 properMode = 'ship';
93 } else if (analysis.search.length > 0) {
96 } else if (analysis.search.length > 0) {
94 properMode = 'search';
97 properMode = 'search';
95 }
98 }
99+ }
96− if (params.mode !== properMode) {
100+ if (params.mode !== properMode) {
97− navToMode(properMode, false);
101+ navToMode(properMode, false);
98− }
99 }
102 }
100 }
103 }
101
104
@@ -108,6 +111,8 @@
108 case 'search':
111 case 'search':
109 $pageName = 'Search';
112 $pageName = 'Search';
110 break;
113 break;
114+ case 'chart':
115+ $pageName = 'Star Chart';
111 default:
116 default:
112 $pageName = null;
117 $pageName = null;
113 }
118 }
@@ -117,19 +122,19 @@
117
122
118</script>
123</script>
119
124
120−<div class="m-4 flex flex-col items-center space-y-8 2xs:m-8">
125+<div class="p-4 min-h-full flex flex-col items-center space-y-8 2xs:p-8">
121 <SearchBar bind:analysis bind:search />
126 <SearchBar bind:analysis bind:search />
122 <!-- <p class="bg-white">navigating</p> -->
127 <!-- <p class="bg-white">navigating</p> -->
123− {#if analysis.patpIsValid}
128+ {#if params.mode == "chart"}
124− <!-- {#if analysis.search === urlSearch} -->
129+ <StarChart patp={analysis.patpIsValid ? analysis.patp : null} />
130+ {:else if analysis.patpIsValid}
125− <ShipView patp={analysis.patp} />
131+ <ShipView patp={analysis.patp} />
126− <!-- {/if} -->
127− {:else if (analysis.queryIsValid)}
132+ {:else if (analysis.queryIsValid)}
128− <SearchResults query={analysis.query} search={analysis.search} />
133+ <SearchResults query={analysis.query} search={analysis.search} />
129− {:else if (analysis.search.length > 0)}
134+ {:else if (analysis.search.length > 0)}
130− <ValidationProblems problems={analysis.queryProblems} />
135+ <ValidationProblems problems={analysis.queryProblems} />
131− {:else}
136+ {:else}
132− <HomeScreenTiles />
137+ <HomeScreenTiles />
133− {/if}
138+ {/if}
134 <!-- {/if} -->
139 <!-- {/if} -->
135</div>
140</div>
ui/src/styles/tailwind-output.css+95−11
@@ -424,6 +424,51 @@ Ensure the default browser behavior of the `hidden` attribute.
424 --tw-backdrop-saturate: ;
424 --tw-backdrop-saturate: ;
425 --tw-backdrop-sepia: ;
425 --tw-backdrop-sepia: ;
426}
426}
427+.container {
428+ width: 100%;
429+}
430+@media (min-width: 360px) {
431+
432+ .container {
433+ max-width: 360px;
434+ }
435+}
436+@media (min-width: 475px) {
437+
438+ .container {
439+ max-width: 475px;
440+ }
441+}
442+@media (min-width: 640px) {
443+
444+ .container {
445+ max-width: 640px;
446+ }
447+}
448+@media (min-width: 768px) {
449+
450+ .container {
451+ max-width: 768px;
452+ }
453+}
454+@media (min-width: 1024px) {
455+
456+ .container {
457+ max-width: 1024px;
458+ }
459+}
460+@media (min-width: 1280px) {
461+
462+ .container {
463+ max-width: 1280px;
464+ }
465+}
466+@media (min-width: 1536px) {
467+
468+ .container {
469+ max-width: 1536px;
470+ }
471+}
427.pointer-events-none {
472.pointer-events-none {
428 pointer-events: none;
473 pointer-events: none;
429}
474}
@@ -592,6 +637,9 @@ Ensure the default browser behavior of the `hidden` attribute.
592.h-12 {
637.h-12 {
593 height: 3rem;
638 height: 3rem;
594}
639}
640+.h-3 {
641+ height: 0.75rem;
642+}
595.h-\[100vh\] {
643.h-\[100vh\] {
596 height: 100vh;
644 height: 100vh;
597}
645}
@@ -607,6 +655,15 @@ Ensure the default browser behavior of the `hidden` attribute.
607.h-4 {
655.h-4 {
608 height: 1rem;
656 height: 1rem;
609}
657}
658+.h-24 {
659+ height: 6rem;
660+}
661+.min-h-full {
662+ min-height: 100%;
663+}
664+.min-h-0 {
665+ min-height: 0px;
666+}
610.w-full {
667.w-full {
611 width: 100%;
668 width: 100%;
612}
669}
@@ -616,6 +673,9 @@ Ensure the default browser behavior of the `hidden` attribute.
616.w-12 {
673.w-12 {
617 width: 3rem;
674 width: 3rem;
618}
675}
676+.w-3 {
677+ width: 0.75rem;
678+}
619.w-\[100vw\] {
679.w-\[100vw\] {
620 width: 100vw;
680 width: 100vw;
621}
681}
@@ -628,6 +688,9 @@ Ensure the default browser behavior of the `hidden` attribute.
628.w-4 {
688.w-4 {
629 width: 1rem;
689 width: 1rem;
630}
690}
691+.w-6 {
692+ width: 1.5rem;
693+}
631.min-w-0 {
694.min-w-0 {
632 min-width: 0px;
695 min-width: 0px;
633}
696}
@@ -711,6 +774,12 @@ Ensure the default browser behavior of the `hidden` attribute.
711.gap-1 {
774.gap-1 {
712 gap: 0.25rem;
775 gap: 0.25rem;
713}
776}
777+.gap-4 {
778+ gap: 1rem;
779+}
780+.gap-2 {
781+ gap: 0.5rem;
782+}
714.gap-x-2 {
783.gap-x-2 {
715 -moz-column-gap: 0.5rem;
784 -moz-column-gap: 0.5rem;
716 column-gap: 0.5rem;
785 column-gap: 0.5rem;
@@ -818,14 +887,17 @@ Ensure the default browser behavior of the `hidden` attribute.
818.border-l {
887.border-l {
819 border-left-width: 1px;
888 border-left-width: 1px;
820}
889}
821−.border-navy {
890+.border-r {
822− --tw-border-opacity: 1;
891+ border-right-width: 1px;
823− border-color: rgb(31 36 60 / var(--tw-border-opacity));
824}
892}
825.border-gold {
893.border-gold {
826 --tw-border-opacity: 1;
894 --tw-border-opacity: 1;
827 border-color: rgb(252 211 77 / var(--tw-border-opacity));
895 border-color: rgb(252 211 77 / var(--tw-border-opacity));
828}
896}
897+.border-navy {
898+ --tw-border-opacity: 1;
899+ border-color: rgb(31 36 60 / var(--tw-border-opacity));
900+}
829.border-gray-300 {
901.border-gray-300 {
830 --tw-border-opacity: 1;
902 --tw-border-opacity: 1;
831 border-color: rgb(209 213 219 / var(--tw-border-opacity));
903 border-color: rgb(209 213 219 / var(--tw-border-opacity));
@@ -858,6 +930,14 @@ Ensure the default browser behavior of the `hidden` attribute.
858 --tw-bg-opacity: 1;
930 --tw-bg-opacity: 1;
859 background-color: rgb(0 0 0 / var(--tw-bg-opacity));
931 background-color: rgb(0 0 0 / var(--tw-bg-opacity));
860}
932}
933+.bg-l1-blue {
934+ --tw-bg-opacity: 1;
935+ background-color: rgb(23 146 199 / var(--tw-bg-opacity));
936+}
937+.bg-l2-green {
938+ --tw-bg-opacity: 1;
939+ background-color: rgb(23 199 106 / var(--tw-bg-opacity));
940+}
861.bg-orange-gold {
941.bg-orange-gold {
862 --tw-bg-opacity: 1;
942 --tw-bg-opacity: 1;
863 background-color: rgb(247 211 146 / var(--tw-bg-opacity));
943 background-color: rgb(247 211 146 / var(--tw-bg-opacity));
@@ -991,6 +1071,10 @@ Ensure the default browser behavior of the `hidden` attribute.
991 padding-left: 2rem;
1071 padding-left: 2rem;
992 padding-right: 2rem;
1072 padding-right: 2rem;
993}
1073}
1074+.py-1 {
1075+ padding-top: 0.25rem;
1076+ padding-bottom: 0.25rem;
1077+}
994.px-0\.5 {
1078.px-0\.5 {
995 padding-left: 0.125rem;
1079 padding-left: 0.125rem;
996 padding-right: 0.125rem;
1080 padding-right: 0.125rem;
@@ -1003,10 +1087,6 @@ Ensure the default browser behavior of the `hidden` attribute.
1003 padding-left: 0.75rem;
1087 padding-left: 0.75rem;
1004 padding-right: 0.75rem;
1088 padding-right: 0.75rem;
1005}
1089}
1006−.py-1 {
1007− padding-top: 0.25rem;
1008− padding-bottom: 0.25rem;
1009−}
1010.px-1 {
1090.px-1 {
1011 padding-left: 0.25rem;
1091 padding-left: 0.25rem;
1012 padding-right: 0.25rem;
1092 padding-right: 0.25rem;
@@ -1095,6 +1175,10 @@ Ensure the default browser behavior of the `hidden` attribute.
1095.leading-3 {
1175.leading-3 {
1096 line-height: .75rem;
1176 line-height: .75rem;
1097}
1177}
1178+.text-white {
1179+ --tw-text-opacity: 1;
1180+ color: rgb(255 255 255 / var(--tw-text-opacity));
1181+}
1098.text-gray-700 {
1182.text-gray-700 {
1099 --tw-text-opacity: 1;
1183 --tw-text-opacity: 1;
1100 color: rgb(55 65 81 / var(--tw-text-opacity));
1184 color: rgb(55 65 81 / var(--tw-text-opacity));
@@ -1123,10 +1207,6 @@ Ensure the default browser behavior of the `hidden` attribute.
1123 --tw-text-opacity: 1;
1207 --tw-text-opacity: 1;
1124 color: rgb(21 128 61 / var(--tw-text-opacity));
1208 color: rgb(21 128 61 / var(--tw-text-opacity));
1125}
1209}
1126−.text-white {
1127− --tw-text-opacity: 1;
1128− color: rgb(255 255 255 / var(--tw-text-opacity));
1129−}
1130.text-gray-400 {
1210.text-gray-400 {
1131 --tw-text-opacity: 1;
1211 --tw-text-opacity: 1;
1132 color: rgb(156 163 175 / var(--tw-text-opacity));
1212 color: rgb(156 163 175 / var(--tw-text-opacity));
@@ -1181,6 +1261,10 @@ Ensure the default browser behavior of the `hidden` attribute.
1181 margin-right: 2rem;
1261 margin-right: 2rem;
1182 }
1262 }
1183
1263
1264+ .\32xs\:p-8 {
1265+ padding: 2rem;
1266+ }
1267+
1184 .\32xs\:py-8 {
1268 .\32xs\:py-8 {
1185 padding-top: 2rem;
1269 padding-top: 2rem;
1186 padding-bottom: 2rem;
1270 padding-bottom: 2rem;
ui/tailwind.config.cjs+3−1
@@ -11,6 +11,9 @@ module.exports = {
11 colors: {
11 colors: {
12 navy: '#1f243c',
12 navy: '#1f243c',
13 gold: '#fcd34d',
13 gold: '#fcd34d',
14+ 'orange-gold': '#f7d392',
15+ 'l1-blue': '#1792c7',
16+ 'l2-green': '#17c76a',
14 'gold-s1': '#e3be45',
17 'gold-s1': '#e3be45',
15 'gold-s2': '#caa93e',
18 'gold-s2': '#caa93e',
16 'gold-s3': '#b09436',
19 'gold-s3': '#b09436',
@@ -29,7 +32,6 @@ module.exports = {
29 // 'gold-t7': '4c3f17',
32 // 'gold-t7': '4c3f17',
30 'gold-t8': '#fef6db',
33 'gold-t8': '#fef6db',
31 // 'gold-t9': '191508',
34 // 'gold-t9': '191508',
32− 'orange-gold': '#f7d392',
33 }
35 }
34 },
36 },
35 screens: {
37 screens: {