on master help sign in
Merge branch 'mp/ecosystem' of https://github.com/urbit/urbit.org into mp/ecosystem
651f221Gordon
components/Contact.js+8−22
@@ -2,37 +2,23 @@ import NewsletterSignup from "./NewletterSignup";
2import { name, contact } from "../lib/constants";
2import { name, contact } from "../lib/constants";
3import Link from "next/link";
3import Link from "next/link";
4
4
5−export default function Contact({ emphasize }) {
5+export default function Contact({
6+ emphasize,
7+ ecosystem = false,
8+ className = "",
9+}) {
6 const linkText = emphasize ? "text-green-400" : "text-wall-600";
10 const linkText = emphasize ? "text-green-400" : "text-wall-600";
7 const border = emphasize ? "border-green-400" : "border-wall-600";
11 const border = emphasize ? "border-green-400" : "border-wall-600";
8
12
9 return (
13 return (
10− <div>
14+ <div className={className}>
11 <div className="measure">
15 <div className="measure">
12 <h4 className="pb-6">
16 <h4 className="pb-6">
13− If you’d like to follow our progress, we send monthly updates via
17+ Sign up for the Urbit Newsletter for a monthly Spotlight summary, and
14− email:
18+ more Urbit news.
15 </h4>
19 </h4>
16 </div>
20 </div>
17 <NewsletterSignup color={border} />
21 <NewsletterSignup color={border} />
18− <h4 className="mt-12 text-wall-500">
19− Follow us on{" "}
20− <a className={linkText} href={contact.twitter}>
21− Twitter
22− </a>
23− </h4>
24− <h4 className="mt-6 text-wall-500">
25− Explore code on{" "}
26− <a className={linkText} href={contact.github}>
27− Github
28− </a>
29− </h4>
30− <h4 className="mt-6 text-wall-500 flex-wrap">
31− Boot Urbit and join{" "}
32− <Link href={`/groups/${contact.urbitCommunity}`} passHref>
33− <a className={linkText}>Urbit Community</a>
34− </Link>
35− </h4>
36 </div>
22 </div>
37 );
23 );
38}
24}
components/Header.js+11−8
@@ -62,21 +62,20 @@ export default function Header(props) {
62 >
62 >
63 Overview
63 Overview
64 </ActiveLink>
64 </ActiveLink>
65−
66 <ActiveLink
65 <ActiveLink
67 currentPath={currentPath}
66 currentPath={currentPath}
68 className="mr-5 type-ui"
67 className="mr-5 type-ui"
69− href="/using"
68+ href="/ecosystem"
70 >
69 >
71− Manual
70+ Ecosystem
72 </ActiveLink>
71 </ActiveLink>
73
72
74 <ActiveLink
73 <ActiveLink
75 currentPath={currentPath}
74 currentPath={currentPath}
76 className="mr-5 type-ui"
75 className="mr-5 type-ui"
77− href="/faq"
76+ href="/using"
78 >
77 >
79− FAQ
78+ Manual
80 </ActiveLink>
79 </ActiveLink>
81
80
82 <ActiveLink
81 <ActiveLink
@@ -120,6 +119,13 @@ export default function Header(props) {
120 >
119 >
121 Overview
120 Overview
122 </ActiveLink>
121 </ActiveLink>
122+ <ActiveLink
123+ currentPath={currentPath}
124+ className="mr-5 mb-4"
125+ href="/ecosystem"
126+ >
127+ Ecosystem
128+ </ActiveLink>
123 <ActiveLink
129 <ActiveLink
124 currentPath={currentPath}
130 currentPath={currentPath}
125 className="mr-5 mb-4"
131 className="mr-5 mb-4"
@@ -148,9 +154,6 @@ export default function Header(props) {
148 >
154 >
149 Get Started
155 Get Started
150 </ActiveLink>
156 </ActiveLink>
151− <ActiveLink currentPath={currentPath} className="mr-5 mb-4" href="/faq">
152− FAQ
153− </ActiveLink>
154 </MenuTray>
157 </MenuTray>
155 </header>
158 </header>
156 );
159 );
components/ecosystem/BasicPage.jsnew file+109
@@ -0,0 +1,109 @@
1+import { useRouter } from "next/router";
2+import Link from "next/link";
3+import Head from "next/head";
4+import Meta from "../Meta";
5+import ErrorPage from "../../pages/404";
6+import classnames from "classnames";
7+import {
8+ Container,
9+ SingleColumn,
10+ Section,
11+ Markdown,
12+ IntraNav,
13+} from "foundation-design-system";
14+import Header from "../Header";
15+import Footer from "../Footer";
16+
17+export default function BasicPage({
18+ section,
19+ post,
20+ group,
21+ markdown,
22+ search,
23+ children,
24+}) {
25+ const router = useRouter();
26+ if (!router.isFallback && !post?.slug) {
27+ return <ErrorPage />;
28+ }
29+ return (
30+ <Container>
31+ <Head>
32+ <title>
33+ {post.title} • {section} • Urbit
34+ </title>
35+ {Meta(post)}
36+ </Head>
37+ <IntraNav ourSite="https://urbit.org" search={search} />
38+ <SingleColumn>
39+ <Header />
40+ <Section narrow className="space-y-12">
41+ <div className="flex items-center space-x-4">
42+ <img src={post.image} className="w-36" />
43+ <div className="flex flex-col">
44+ <h1
45+ className={classnames({ "text-3xl": section === "Podcasts" })}
46+ >
47+ {post.title}
48+ </h1>
49+ <p>{section.slice(0, -1)}</p>
50+ </div>
51+ </div>
52+ {/* General purpose metadata bar -- podcast listen button is special-cased URL, flexed at the end of the row */}
53+ <div className="flex justify-between">
54+ <div className="flex space-x-12">
55+ {post?.podcast && (
56+ <div className="flex flex-col">
57+ <p className="font-bold text-wall-400">Podcast</p>
58+ <p>{post.podcast}</p>
59+ </div>
60+ )}
61+ {post?.date && (
62+ <div className="flex flex-col">
63+ <p className="font-bold text-wall-400">Date</p>
64+ <p className="shrink-0">{post.date}</p>
65+ </div>
66+ )}
67+ {post?.URL && section !== "Podcasts" && (
68+ <div className="flex flex-col">
69+ <p className="font-bold text-wall-400">Website</p>
70+ <a
71+ className="text-green-400 text-sm font-semibold font-mono"
72+ href={post.URL}
73+ >
74+ {post.URL.slice(post.URL.indexOf("://") + 3)}
75+ </a>
76+ </div>
77+ )}
78+ {(group || post?.group) && (
79+ <div className="flex flex-col">
80+ <p className="font-bold text-wall-400">Group</p>
81+ <Link href={`/groups/${post.group}`} passHref>
82+ <a className="text-green-400 text-sm font-semibold font-mono">
83+ {group?.title || post.group}
84+ </a>
85+ </Link>
86+ </div>
87+ )}
88+ </div>
89+ {post?.URL && section === "Podcasts" && (
90+ <div className="button-lg bg-green-400 text-white cursor-pointer flex space-x-2">
91+ <img src="/images/sound.svg" />
92+ <a target="_blank" href={post.URL}>
93+ Listen
94+ </a>
95+ </div>
96+ )}
97+ </div>
98+ <div className="flex">
99+ <div className="markdown">
100+ <Markdown.render content={JSON.parse(markdown)} />
101+ {children}
102+ </div>
103+ </div>
104+ </Section>
105+ </SingleColumn>
106+ <Footer />
107+ </Container>
108+ );
109+}
components/ecosystem/IndexCard.jsnew file+47
@@ -0,0 +1,47 @@
1+import Link from "next/link";
2+import classnames from "classnames";
3+export default function IndexCard({ slug, feat }) {
4+ return (
5+ <Link href={slug}>
6+ <div className="cursor-pointer bg-wall-100 rounded-xl basis-1/3 h-full">
7+ <div className="flex flex-col p-6 justify-between items-between h-full relative">
8+ {feat.image ? (
9+ <img
10+ className="rounded-xl w-full flex-1 object-cover"
11+ src={feat.image}
12+ style={{ aspectRatio: "4 / 3" }}
13+ />
14+ ) : (
15+ <div
16+ className="bg-wall-200 rounded-xl w-full flex-1"
17+ style={{ aspectRatio: "4 / 3" }}
18+ />
19+ )}
20+ <div className="grow-1 shrink-0 flex flex-col h-full min-h-0 pt-4">
21+ <h3 className="mb-2">{feat.title}</h3>
22+ {badge(feat.type)}
23+ <p>{feat?.description}</p>
24+ </div>
25+ </div>
26+ </div>
27+ </Link>
28+ );
29+}
30+
31+const badge = (type) => {
32+ return (
33+ <div
34+ className={classnames(
35+ "uppercase font-bold text-white text-xs w-fit rounded-lg mb-2 px-2 py-1",
36+ {
37+ "bg-[#B37ED7]": type === "Organization",
38+ "bg-[#83b2d4]": type === "Application",
39+ "bg-[#8dd5b1]": type === "Podcast",
40+ "bg-[#8dd5b1]": type === "Marketplace",
41+ }
42+ )}
43+ >
44+ {type}
45+ </div>
46+ );
47+};
components/ecosystem/Post.jsnew file+52
@@ -0,0 +1,52 @@
1+import { Markdown } from "foundation-design-system";
2+import classnames from "classnames";
3+import Link from "next/link";
4+export default function Post({ post }) {
5+ return ["featured-1", "featured-2", "featured-3"].map((feat) => {
6+ return post?.[feat] ? (
7+ <div className="mt-8 mb-24">
8+ <div className="flex space-x-4 items-center">
9+ <img
10+ className="rounded-xl w-24 object-cover"
11+ src={post[feat].image}
12+ />
13+ <div className="flex flex-col">
14+ <h4 className="text-2xl font-bold">{post?.[feat].title}</h4>
15+ {badge(post[feat].type)}
16+ {post[feat].type !== "Application" && (
17+ <a className="font-bold text-green-400" href={post[feat].URL}>
18+ {post[feat].URL.slice(post[feat].URL.indexOf("://") + 3)}
19+ </a>
20+ )}
21+ {post[feat].type === "Application" && (
22+ <Link href={`/applications/${post[feat].URL}`}>
23+ <a>{post[feat].URL}</a>
24+ </Link>
25+ )}
26+ </div>
27+ </div>
28+ <div className="markdown mt-8">
29+ <Markdown.render content={JSON.parse(post[feat].content)} />
30+ </div>
31+ </div>
32+ ) : null;
33+ });
34+}
35+
36+const badge = (type) => {
37+ return (
38+ <div
39+ className={classnames(
40+ "uppercase font-bold text-white text-xs w-fit rounded-lg mb-2 px-2 py-1",
41+ {
42+ "bg-[#B37ED7]": type === "Organization",
43+ "bg-[#83b2d4]": type === "Application",
44+ "bg-[#8dd5b1]": type === "Podcast",
45+ "bg-[#8dd5b1]": type === "Marketplace",
46+ }
47+ )}
48+ >
49+ {type}
50+ </div>
51+ );
52+};
content/ecosystem/spotlight/june-2022.mdnew file+36
@@ -0,0 +1,36 @@
1++++
2+title = "June 2022"
3+date = "2022-06-01"
4+[featured-1]
5+title = "Urbit.live"
6+description = "One line about the thing"
7+type = "Marketplace"
8+image = "https://s3.us-east-1.amazonaws.com/haddefsigwen1/reciprocal/2022.5.20..19.52.50-B137D4AA-DA9E-4B07-98CE-C001C576C7CA_1_201_a.jpeg"
9+URL = "https://urbit.live"
10+content = """
11+This layer 1 marketplace is all the rage.
12+
13+Let this be a lesson to you."""
14+
15+[featured-2]
16+title = "Reciprocal Ltd."
17+description = "One line about the thing"
18+type = "Organization"
19+image = "https://media.urbit.org/site/ecosystem/podcasts/full_1639502000-artwork.jpg"
20+URL = "https://reciprocal.ltd"
21+content = """
22+A design consultancy and friend network.
23+
24+Let this be a lesson to you."""
25+
26+[featured-3]
27+title = "Reciprocal Ltd."
28+description = "One line about the thing"
29+type = "Organization"
30+image = "https://media.urbit.org/site/ecosystem/podcasts/full_1639502000-artwork.jpg"
31+URL = "https://reciprocal.ltd"
32+content = """
33+A design consultancy and friend network.
34+
35+Let this be a lesson to you."""
36++++
content/marketplaces/urbit-live.mdnew file+7
@@ -0,0 +1,7 @@
1++++
2+title = "Urbit.live"
3+image = "https://urbit.live/static/media/urbit-live-logo-png-400.6ec9a92b.png"
4+URL = "https://urbit.live"
5++++
6+
7+Layer 1 planet market.
content/media/blockchannel-episode-19-virtual-land-in-the-clouds-with-urbit.mddeleted−14
@@ -1,14 +0,0 @@
1−+++
2−title = "Episode 19: Virtual Land in the Clouds, with Urbit"
3−date = "2017-04-15"
4−description = "Galen and Curtis talk Urbit on the BlockChannel podcast."
5−
6−[extra]
7−author = "Galen Wolfe-Pauly + Curtis Yarvin"
8−ship = "~ravmel-ropdyl + ~sorreg-namtyv"
9−soundcloud = "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/318017856&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true"
10−+++
11−
12−{{ soundcloud(url="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/318017856&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true") }}
13−
14−[Episode 19: Virtual Land in the Clouds, with Urbit](https://medium.com/blockchannel/episode-19-virtual-land-in-the-clouds-with-urbit-1428ba98243a)
content/media/blueprint-2017-galen-wolfe-pauly-on-the-distributed-web.mddeleted−15
@@ -1,15 +0,0 @@
1−+++
2−title = "Blueprint 2017 Talks: Galen Wolfe-Pauly on the Distributed Web"
3−date = "2017-11-19"
4−description = "Galen presents to IDEO on Urbit and decentralized systems."
5−aliases = [
6− "/posts/blueprint-2017-galen-wolfe-pauly-on-the-distributed-web/"
7−]
8−
9−[extra]
10−author = "Galen Wolfe-Pauly"
11−ship = "~ravmel-ropdyl"
12−youtube = "9cXTGW9J9a0"
13−+++
14−
15−[Blueprint 2017 Talks: Galen Wolfe-Pauly on the Distributed Web](https://www.youtube.com/watch?v=9cXTGW9J9a0)
content/media/epicenter-episode-205-urbit-a-digital-republic-reinventing-the-internet.mddeleted−15
@@ -1,15 +0,0 @@
1−+++
2−title = "Epicenter #205: Urbit – A Digital Republic Reinventing the Internet"
3−date = "2017-10-17"
4−description = "Galen discusses Urbit with Brian Fabian Crain and Meher Roy on the Epicenter podcast."
5−aliases = [
6− "/posts/epicenter-episode-205-urbit-a-digital-republic-reinventing-the-internet/"
7−]
8−
9−[extra]
10−author = "Galen Wolfe-Pauly"
11−ship = "~ravmel-ropdyl"
12−youtube = "DFeN-TKZor0"
13−+++
14−
15−[\#205 Galen Wolfe-Pauly: Urbit - A Digital Republic Reinventing the Internet](https://epicenter.tv/episode/205/)
content/media/governing-the-future-e09-urbit-a-user-owned-internet-on-top-of-the-internet.mddeleted−16
@@ -1,16 +0,0 @@
1−+++
2−title = "Governing the Future E09: Urbit – A User Owned Internet on top of the Internet"
3−date = "2017-08-09"
4−description = "Galen talks with Hampus Jakobsson about Urbit on the Governing the Future podcast."
5−aliases = [
6− "/posts/governing-the-future-e09-urbit-a-user-owned-internet-on-top-of-the-internet/"
7−]
8−
9−[extra]
10−author = "Galen Wolfe-Pauly"
11−ship = "~ravmel-ropdyl"
12−+++
13−
14−> What happens when users get to control a piece of the internet. What does this browser of servers mean when we can use own client and still communicate? Can we create a new Internet owned by the people, an internet designed like a city by an architect and not only engineers?
15−
16−[E09: Galen Wolfe-Pauly, Urbit - a user owned internet on top of the internet](http://podcast.hajak.se/104117/548011-e09-galen-wolfe-pauly-urbit-a-user-owned-internet-on-top-of-the-internet)
content/media/lambdaconf-2016-curtis-yarvin-urbit-a-clean-slate-functional-operating-stack.mddeleted−15
@@ -1,15 +0,0 @@
1−+++
2−title = "LambdaConf 2016: Curtis Yarvin - Urbit: A Clean Slate Functional Operating Stack"
3−date = "2016-09-27"
4−description = "Curtis presents a comprehensive technical overview of Urbit at LambdaConf 2016."
5−aliases = [
6− "/posts/lambdaconf-2016-curtis-yarvin-urbit-a-clean-slate-functional-operating-stack/"
7−]
8−
9−[extra]
10−author = "Curtis Yarvin"
11−ship = "~sorreg-namtyv"
12−youtube = "bTisf4oxIFo"
13−+++
14−
15−[Curtis Yarvin: Urbit- A Clean Slate Functional Operating Stack - λC 2016](https://www.youtube.com/watch?v=bTisf4oxIFo)
content/media/lambdaconf-2018-ted-blackman-ford-urbits-build-system.mddeleted−15
@@ -1,15 +0,0 @@
1−+++
2−title = "LambdaConf 2018: Ted Blackman - Ford: Urbit's Build System"
3−date = "2018-12-19"
4−description = "Ted presents a technical overview of Ford, Urbit's build system, at LambdaConf 2018."
5−aliases = [
6− "/posts/lambdaconf-2018-ted-blackman-ford-urbits-build-system/"
7−]
8−
9−[extra]
10−author = "Ted Blackman"
11−ship = "~rovnys-ricfer"
12−youtube = "5ZAFEylHdiU"
13−+++
14−
15−[Ted Blackman Ford - Urbit's Build System - λC 2018](https://www.youtube.com/watch?v=5ZAFEylHdiU)
content/media/on-the-brink-castle-island.mddeleted−14
@@ -1,14 +0,0 @@
1−+++
2−title = "On The Brink with Castle Island - Episode 17"
3−description = "Christian Langalis and Logan Allen discuss Urbit with Nic Carter: its mutual benefits with Bitcoin, and why a complete rewrite of Unix and the computing stack is necessary."
4−date = "2019-11-10"
5−
6−[extra]
7−author = "Christian Langalis + Logan Allen"
8−ship = "~pindet-timmut + ~tacryt-socryp"
9−libsyn = "https://html5-player.libsyn.com/embed/episode/id/11959088/height/90/theme/custom/thumbnail/yes/direction/forward/render-playlist/no/custom-color/000000/"
10−+++
11−
12−"Christian Langalis and Logan Allen from Tlon join the show to discuss Urbit, the ambitious and somewhat mysterious computing project that they work on. We discuss how Bitcoin and Urbit could be mutually beneficial, how Urbit might free us from the tyrannical tech oligopolies, and why a from-scratch rewrite of Unix and the computing stack is necessary."
13−
14−<iframe style="border: none" src="//html5-player.libsyn.com/embed/episode/id/11959088/height/90/theme/custom/thumbnail/yes/direction/forward/render-playlist/no/custom-color/000000/" height="90" width="100%" scrolling="no" allowfullscreen webkitallowfullscreen mozallowfullscreen oallowfullscreen msallowfullscreen></iframe>
content/media/the-ether-review-46-curtis-yarvin-and-galen-wolfe-pauly-on-urbit.mddeleted−15
@@ -1,15 +0,0 @@
1−+++
2−title = "The Ether Review #46: Curtis Yarvin & Galen Wolfe-Pauly on Urbit"
3−date = "2016-10-23"
4−description = "Curtis and Galen talk about Urbit on The Ether Review podcast."
5−aliases = [
6− "/posts/the-ether-review-46-curtis-yarvin-and-galen-wolfe-pauly-on-urbit/"
7−]
8−
9−[extra]
10−author = "Curtis Yarvin + Galen Wolfe-Pauly"
11−ship = "~sorreg-namtyv + ~ravmel-ropdyl"
12−soundcloud = "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/289783332&color=%23000000&auto_play=false&hide_related=true&show_comments=false&show_user=true&show_reposts=false&show_teaser=false&visual=true"
13−+++
14−
15−[The Ether Review \#46 - Curtis Yarvin & Galen Wolfe-Pauly on Urbit](https://soundcloud.com/arthurfalls/the-ether-review-46-smoking-dmt-with-mencius-moldbug)
content/media/urbit-and-the-not-so-dark-future-of-the-internet.mddeleted−11
@@ -1,11 +0,0 @@
1−+++
2−title = "Urbit and the Not-So-Dark Future of the Internet"
3−date = "2017-04-11"
4−description = "Isaac Simpson's high-level introduction to Urbit."
5−aliases = [ "/posts/urbit-and-the-not-so-dark-future-of-the-internet/" ]
6−
7−[extra]
8−author = "Isaac Simpson"
9−+++
10−
11−[Urbit and the Not-So-Dark Future of the Internet](https://medium.com/vandal-press/urbit-and-the-not-so-dark-future-of-the-internet-400c9b667e2)
content/media/what-is-urbit.mddeleted−10
@@ -1,10 +0,0 @@
1−+++
2−title = "What is Urbit?"
3−date = "2017-05-14"
4−description = "A quick video explainer of how Urbit works."
5−
6−[extra]
7−youtube = "M04AKTCDavc"
8−+++
9−
10−[What is Urbit?](https://www.youtube.com/watch?v=M04AKTCDavc)
content/organizations/reciprocal.mdnew file+12
@@ -0,0 +1,12 @@
1++++
2+title = "Reciprocal Ltd."
3+image = "https://s3.us-east-1.amazonaws.com/haddefsigwen1/reciprocal/2022.5.20..19.52.50-B137D4AA-DA9E-4B07-98CE-C001C576C7CA_1_201_a.jpeg"
4+URL = "https://reciprocal.ltd"
5+ships = ["~sitden-sonnet", "~haddef-sigwen"]
6++++
7+
8+### Reciprocal Ltd. is an interactive design and development studio.
9+
10+They serve as full-service design and development consultancy for external partners and internally develop experimental applications, artwork, and interactive media.
11+
12+Members include [~haddef-sigwen](https://urbit.org/ids/~haddef-sigwen), [~libbyl-lonsyd](https://urbit.org/ids/~libbyl-lonsyd), and [~wolfer-witryx](https://urbit.org/ids/~wolfer-witryx).
content/podcasts/100-year-computer.mdnew file+11
@@ -0,0 +1,11 @@
1++++
2+title = "The 100-Year Computer with Galen Wolfe-Pauly | The Urbit Series"
3+podcast = "Other Life"
4+date = "February 1, 2022"
5+URL = "https://www.youtube.com/watch?v=YWOsBWBQAnQ"
6+image = "https://media.urbit.org/site/ecosystem/podcasts/full_1639502000-artwork.jpg"
7++++
8+
9+Galen Wolfe-Pauly is the CEO of Tlon. We discuss why Urbit is led by designers rather than engineers, the theories of Christopher Alexander, the longest-running company in the world (Kongo Gumi), and much more.
10+
11+Galen on Urbit: [~ravmel-ropdyl](/ids/~ravmel-ropdyl)
package-lock.json+38−38
@@ -45,9 +45,9 @@
45 }
45 }
46 },
46 },
47 "node_modules/@babel/runtime": {
47 "node_modules/@babel/runtime": {
48− "version": "7.18.6",
48+ "version": "7.18.9",
49− "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.6.tgz",
49+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz",
50− "integrity": "sha512-t9wi7/AW6XtKahAe20Yw0/mMljKq0B1r2fPdvaAdV/KPDZewFXdaaa6K7lxmZBZ8FBNpCiAT6iHPmd6QO9bKfQ==",
50+ "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==",
51 "dependencies": {
51 "dependencies": {
52 "regenerator-runtime": "^0.13.4"
52 "regenerator-runtime": "^0.13.4"
53 },
53 },
@@ -538,9 +538,9 @@
538 }
538 }
539 },
539 },
540 "node_modules/browserslist": {
540 "node_modules/browserslist": {
541− "version": "4.21.1",
541+ "version": "4.21.2",
542− "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.1.tgz",
542+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.2.tgz",
543− "integrity": "sha512-Nq8MFCSrnJXSc88yliwlzQe3qNe3VntIjhsArW9IJOEPSHNx23FalwApUVbzAWABLhYJJ7y8AynWI/XM8OdfjQ==",
543+ "integrity": "sha512-MonuOgAtUB46uP5CezYbRaYKBNt2LxP0yX+Pmj4LkcDFGkn9Cbpi83d9sCjwQDErXsIJSzY5oKGDbgOlF/LPAA==",
544 "funding": [
544 "funding": [
545 {
545 {
546 "type": "opencollective",
546 "type": "opencollective",
@@ -552,9 +552,9 @@
552 }
552 }
553 ],
553 ],
554 "dependencies": {
554 "dependencies": {
555− "caniuse-lite": "^1.0.30001359",
555+ "caniuse-lite": "^1.0.30001366",
556− "electron-to-chromium": "^1.4.172",
556+ "electron-to-chromium": "^1.4.188",
557− "node-releases": "^2.0.5",
557+ "node-releases": "^2.0.6",
558 "update-browserslist-db": "^1.0.4"
558 "update-browserslist-db": "^1.0.4"
559 },
559 },
560 "bin": {
560 "bin": {
@@ -596,9 +596,9 @@
596 }
596 }
597 },
597 },
598 "node_modules/caniuse-lite": {
598 "node_modules/caniuse-lite": {
599− "version": "1.0.30001365",
599+ "version": "1.0.30001367",
600− "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001365.tgz",
600+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001367.tgz",
601− "integrity": "sha512-VDQZ8OtpuIPMBA4YYvZXECtXbddMCUFJk1qu8Mqxfm/SZJNSr1cy4IuLCOL7RJ/YASrvJcYg1Zh+UEUQ5m6z8Q==",
601+ "integrity": "sha512-XDgbeOHfifWV3GEES2B8rtsrADx4Jf+juKX2SICJcaUhjYBO3bR96kvEIHa15VU6ohtOhBZuPGGYGbXMRn0NCw==",
602 "funding": [
602 "funding": [
603 {
603 {
604 "type": "opencollective",
604 "type": "opencollective",
@@ -856,9 +856,9 @@
856 }
856 }
857 },
857 },
858 "node_modules/electron-to-chromium": {
858 "node_modules/electron-to-chromium": {
859− "version": "1.4.187",
859+ "version": "1.4.194",
860− "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.187.tgz",
860+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.194.tgz",
861− "integrity": "sha512-t3iFLHVIMhB8jGZ+8ui951nz6Bna5qKfhxezG3wzXdBJ79qFKPsE2chjjVFNqC1ewhfrPQrw9pmVeo4FFpZeQA=="
861+ "integrity": "sha512-ola5UH0xAP1oYY0FFUsPvwtucEzCQHucXnT7PQ1zjHJMccZhCDktEugI++JUR3YuIs7Ff7afz+OVEhVAIMhLAQ=="
862 },
862 },
863 "node_modules/end-of-stream": {
863 "node_modules/end-of-stream": {
864 "version": "1.4.4",
864 "version": "1.4.4",
@@ -1116,7 +1116,7 @@
1116 },
1116 },
1117 "node_modules/foundation-design-system": {
1117 "node_modules/foundation-design-system": {
1118 "version": "0.0.0",
1118 "version": "0.0.0",
1119− "resolved": "git+ssh://git@github.com/urbit/foundation-design-system.git#af413f40c379005cf609c2adde545da9638f38e3",
1119+ "resolved": "git+ssh://git@github.com/urbit/foundation-design-system.git#a873bc83cf7fa92e0a54429c0be79a1c0157896c",
1120 "license": "MIT",
1120 "license": "MIT",
1121 "dependencies": {
1121 "dependencies": {
1122 "@markdoc/markdoc": "0.1.3",
1122 "@markdoc/markdoc": "0.1.3",
@@ -2848,9 +2848,9 @@
2848 }
2848 }
2849 },
2849 },
2850 "node_modules/update-browserslist-db": {
2850 "node_modules/update-browserslist-db": {
2851− "version": "1.0.4",
2851+ "version": "1.0.5",
2852− "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz",
2852+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz",
2853− "integrity": "sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA==",
2853+ "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==",
2854 "funding": [
2854 "funding": [
2855 {
2855 {
2856 "type": "opencollective",
2856 "type": "opencollective",
@@ -2958,9 +2958,9 @@
2958 },
2958 },
2959 "dependencies": {
2959 "dependencies": {
2960 "@babel/runtime": {
2960 "@babel/runtime": {
2961− "version": "7.18.6",
2961+ "version": "7.18.9",
2962− "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.6.tgz",
2962+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz",
2963− "integrity": "sha512-t9wi7/AW6XtKahAe20Yw0/mMljKq0B1r2fPdvaAdV/KPDZewFXdaaa6K7lxmZBZ8FBNpCiAT6iHPmd6QO9bKfQ==",
2963+ "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==",
2964 "requires": {
2964 "requires": {
2965 "regenerator-runtime": "^0.13.4"
2965 "regenerator-runtime": "^0.13.4"
2966 }
2966 }
@@ -3249,13 +3249,13 @@
3249 }
3249 }
3250 },
3250 },
3251 "browserslist": {
3251 "browserslist": {
3252− "version": "4.21.1",
3252+ "version": "4.21.2",
3253− "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.1.tgz",
3253+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.2.tgz",
3254− "integrity": "sha512-Nq8MFCSrnJXSc88yliwlzQe3qNe3VntIjhsArW9IJOEPSHNx23FalwApUVbzAWABLhYJJ7y8AynWI/XM8OdfjQ==",
3254+ "integrity": "sha512-MonuOgAtUB46uP5CezYbRaYKBNt2LxP0yX+Pmj4LkcDFGkn9Cbpi83d9sCjwQDErXsIJSzY5oKGDbgOlF/LPAA==",
3255 "requires": {
3255 "requires": {
3256− "caniuse-lite": "^1.0.30001359",
3256+ "caniuse-lite": "^1.0.30001366",
3257− "electron-to-chromium": "^1.4.172",
3257+ "electron-to-chromium": "^1.4.188",
3258− "node-releases": "^2.0.5",
3258+ "node-releases": "^2.0.6",
3259 "update-browserslist-db": "^1.0.4"
3259 "update-browserslist-db": "^1.0.4"
3260 }
3260 }
3261 },
3261 },
@@ -3274,9 +3274,9 @@
3274 "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="
3274 "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="
3275 },
3275 },
3276 "caniuse-lite": {
3276 "caniuse-lite": {
3277− "version": "1.0.30001365",
3277+ "version": "1.0.30001367",
3278− "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001365.tgz",
3278+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001367.tgz",
3279− "integrity": "sha512-VDQZ8OtpuIPMBA4YYvZXECtXbddMCUFJk1qu8Mqxfm/SZJNSr1cy4IuLCOL7RJ/YASrvJcYg1Zh+UEUQ5m6z8Q=="
3279+ "integrity": "sha512-XDgbeOHfifWV3GEES2B8rtsrADx4Jf+juKX2SICJcaUhjYBO3bR96kvEIHa15VU6ohtOhBZuPGGYGbXMRn0NCw=="
3280 },
3280 },
3281 "chalk": {
3281 "chalk": {
3282 "version": "2.4.2",
3282 "version": "2.4.2",
@@ -3465,9 +3465,9 @@
3465 }
3465 }
3466 },
3466 },
3467 "electron-to-chromium": {
3467 "electron-to-chromium": {
3468− "version": "1.4.187",
3468+ "version": "1.4.194",
3469− "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.187.tgz",
3469+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.194.tgz",
3470− "integrity": "sha512-t3iFLHVIMhB8jGZ+8ui951nz6Bna5qKfhxezG3wzXdBJ79qFKPsE2chjjVFNqC1ewhfrPQrw9pmVeo4FFpZeQA=="
3470+ "integrity": "sha512-ola5UH0xAP1oYY0FFUsPvwtucEzCQHucXnT7PQ1zjHJMccZhCDktEugI++JUR3YuIs7Ff7afz+OVEhVAIMhLAQ=="
3471 },
3471 },
3472 "end-of-stream": {
3472 "end-of-stream": {
3473 "version": "1.4.4",
3473 "version": "1.4.4",
@@ -3650,7 +3650,7 @@
3650 "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA=="
3650 "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA=="
3651 },
3651 },
3652 "foundation-design-system": {
3652 "foundation-design-system": {
3653− "version": "git+ssh://git@github.com/urbit/foundation-design-system.git#af413f40c379005cf609c2adde545da9638f38e3",
3653+ "version": "git+ssh://git@github.com/urbit/foundation-design-system.git#a873bc83cf7fa92e0a54429c0be79a1c0157896c",
3654 "from": "foundation-design-system@github:urbit/foundation-design-system",
3654 "from": "foundation-design-system@github:urbit/foundation-design-system",
3655 "requires": {
3655 "requires": {
3656 "@markdoc/markdoc": "0.1.3",
3656 "@markdoc/markdoc": "0.1.3",
@@ -4847,9 +4847,9 @@
4847 }
4847 }
4848 },
4848 },
4849 "update-browserslist-db": {
4849 "update-browserslist-db": {
4850− "version": "1.0.4",
4850+ "version": "1.0.5",
4851− "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz",
4851+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz",
4852− "integrity": "sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA==",
4852+ "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==",
4853 "requires": {
4853 "requires": {
4854 "escalade": "^3.1.1",
4854 "escalade": "^3.1.1",
4855 "picocolors": "^1.0.0"
4855 "picocolors": "^1.0.0"
pages/ecosystem/index.jsnew file+341
@@ -0,0 +1,341 @@
1+// All sections in one template
2+// Grid-columns-three
3+// Get content from content/ecosystem/spotlight, content/applications, content/marketplaces, content/organizations, content/podcasts, content/groups
4+import { useRouter } from "next/router";
5+import Head from "next/head";
6+import Meta from "../../components/Meta";
7+import {
8+ Container,
9+ SingleColumn,
10+ Section,
11+ IntraNav,
12+ Markdown,
13+} from "foundation-design-system";
14+import Header from "../../components/Header";
15+import Footer from "../../components/Footer";
16+import Sidebar from "../../components/Sidebar";
17+import Post from "../../components/ecosystem/Post";
18+import classnames from "classnames";
19+import Link from "next/link";
20+import { getAllPosts } from "../../lib/lib";
21+import fs from "fs";
22+import path from "path";
23+import Contact from "../../components/Contact";
24+import { generateDisplayDate, formatDate } from "../../lib/lib";
25+
26+export default function Ecosystem({
27+ search,
28+ post,
29+ posts,
30+ applications,
31+ marketplaces,
32+ organizations,
33+ podcasts,
34+}) {
35+ const router = useRouter();
36+
37+ const { type } = router.query;
38+
39+ if (type === "archive") {
40+ return <SpotlightArchive posts={posts} search={search} />;
41+ }
42+
43+ let title;
44+ if (type) {
45+ switch (type) {
46+ case "applications":
47+ title = "Applications";
48+ break;
49+ case "marketplaces":
50+ title = "Marketplaces";
51+ break;
52+ case "podcasts":
53+ title = "Podcasts";
54+ break;
55+ case "organizations":
56+ title = "Organizations";
57+ break;
58+ default:
59+ title = "Spotlight";
60+ }
61+ } else {
62+ title = "Spotlight";
63+ }
64+
65+ const data = {
66+ title: title,
67+ description: "Explore the Urbit ecosystem.",
68+ };
69+ return (
70+ <Container>
71+ <Head>
72+ <title>{title} • Urbit</title>
73+ {Meta(data)}
74+ </Head>
75+ <IntraNav ourSite="https://urbit.org" search={search} />
76+ <SingleColumn>
77+ <Header />
78+ <Section>
79+ <h1>Ecosystem</h1>
80+ </Section>
81+ <Section className="justify-center">
82+ <div className="flex justify-between sidebar">
83+ <Sidebar>
84+ <EcosystemSidebar />
85+ </Sidebar>
86+ <div
87+ className={classnames("w-full", {
88+ "max-w-prose": type === undefined,
89+ })}
90+ >
91+ {type === undefined && (
92+ <h3>{`Ecosystem Spotlight - ${post.title}`}</h3>
93+ )}
94+ {type === undefined && (
95+ <>
96+ <Post post={post} />
97+ <Link href="/ecosystem/?type=archive" passHref>
98+ <a className="button-lg bg-wall-600 text-white w-fit">
99+ Spotlight Archive
100+ </a>
101+ </Link>
102+ </>
103+ )}
104+ <div
105+ className={classnames("grid grid-cols-2 gap-12 w-full", {
106+ "md:grid-cols-3": type !== "podcasts",
107+ hidden: type === undefined,
108+ })}
109+ >
110+ {type === "organizations" &&
111+ organizations.map((org) => (
112+ <Link href={`/organizations/${org.slug}`}>
113+ <div className="flex flex-col space-y-4 justify-center items-center cursor-pointer">
114+ <img className="w-36" src={org.image} />
115+ <p className="text-center font-bold">{org.title}</p>
116+ </div>
117+ </Link>
118+ ))}
119+ {type === "applications" &&
120+ applications.map((app) => (
121+ <Link href={`/applications/${app.ship}/${app.slug}`}>
122+ <div className="flex flex-col space-y-4 justify-center items-center cursor-pointer">
123+ <div
124+ className="w-36 h-36 overflow-hidden rounded-xl"
125+ style={{
126+ backgroundColor: app?.bgColor || "rgba(0,0,0,0)",
127+ }}
128+ >
129+ {app?.image && (
130+ <img className="w-full h-full" src={app.image} />
131+ )}
132+ </div>
133+ <p className="text-center font-bold">{app.title}</p>
134+ </div>
135+ </Link>
136+ ))}
137+ {type === "marketplaces" &&
138+ marketplaces.map((market) => (
139+ <Link href={`/marketplaces/${market.slug}`}>
140+ <div className="flex flex-col space-y-4 justify-center items-center cursor-pointer">
141+ <img className="w-36" src={market.image} />
142+ <p className="text-center font-bold">{market.title}</p>
143+ </div>
144+ </Link>
145+ ))}
146+ {type === "podcasts" &&
147+ podcasts.map((pod) => (
148+ <Link href={`/podcasts/${pod.slug}`}>
149+ <div className="flex cursor-pointer space-x-4 items-center">
150+ <img className="w-36" src={pod.image} />
151+ <div className="flex flex-col space-y-4">
152+ <p className="font-bold">{pod.podcast}</p>
153+ <p>{pod.title}</p>
154+ <p className="text-wall-400">{pod.date}</p>
155+ </div>
156+ </div>
157+ </Link>
158+ ))}
159+ </div>
160+ </div>
161+ </div>
162+ <Contact emphasize className="pt-36 self-center w-fit mx-auto" />
163+ </Section>
164+ </SingleColumn>
165+ <Footer />
166+ </Container>
167+ );
168+}
169+
170+function ActiveLink({ children, href, className, currentPath }) {
171+ // special casing this behaviour which overloads one nav
172+ const archive = Boolean(
173+ currentPath === "/ecosystem?type=archive" && href === "/ecosystem"
174+ );
175+ const activeClassName = classnames({
176+ "text-wall-600": currentPath === href || archive,
177+ "text-wall-500": currentPath !== href && !archive,
178+ });
179+
180+ return (
181+ <Link href={href} passHref>
182+ <a className={`${className} ${activeClassName}`}>{children}</a>
183+ </Link>
184+ );
185+}
186+
187+function EcosystemSidebar() {
188+ const router = useRouter();
189+ let currentPath = router.asPath;
190+ return (
191+ <div className="flex flex-col space-y-4 pr-24">
192+ <ActiveLink
193+ currentPath={currentPath}
194+ className="type-ui"
195+ href="/ecosystem"
196+ >
197+ Spotlight
198+ </ActiveLink>
199+ <ActiveLink
200+ currentPath={currentPath}
201+ className="type-ui"
202+ href="/ecosystem?type=organizations"
203+ >
204+ Organizations
205+ </ActiveLink>
206+ <ActiveLink
207+ currentPath={currentPath}
208+ className="type-ui"
209+ href="/ecosystem?type=applications"
210+ >
211+ Applications
212+ </ActiveLink>
213+ <ActiveLink
214+ currentPath={currentPath}
215+ className="type-ui"
216+ href="/ecosystem?type=marketplaces"
217+ >
218+ Marketplaces
219+ </ActiveLink>
220+ <ActiveLink
221+ currentPath={currentPath}
222+ className="mr-5 type-ui"
223+ href="/ecosystem?type=podcasts"
224+ >
225+ Podcasts
226+ </ActiveLink>
227+ </div>
228+ );
229+}
230+
231+function SpotlightArchive({ posts, search }) {
232+ const post = {
233+ title: "Spotlight Archive",
234+ description: "The archive of ecosystem spotlights.",
235+ };
236+ return (
237+ <Container>
238+ <Head>
239+ <title>Archive • Spotlight • Urbit</title>
240+ {Meta(post)}
241+ </Head>
242+ <IntraNav ourSite="https://urbit.org" search={search} />
243+ <SingleColumn>
244+ <Header />
245+ <Section>
246+ <div className="measure">
247+ <h1>Ecosystem</h1>
248+ </div>
249+ </Section>
250+ <Section className="flex">
251+ <Sidebar>
252+ <EcosystemSidebar />
253+ </Sidebar>
254+ <div>
255+ <h3 className="mb-12">Spotlight Archive</h3>
256+ {posts.map((post) => {
257+ return (
258+ <div key={post.slug} className="mb-24 cursor-pointer">
259+ <Link href={`/ecosystem/spotlight/${post.slug}`}>
260+ <div>
261+ <h3 className="text-green-400">{post.title}</h3>
262+ <div className="type-ui mt-2">
263+ {["featured-1", "featured-2", "featured-3"].map(
264+ (feat, i) => {
265+ if (post?.[feat]) {
266+ return (
267+ <>
268+ {i ? ", " : ""}
269+ <span>{post[feat].title}</span>
270+ </>
271+ );
272+ }
273+ }
274+ )}
275+ </div>
276+ </div>
277+ </Link>
278+ </div>
279+ );
280+ })}
281+ </div>
282+ </Section>
283+ </SingleColumn>
284+ <Footer />
285+ </Container>
286+ );
287+}
288+
289+export async function getStaticProps({}) {
290+ const posts = getAllPosts(
291+ ["title", "date", "slug", "featured-1", "featured-2", "featured-3"],
292+ "ecosystem/spotlight",
293+ "date"
294+ );
295+ let post = getAllPosts(
296+ ["title", "date", "featured-1", "featured-2", "featured-3"],
297+ "ecosystem/spotlight",
298+ "date"
299+ )[0];
300+ ["featured-1", "featured-2", "featured-3"].forEach((feat) => {
301+ if (post?.[feat]) {
302+ post[feat].content = JSON.stringify(
303+ Markdown.parse({
304+ post: { content: post[feat].content },
305+ })
306+ );
307+ }
308+ });
309+ const marketplaces = getAllPosts(["title", "image", "slug"], "marketplaces");
310+ const podcasts = getAllPosts(
311+ ["title", "image", "date", "podcast", "slug"],
312+ "podcasts"
313+ );
314+ const organizations = getAllPosts(
315+ ["title", "image", "slug"],
316+ "organizations"
317+ );
318+ const applications = fs
319+ .readdirSync(path.join(process.cwd(), "content/applications"), {
320+ withFileTypes: true,
321+ })
322+ .filter((f) => f.isDirectory())
323+ .map((dir) =>
324+ getAllPosts(
325+ ["title", "bgColor", "image", "slug"],
326+ `applications/${dir.name}`
327+ )
328+ .map((e) => ({ ...e, ship: dir.name }))
329+ .flat()
330+ )
331+ .flat()
332+ .sort((a, b) => {
333+ const nameA = a.title.toLowerCase();
334+ const nameB = b.title.toLowerCase();
335+ return nameA < nameB ? -1 : 1;
336+ });
337+
338+ return {
339+ props: { posts, post, applications, marketplaces, podcasts, organizations },
340+ };
341+}
pages/ecosystem/spotlight/[slug].jsnew file+77
@@ -0,0 +1,77 @@
1+import { useRouter } from "next/router";
2+import Head from "next/head";
3+import Meta from "../../../components/Meta";
4+import ErrorPage from "../../../pages/404";
5+import {
6+ Container,
7+ SingleColumn,
8+ Section,
9+ Markdown,
10+ IntraNav,
11+} from "foundation-design-system";
12+import Header from "../../../components/Header";
13+import Footer from "../../../components/Footer";
14+import Post from "../../../components/ecosystem/Post";
15+import { getAllPosts, getPostBySlug } from "../../../lib/lib";
16+
17+export default function BasicPage({ post, markdown, search, index = false }) {
18+ const router = useRouter();
19+ if (!router.isFallback && !post?.slug) {
20+ return <ErrorPage />;
21+ }
22+ return (
23+ <Container>
24+ <Head>
25+ <title>Urbit • {post.title}</title>
26+ {Meta(post)}
27+ </Head>
28+ <IntraNav ourSite="https://urbit.org" search={search} />
29+ <SingleColumn>
30+ <Header />
31+ <Section narrow>
32+ <h1>Ecosystem Spotlight - {post.title}</h1>
33+ </Section>
34+ <Section narrow>
35+ <Post post={post} />
36+ </Section>
37+ </SingleColumn>
38+ <Footer />
39+ </Container>
40+ );
41+}
42+
43+export async function getStaticProps({ params }) {
44+ const post = getPostBySlug(
45+ params.slug,
46+ ["title", "slug", "date", "featured-1", "featured-2", "featured-3"],
47+ "ecosystem/spotlight"
48+ );
49+ ["featured-1", "featured-2", "featured-3"].forEach((feat) => {
50+ if (post?.[feat]) {
51+ post[feat].content = JSON.stringify(
52+ Markdown.parse({
53+ post: { content: post[feat].content },
54+ })
55+ );
56+ }
57+ });
58+
59+ return {
60+ props: { post },
61+ };
62+}
63+
64+export async function getStaticPaths() {
65+ const posts = getAllPosts(["slug", "date"], "ecosystem/spotlight", "date");
66+
67+ return {
68+ paths: posts.map((post) => {
69+ return {
70+ params: {
71+ slug: post.slug,
72+ },
73+ };
74+ }),
75+ fallback: false,
76+ };
77+}
pages/index.js+50−6
@@ -16,8 +16,15 @@ import EventPreview from "../components/EventPreview";
16import TwoUp from "../components/TwoUp";
16import TwoUp from "../components/TwoUp";
17import { getAllPosts, getAllEvents, generateRealtimeDate } from "../lib/lib";
17import { getAllPosts, getAllEvents, generateRealtimeDate } from "../lib/lib";
18import { eventKeys } from "../lib/constants";
18import { eventKeys } from "../lib/constants";
19+import IndexCard from "../components/ecosystem/IndexCard";
19
20
20−export default function Home({ posts, events, grantNumbers, search }) {
21+export default function Home({
22+ posts,
23+ ecosystem,
24+ events,
25+ grantNumbers,
26+ search,
27+}) {
21 return (
28 return (
22 <Container>
29 <Container>
23 <Head>
30 <Head>
@@ -69,9 +76,9 @@ export default function Home({ posts, events, grantNumbers, search }) {
69 computer from scratch. Good thing we started over a decade ago.
76 computer from scratch. Good thing we started over a decade ago.
70 </p>
77 </p>
71 <p className="max-w-prose">
78 <p className="max-w-prose">
72− Today, Urbit is a real system with thousands of users that are building
79+ Today, Urbit is a real system with thousands of users that are
73− all kinds of communities, software, DAOs, and more. And it’s getting
80+ building all kinds of communities, software, DAOs, and more. And
74− better every day.{" "}
81+ it’s getting better every day.{" "}
75 </p>
82 </p>
76 <Link href="/overview" passHref>
83 <Link href="/overview" passHref>
77 <a className="button-lg bg-green-400 text-white type-ui max-w-fit">
84 <a className="button-lg bg-green-400 text-white type-ui max-w-fit">
@@ -79,7 +86,38 @@ export default function Home({ posts, events, grantNumbers, search }) {
79 </a>
86 </a>
80 </Link>
87 </Link>
81 </Section>
88 </Section>
82−
89+ {/* Ecosystem */}
90+ <Section>
91+ <div className="flex flex-col space-y-8 pb-12">
92+ <h2 className="m-0 p-0 mr-4">Ecosystem</h2>
93+ <p>
94+ The Urbit ecosystem is comprised of a wide variety of individuals
95+ and organizations, including developers, DAOs, podcasters, and
96+ more.
97+ </p>
98+ <div className="flex flex-col space-y-4 md:space-y-0 md:flex-row md:space-x-4">
99+ {["featured-1", "featured-2", "featured-3"].map((feat) => {
100+ if (ecosystem?.[feat]) {
101+ return (
102+ <IndexCard
103+ slug={
104+ ecosystem[feat].type === "Application"
105+ ? `/ids/${ecosystem[feat].URL}`
106+ : ecosystem[feat].URL
107+ }
108+ feat={ecosystem[feat]}
109+ />
110+ );
111+ }
112+ })}
113+ </div>
114+ <Link href="/ecosystem" passHref>
115+ <a className="button-lg bg-green-400 text-white w-fit">
116+ Explore Ecosystem
117+ </a>
118+ </Link>
119+ </div>
120+ </Section>
83 {
121 {
84 // Grants
122 // Grants
85 }
123 }
@@ -222,7 +260,6 @@ export default function Home({ posts, events, grantNumbers, search }) {
222 <Contact emphasize />
260 <Contact emphasize />
223 </Section>
261 </Section>
224 </SingleColumn>
262 </SingleColumn>
225−
226 <Footer />
263 <Footer />
227 </Container>
264 </Container>
228 );
265 );
@@ -235,6 +272,12 @@ export async function getStaticProps() {
235 "date"
272 "date"
236 );
273 );
237
274
275+ const ecosystem = getAllPosts(
276+ ["featured-1", "featured-2", "featured-3"],
277+ "ecosystem/spotlight",
278+ "date"
279+ )[0];
280+
238 const events = getAllEvents(eventKeys, "events");
281 const events = getAllEvents(eventKeys, "events");
239
282
240 const grants = getAllPosts(["extra", "taxonomies"], "grants", "date");
283 const grants = getAllPosts(["extra", "taxonomies"], "grants", "date");
@@ -291,6 +334,7 @@ export async function getStaticProps() {
291 return {
334 return {
292 props: {
335 props: {
293 posts,
336 posts,
337+ ecosystem,
294 grantNumbers,
338 grantNumbers,
295 events: happeningNow.concat(futureEvents).concat(pastEvents),
339 events: happeningNow.concat(futureEvents).concat(pastEvents),
296 },
340 },
pages/marketplaces/[slug].jsnew file+80
@@ -0,0 +1,80 @@
1+import { getAllPosts, getPostBySlug, getPostSlugs } from "../../lib/lib";
2+import BasicPage from "../../components/ecosystem/BasicPage";
3+import { Markdown } from "foundation-design-system";
4+import Link from "next/link";
5+
6+export default function Marketplace({
7+ post,
8+ markdown,
9+ search,
10+ index,
11+ spotlights = [],
12+}) {
13+ return (
14+ <BasicPage
15+ section="Marketplaces"
16+ post={post}
17+ markdown={markdown}
18+ search={search}
19+ index={index}
20+ >
21+ {spotlights.length > 0 && (
22+ <p>
23+ Featured in{" "}
24+ {spotlights.map((e, i) => (
25+ <>
26+ {i ? ", " : ""}
27+ <Link href={`/ecosystem/spotlight/${e.slug}`}>{e.title}</Link>
28+ </>
29+ ))}
30+ </p>
31+ )}
32+ </BasicPage>
33+ );
34+}
35+
36+export async function getStaticProps({ params }) {
37+ const post = getPostBySlug(
38+ params.slug,
39+ ["title", "slug", "URL", "image", "content"],
40+ "marketplaces"
41+ );
42+
43+ const spotlights = getAllPosts(
44+ ["title", "slug", "featured-1", "featured-2", "featured-3"],
45+ "ecosystem/spotlight"
46+ ).filter(
47+ (e) =>
48+ e?.["featured-1"]?.title === post.title ||
49+ e?.["featured-2"]?.title === post.title ||
50+ e?.["featured-3"]?.title === post.title
51+ );
52+
53+ let { index } = post?.extra || { index: null };
54+
55+ if (index === undefined) {
56+ index = null;
57+ }
58+
59+ const markdown = JSON.stringify(Markdown.parse({ post }));
60+ return {
61+ props: { post, markdown, spotlights, index },
62+ };
63+}
64+
65+export async function getStaticPaths() {
66+ const posts = getPostSlugs("marketplaces");
67+
68+ const slugs = posts.map((e) => e.slice(0, -3));
69+
70+ return {
71+ paths: slugs.map((post) => {
72+ return {
73+ params: {
74+ slug: post,
75+ },
76+ };
77+ }),
78+ fallback: false,
79+ };
80+}
pages/media.jsdeleted−114
@@ -1,114 +0,0 @@
1−import Head from "next/head";
2−import Link from "next/link";
3−import Meta from "../components/Meta";
4−import {
5− Container,
6− SingleColumn,
7− Section,
8− IntraNav,
9−} from "foundation-design-system";
10−import Footer from "../components/Footer";
11−import Header from "../components/Header";
12−import { getAllPosts } from "../lib/lib";
13−
14−export function MediaPreview({ event, dir, className }) {
15− // Event tiles have a 'dark mode' used when their background images are dark and white text is needed for legibility.
16− const grayText = event.extra?.dark ? "text-midWhite" : "text-wall-500";
17− const blackText = event.extra?.dark ? "text-white" : "text-wall-600";
18−
19− return (
20− <div
21− key={event.slug}
22− className={`mb-24 cursor-pointer bg-wall-100 rounded-xl h-96 bg-cover bg-center bg-no-repeat ${className}`}
23− style={{
24− backgroundImage:
25− `${
26− event.extra?.overlay
27− ? "linear-gradient(rgba(0,0,0,0.3), rgba(0,0,0,0.3)),"
28− : ""
29− }url(${event.extra?.image})` || "",
30− }}
31− >
32− <Link href={`${dir ? dir : "/events/"}${event.slug}`}>
33− <div className="flex flex-col p-8 justify-between items-between h-full relative">
34− <div className="grow-1 flex flex-col justify-center h-full">
35− <h3 className={`mb-4 ${blackText}`}>{event.title}</h3>
36− <h4 className={grayText}>{event?.type}</h4>
37− </div>
38− <div className="absolute left-8 bottom-8 w-full">
39− <p className={grayText}>{event.extra.host}</p>
40− <p className={grayText}>{event.extra.guests}</p>
41− </div>
42− {event.extra.youtube ? (
43− <a
44− onClick={(e) => e.stopPropagation()}
45− className="absolute w-16 h-16 right-8 bottom-8 bg-white flex items-center justify-center rounded-full"
46− target="_blank"
47− href={`https://www.youtube.com/watch?v=${event.extra.youtube}`}
48− >
49− <svg
50− width="48"
51− height="48"
52− viewBox="0 0 16 16"
53− fill="none"
54− xmlns="http://www.w3.org/2000/svg"
55− >
56− <path
57− d="M11.25 7.56699C11.5833 7.75944 11.5833 8.24056 11.25 8.43301L6.75 11.0311C6.41667 11.2235 6 10.983 6 10.5981L6 5.40192C6 5.01702 6.41667 4.77646 6.75 4.96891L11.25 7.56699Z"
58− fill="black"
59− />
60− </svg>
61− </a>
62− ) : null}
63− </div>
64− </Link>
65− </div>
66− );
67−}
68−
69−export default function Media({ posts, search }) {
70− const post = {
71− title: "Media",
72− description:
73− "A selection of external appearances by the Tlon Corporation and the Urbit Foundation.",
74− };
75− return (
76− <Container>
77− <Head>
78− <title>Media • urbit.org</title>
79− {Meta(post)}
80− </Head>
81− <IntraNav ourSite="https://urbit.org" search={search} />
82− <SingleColumn>
83− <Header />
84− <Section narrow>
85− <div className="measure">
86− <h1 className="pb-16">Media</h1>
87− <p className="pb-6">
88− Selected external media appearances from Tlon and the Urbit
89− Foundation.
90− </p>
91− </div>
92− </Section>
93− <Section narrow>
94− {posts.map((post) => {
95− return <MediaPreview event={post} dir="/media/" />;
96− })}
97− </Section>
98− </SingleColumn>
99− <Footer />
100− </Container>
101− );
102−}
103−
104−export async function getStaticProps() {
105− const posts = getAllPosts(
106− ["title", "slug", "date", "description", "extra"],
107− "media",
108− "date"
109− );
110−
111− return {
112− props: { posts },
113− };
114−}
pages/media/[slug].jsdeleted−117
@@ -1,117 +0,0 @@
1−import { useRouter } from "next/router";
2−import {
3− getPostBySlug,
4− getAllPosts,
5− formatDate,
6− generateDisplayDate,
7−} from "../../lib/lib";
8−import Head from "next/head";
9−import Link from "next/link";
10−import Meta from "../../components/Meta";
11−import ErrorPage from "../404";
12−import {
13− Container,
14− Markdown,
15− SingleColumn,
16− Section,
17− IntraNav,
18−} from "foundation-design-system";
19−import Header from "../../components/Header";
20−import Footer from "../../components/Footer";
21−import Contact from "../../components/Contact";
22−
23−export default function MediaPage({ post, markdown, search }) {
24− const router = useRouter();
25− if (!router.isFallback && !post?.slug) {
26− return <ErrorPage />;
27− }
28− const date = generateDisplayDate(post.date);
29− return (
30− <Container>
31− <Head>
32− <title>{post.title} • Media • urbit.org</title>
33− {Meta(post)}
34− </Head>
35− <IntraNav ourSite="https://urbit.org" search={search} />
36− <SingleColumn>
37− <Header />
38− <Section narrow short>
39− <h1>{post.title}</h1>
40− {post.extra.author ? (
41− <div className="type-ui text-wall-500 mt-4 md:mt-8 lg:mt-10">
42− {post.extra.author}
43− </div>
44− ) : null}
45− {post.extra.ship ? (
46− <Link href={`/ids/${post.extra.ship}`} passHref>
47− <a className="type-sub-bold text-wall-500 font-mono">
48− {post.extra.ship}
49− </a>
50− </Link>
51− ) : null}
52− <div className="type-ui text-wall-500 mt-16">{formatDate(date)}</div>
53− </Section>
54− <Section narrow>
55− {post.extra.youtube ? (
56− <iframe
57− className="rounded-xl"
58− width="100%"
59− height="640px"
60− src={`https://www.youtube.com/embed/${post.extra.youtube}`}
61− frameborder="0"
62− allow="encrypted-media"
63− allowfullscreen
64− ></iframe>
65− ) : null}
66− {post.extra.soundcloud ? (
67− <iframe
68− width="100%"
69− height="300"
70− scrolling="no"
71− frameborder="no"
72− allow="autoplay"
73− src={post.extra.soundcloud}
74− ></iframe>
75− ) : null}
76− </Section>
77− <Section narrow className="markdown">
78− <Markdown.render content={JSON.parse(markdown)} />
79− </Section>
80− <Section narrow>
81− <Contact />
82− </Section>
83− </SingleColumn>
84− <Footer />
85− </Container>
86− );
87−}
88−
89−//
90−export async function getStaticProps({ params }) {
91− const post = getPostBySlug(
92− params.slug,
93− ["title", "slug", "date", "description", "content", "extra"],
94− "media"
95− );
96−
97− const markdown = JSON.stringify(Markdown.parse({ post }));
98−
99− return {
100− props: { post, markdown },
101− };
102−}
103−
104−export async function getStaticPaths() {
105− const posts = getAllPosts(["slug", "date"], "media", "date");
106−
107− return {
108− paths: posts.map((post) => {
109− return {
110− params: {
111− slug: post.slug,
112− },
113− };
114− }),
115− fallback: false,
116− };
117−}
pages/organizations/[slug].jsnew file+137
@@ -0,0 +1,137 @@
1+// Single page per organisation
2+// Needs to have *associated ships* by which it polls content/applications for applications it "owns"
3+// Needs to look for its title in ecosystem/spotlight
4+import { getAllPosts, getPostBySlug, getPostSlugs } from "../../lib/lib";
5+import BasicPage from "../../components/ecosystem/BasicPage";
6+import { Markdown } from "foundation-design-system";
7+import Link from "next/link";
8+import fs from "fs";
9+import path from "path";
10+
11+export default function Organization({
12+ post,
13+ markdown,
14+ search,
15+ index,
16+ group,
17+ applications = [],
18+ spotlights = [],
19+}) {
20+ return (
21+ <BasicPage
22+ section="Organizations"
23+ post={post}
24+ group={group}
25+ markdown={markdown}
26+ search={search}
27+ index={index}
28+ >
29+ {spotlights.length > 0 && (
30+ <p>
31+ Featured in{" "}
32+ {spotlights.map((e, i) => (
33+ <>
34+ {i ? ", " : ""}
35+ <Link href={`/ecosystem/spotlight/${e.slug}`}>{e.title}</Link>
36+ </>
37+ ))}
38+ </p>
39+ )}
40+ {applications.length > 0 && (
41+ <>
42+ <h3>Applications</h3>
43+ <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
44+ {applications.map((app) => (
45+ <Link href={`/applications/${app.ship}/${app.slug}`}>
46+ <div className="flex flex-col space-y-4 cursor-pointer">
47+ <div
48+ className="overflow-hidden rounded-xl"
49+ style={{ backgroundColor: app.bgColor || "rgba(0,0,0,0)" }}
50+ >
51+ {app.image && (
52+ <img src={app.image} className="h-full w-full" />
53+ )}
54+ </div>
55+ <p className="text-center font-bold font-xl">{app.title}</p>
56+ </div>
57+ </Link>
58+ ))}
59+ </div>
60+ </>
61+ )}
62+ </BasicPage>
63+ );
64+}
65+
66+export async function getStaticProps({ params }) {
67+ const post = getPostBySlug(
68+ params.slug,
69+ ["title", "slug", "URL", "image", "ships", "group", "content"],
70+ "organizations"
71+ );
72+
73+ const spotlights = getAllPosts(
74+ ["title", "slug", "featured-1", "featured-2", "featured-3"],
75+ "ecosystem/spotlight"
76+ ).filter(
77+ (e) =>
78+ e?.["featured-1"]?.title === post.title ||
79+ e?.["featured-2"]?.title === post.title ||
80+ e?.["featured-3"]?.title === post.title
81+ );
82+
83+ const applications = post.ships
84+ .map((ship) => {
85+ let apps = [];
86+ if (
87+ fs.existsSync(path.join(process.cwd(), "content/applications", ship))
88+ ) {
89+ apps = getAllPosts(
90+ ["title", "image", "bgColor", "slug"],
91+ `applications/${ship}`
92+ ).map((e) => ({ ...e, ship }));
93+ }
94+ return apps;
95+ })
96+ .flat();
97+
98+ let group = null;
99+ const groupHost = post?.group?.slice(0, post.group.indexOf("/"));
100+ const groupName = post?.group?.slice(post.group.indexOf("/"));
101+ if (
102+ post.group &&
103+ fs.existsSync(path.join(process.cwd(), "content/groups", groupHost)) &&
104+ fs.existsSync(
105+ path.join(process.cwd(), "content/groups", groupHost, `${groupName}.md`)
106+ )
107+ ) {
108+ group = getPostBySlug(groupName, ["title"], `groups/${groupHost}`);
109+ }
110+ let { index } = post?.extra || { index: null };
111+
112+ if (index === undefined) {
113+ index = null;
114+ }
115+
116+ const markdown = JSON.stringify(Markdown.parse({ post }));
117+ return {
118+ props: { post, markdown, spotlights, applications, group, index },
119+ };
120+}
121+
122+export async function getStaticPaths() {
123+ const posts = getPostSlugs("organizations");
124+
125+ const slugs = posts.map((e) => e.slice(0, -3));
126+
127+ return {
128+ paths: slugs.map((post) => {
129+ return {
130+ params: {
131+ slug: post,
132+ },
133+ };
134+ }),
135+ fallback: false,
136+ };
137+}
pages/podcasts/[slug].jsnew file+80
@@ -0,0 +1,80 @@
1+import { getAllPosts, getPostBySlug, getPostSlugs } from "../../lib/lib";
2+import BasicPage from "../../components/ecosystem/BasicPage";
3+import { Markdown } from "foundation-design-system";
4+import Link from "next/link";
5+
6+export default function Podcast({
7+ post,
8+ markdown,
9+ search,
10+ index,
11+ spotlights = [],
12+}) {
13+ return (
14+ <BasicPage
15+ section="Podcasts"
16+ post={post}
17+ markdown={markdown}
18+ search={search}
19+ index={index}
20+ >
21+ {spotlights.length > 0 && (
22+ <p>
23+ Featured in{" "}
24+ {spotlights.map((e, i) => (
25+ <>
26+ {i ? ", " : ""}
27+ <Link href={`/ecosystem/spotlight/${e.slug}`}>{e.title}</Link>
28+ </>
29+ ))}
30+ </p>
31+ )}
32+ </BasicPage>
33+ );
34+}
35+
36+export async function getStaticProps({ params }) {
37+ const post = getPostBySlug(
38+ params.slug,
39+ ["title", "slug", "URL", "podcast", "date", "image", "content"],
40+ "podcasts"
41+ );
42+
43+ const spotlights = getAllPosts(
44+ ["title", "slug", "featured-1", "featured-2", "featured-3"],
45+ "ecosystem/spotlight"
46+ ).filter(
47+ (e) =>
48+ e?.["featured-1"]?.title === post.title ||
49+ e?.["featured-2"]?.title === post.title ||
50+ e?.["featured-3"]?.title === post.title
51+ );
52+
53+ let { index } = post?.extra || { index: null };
54+
55+ if (index === undefined) {
56+ index = null;
57+ }
58+
59+ const markdown = JSON.stringify(Markdown.parse({ post }));
60+ return {
61+ props: { post, markdown, spotlights, index },
62+ };
63+}
64+
65+export async function getStaticPaths() {
66+ const posts = getPostSlugs("podcasts");
67+
68+ const slugs = posts.map((e) => e.slice(0, -3));
69+
70+ return {
71+ paths: slugs.map((post) => {
72+ return {
73+ params: {
74+ slug: post,
75+ },
76+ };
77+ }),
78+ fallback: false,
79+ };
80+}
public/images/sound.svgnew file+5
@@ -0,0 +1,5 @@
1+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2+<path d="M16.0445 16.6848C15.6226 16.862 15.137 16.6642 14.9589 16.2423C14.7817 15.8204 14.9795 15.3348 15.4014 15.1567C15.6001 15.0723 15.797 14.9635 15.9883 14.8351C16.1729 14.7104 16.3464 14.567 16.5067 14.4067C16.667 14.2454 16.8114 14.072 16.9352 13.8882C17.0645 13.697 17.1723 13.5001 17.2567 13.3014C17.3467 13.0885 17.4161 12.8673 17.462 12.6385C17.5051 12.4239 17.5276 12.1923 17.5276 11.9429C17.5276 11.6935 17.5042 11.4601 17.462 11.2464C17.4161 11.0176 17.3467 10.7964 17.2567 10.5836C17.1723 10.3848 17.0645 10.188 16.9352 9.9967C16.8105 9.81201 16.667 9.63857 16.5067 9.47825C16.187 9.15857 15.812 8.90263 15.4014 8.72825C14.9795 8.55106 14.7808 8.0645 14.9589 7.64263C15.137 7.22076 15.6226 7.02295 16.0445 7.20012C16.666 7.46357 17.222 7.83951 17.6832 8.30167C17.9148 8.53323 18.1267 8.79012 18.3132 9.06761C18.4923 9.33386 18.6507 9.62542 18.7839 9.94042C18.9151 10.2517 19.0182 10.5798 19.0867 10.922C19.1551 11.2632 19.1917 11.6045 19.1917 11.9438C19.1917 12.2823 19.156 12.6245 19.0867 12.9657C19.0182 13.3079 18.9151 13.6351 18.7839 13.9463C18.6507 14.2613 18.4923 14.5538 18.3132 14.8201C18.1267 15.0976 17.9148 15.3545 17.6832 15.586C17.4517 15.8176 17.1948 16.0295 16.9173 16.2169C16.652 16.3932 16.3595 16.5507 16.0445 16.6848V16.6848Z" fill="white"/>
3+<path d="M17.5533 19.4897C17.1314 19.6669 16.6458 19.469 16.4677 19.0472C16.2905 18.6253 16.4883 18.1397 16.9102 17.9615C17.2908 17.8003 17.6592 17.5987 18.0117 17.3615C18.3586 17.1272 18.6849 16.8581 18.9839 16.559C19.283 16.26 19.5521 15.9346 19.7864 15.5868C20.0236 15.2343 20.2252 14.865 20.3864 14.4853C20.5542 14.0887 20.6836 13.6725 20.7708 13.2431C20.8533 12.8334 20.8964 12.3984 20.8964 11.9409C20.8964 11.4834 20.8524 11.0484 20.7708 10.6387C20.6846 10.2103 20.5543 9.79403 20.3864 9.39745C20.2252 9.01684 20.0236 8.64839 19.7864 8.2959C19.5521 7.94903 19.283 7.62277 18.9839 7.32371C18.6849 7.02464 18.3586 6.75558 18.0117 6.5212C17.6592 6.28401 17.2899 6.08245 16.9102 5.9212C16.4883 5.74307 16.2896 5.25652 16.4677 4.83558C16.6458 4.41371 17.1323 4.2159 17.5533 4.39307C18.0492 4.60307 18.5142 4.85432 18.9417 5.14307C19.3823 5.44026 19.7902 5.77681 20.1605 6.14713C20.5308 6.51745 20.8673 6.92526 21.1645 7.36588C21.4523 7.79336 21.7045 8.25839 21.9145 8.75432C22.1245 9.24932 22.2867 9.77152 22.3964 10.3143C22.5042 10.8515 22.5605 11.3953 22.5605 11.9418C22.5605 12.4874 22.5042 13.0321 22.3955 13.5693C22.2858 14.1121 22.1236 14.6343 21.9136 15.1293C21.7036 15.6252 21.4514 16.0902 21.1636 16.5168C20.8664 16.9574 20.5299 17.3652 20.1595 17.7355C19.7892 18.1049 19.3814 18.4415 18.9408 18.7396C18.5133 19.0274 18.0492 19.2796 17.5533 19.4896L17.5533 19.4897Z" fill="white"/>
4+<path d="M11.0694 4.12871C9.41287 5.37559 7.75526 6.62351 6.09878 7.87127L2.87198 7.87034C2.08543 7.87034 1.44043 8.51534 1.44043 9.30189V14.698C1.44043 15.4855 2.08543 16.1296 2.87198 16.1296H6.09782C7.75437 17.3765 9.41198 18.6244 11.0685 19.8722C11.8241 20.4403 12.6988 20.3494 12.6988 19.3631V4.6379C12.6997 3.65164 11.825 3.55977 11.0694 4.12883L11.0694 4.12871Z" fill="white"/>
5+</svg>