This commit is contained in:
2023-07-24 23:46:18 +03:00
commit 6051ed0b82
121 changed files with 14058 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
import React from "react";
export interface avatarProps {
imgUrl: string;
name?: string;
userName?: string;
description?: string;
}
export interface avatarLayoutProps {
list: list[];
}
export const Avatar = ({
imgUrl,
name,
description,
}: avatarProps): JSX.Element => {
return (
<div className="card m-4 border-2 border-secondary">
<div className="card__body">
<div className="avatar">
<img className="avatar__photo avatar__photo--sm" src={imgUrl} />
<div className="avatar__intro">
<div className="avatar__name">{name}</div>
<small className="avatar__subtitle">{description}</small>
</div>
</div>
</div>
</div>
);
};
export const AvatarLayout = ({ list }: avatarLayoutProps): JSX.Element => {
return (
<div className="container">
<div className="flex flex-wrap justify-center bg-primary">
{list.map(() => {
return <Avatar />;
})}
</div>
</div>
);
};

165
src/pages/credits/index.tsx Normal file
View File

@@ -0,0 +1,165 @@
import React from "react";
import Layout from "@theme/Layout";
import Link from "@docusaurus/Link";
import { AvatarLayout } from "./_components/Avatar";
const Credits = (): JSX.Element => {
const partnerLogos = [
{
name: "Vercel",
url: "/2.0/vercel-logotype-dark.svg",
},
{
name: "Cloudflare",
url: "/2.0/CF_logo_horizontal_blktype.svg",
},
{
name: "RAK Wireless",
url: "/2.0/RAK-blue-main.svg",
},
{
name: "Open Collective",
url: "/2.0/opencollectivelogo.svg",
},
{
name: "LILYGO",
url: "/2.0/LILYGO.png",
},
{
name: "Discord",
url: "/2.0/discord.svg",
},
];
return (
<Layout
title="Credits"
description="Meshtastic is made possible by the following people & organizations."
>
<main className="relative mt-20">
<div className="container mx-auto p-6 leading-normal space-y-4">
<h1>Credits</h1>
<p>
Meshtastic is community driven. Thousands of hours have been donated
by volunteers who want to develop this amazing project. Whether
you've submitted a pull request or triaged a bug in our
Discord/Forum. You've made Meshtastic possible. Thank you for your
contributions.
</p>
<p>
We would also like to recognize those who have donated financially
to the project. As Meshtastic has grown, we've aquired some ongoing
costs to keep the project running. Thank you for your generous
donations.
</p>
</div>
<div className="container mx-auto p-6 leading-normal space-y-4">
<h2>Fiscal Sponsors</h2>
<p>
We have partnered with both the{" "}
<a
className="underline"
href="https://opencollective.com"
target="_blank"
>
Open Collective
</a>{" "}
and the{" "}
<a
className="underline"
href="https://www.oscollective.org"
target="_blank"
>
Open Source Collective
</a>{" "}
to help us with a fiscal management framework and banking needs.
They help support over three thousand open source projects including
the PHP Foundation, F-Droid, Sonarr, LinuxServer and DarkReader. We
are in good hands and good company.
</p>
<p>
As with everything we do here, Open Collective provides a fully
transparent framework for our budget and expenses. You can see what
were bringing in, who is spending money and where that money is
going{" "}
<a
className="underline"
href="https://opencollective.com/meshtastic"
target="_blank"
>
here
</a>
.
</p>
<p>
In addition to our partnership with Open Collective and Open Source
Collective, we have also been approved into the{" "}
<a
className="underline"
href="https://github.com/sponsors"
target="_blank"
>
GitHub Sponsors
</a>{" "}
program where we can set fundraising goals with GitHub.
</p>
<p>
All donations made through GitHub will be deposited to our account
with the Open Source Collective and managed by the Open Collective.
This means we have a single place to monitor and maintain
transparency of our finances.
</p>
<p>If you are able, please contribute to this amazing project.</p>
<div className="indexCtasBody">
<Link
className={"button button--outline button--lg cta--button"}
to={"https://opencollective.com/meshtastic/donate"}
>
Sponsor Meshtastic
</Link>
</div>
<h3>
Open Collective Donations
{/*Open Collective Donations*/}
<AvatarLayout list={[]} />
</h3>
<h3>
GitHub Sponsor Donations
{/*GitHub Sponsor Donations*/}
<AvatarLayout list={[]} />
</h3>
</div>
<div className="container mx-auto p-6 leading-normal space-y-4">
<h2>Partnerships</h2>
<div className="mt-12 grid grid-cols-2 gap-0.5 md:grid-cols-3 lg:mt-0 lg:grid-cols-2">
{partnerLogos.map((logo) => (
<div
key={logo.name}
className="col-span-1 flex justify-center bg-gray-50 py-8 px-8"
>
<img className="max-h-12" src={logo.url} alt={logo.name} />
</div>
))}
</div>
</div>
<div className="container mx-auto p-6 leading-normal space-y-4">
<h2>Contributors</h2>
<p>
Literally thousands of hours have gone into creating, maintaining,
and improving Meshtastic. Without our contributors none of this
would be possible. Thank you for donating the time for each and
every commit, issue, and pull request.
</p>
{/*GitHub Organization Contributors*/}
<AvatarLayout list={[]} />
</div>
{/*Admin Bios*/}
<div className="container mx-auto p-6 leading-normal space-y-4">
<AvatarLayout list={[]} />
</div>
</main>
</Layout>
);
};
export default Credits;

View File

@@ -0,0 +1,146 @@
import React from "react";
export interface downloadCardProps {
client: string;
imgUrl: string;
url: string;
imgUrl2: string;
url2: string;
notes: string;
buttonText: string;
}
export const DownloadCard = ({
client,
imgUrl,
url,
imgUrl2,
url2,
notes,
buttonText,
}: downloadCardProps): JSX.Element => {
return (
<div className="card">
<div
className="card__header"
style={{ display: "flex", justifyContent: "space-between" }}
>
<h3>{client}</h3>
</div>
<div
className="card__body"
style={{ display: "flex", justifyContent: "center" }}
>
{buttonText ? (
<a href={url} className="button button--secondary button--block">
{buttonText}
</a>
) : (
<div>
<a href={url}>
<img alt="img1" style={{ height: "4rem" }} src={imgUrl} />
</a>
<a href={url2}>
<img alt="img2" style={{ height: "4rem" }} src={imgUrl2} />
</a>
</div>
)}
</div>
<div className="card__footer">{notes ? notes : null}</div>
</div>
);
};
export const PlaceholderCard = (): JSX.Element => {
return (
<div
className="card"
style={{
width: "100%",
animation: "pulse 2s infinite",
transform: "scale(1)",
display: "flex",
gap: "1rem",
padding: "1rem",
}}
>
<div
style={{
display: "flex",
justifyContent: "space-between",
marginBottom: "1rem",
}}
>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
height: "2rem",
width: "8rem",
}}
/>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
marginTop: "1rem",
height: "1rem",
width: "8rem",
}}
/>
</div>
<div
className="card__body"
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
height: "3rem",
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
/>
<a className="button disabled button--primary button--block">&nbsp;</a>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
width: "8rem",
height: "2rem",
}}
/>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
width: "11rem",
height: "1rem",
}}
/>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
width: "9rem",
height: "1rem",
}}
/>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
width: "13rem",
height: "1rem",
}}
/>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
width: "11rem",
height: "1rem",
}}
/>
</div>
);
};

View File

@@ -0,0 +1,151 @@
import React from "react";
import { DeviceFirmwareResource } from "../../../utils/apiTypes";
export interface releaseCardProps {
variant: string;
description: string;
release?: DeviceFirmwareResource[];
}
export const FirmwareCard = ({
variant,
description,
release,
}: releaseCardProps): JSX.Element => {
return (
<div className="card m-4 border-2 border-secondary">
<div
className="card__header"
style={{ display: "flex", justifyContent: "space-between" }}
>
<h3>{variant}</h3>
{release?.length && (
<a href={release[0].page_url}>{release[0].title}</a>
)}
</div>
<div className="card__body">
<p>{description}</p>
</div>
<div className="card__footer mt-auto">
<div className="margin-top--sm">
<details>
<summary>Older Versions</summary>
{release.slice(1, 6).map((release) => {
return (
<div key={release.id}>
<a href={release.zip_url}>{release.title}</a>
</div>
);
})}
</details>
</div>
{release?.length ? (
<>
<a
href={release[0].zip_url}
className="button button--secondary button--block margin-top--sm"
>
Download {variant}
</a>
</>
) : (
<button disabled className="button button--secondary button--block">
Loading...
</button>
)}
</div>
</div>
);
};
export const PlaceholderFirmwareCard = (): JSX.Element => {
return (
<div
className="card"
style={{
width: "100%",
animation: "pulse 2s infinite",
transform: "scale(1)",
display: "flex",
gap: "1rem",
padding: "1rem",
}}
>
<div
style={{
display: "flex",
justifyContent: "space-between",
marginBottom: "1rem",
}}
>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
height: "2rem",
width: "8rem",
}}
/>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
marginTop: "1rem",
height: "1rem",
width: "8rem",
}}
/>
</div>
<div
className="card__body"
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
height: "3rem",
}}
/>
<a className="button disabled button--primary button--block">&nbsp;</a>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
width: "8rem",
height: "2rem",
}}
/>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
width: "11rem",
height: "1rem",
}}
/>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
width: "9rem",
height: "1rem",
}}
/>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
width: "13rem",
height: "1rem",
}}
/>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
width: "11rem",
height: "1rem",
}}
/>
</div>
);
};

View File

@@ -0,0 +1,18 @@
import React from "react";
export const HeaderText = ({ type, text, link }): JSX.Element => {
const Header = type;
return (
<Header className="anchor anchorWithHideOnScrollNavbar_node_modules-@docusaurus-theme-classic-lib-next-theme-Heading-styles-module">
{text}
{link && (
<a
className="hash-link"
href={`#${link}`}
title="Direct link to heading"
/>
)}
</Header>
);
};

View File

@@ -0,0 +1,288 @@
import React from "react";
import { FaAndroid, FaApple } from "react-icons/fa";
import useBaseUrl from "@docusaurus/useBaseUrl";
import {
ArrowTopRightOnSquareIcon,
ComputerDesktopIcon,
GlobeAltIcon,
} from "@heroicons/react/24/solid";
import Layout from "@theme/Layout";
const Firmware = (): JSX.Element => {
return (
<Layout
title="Завантаження"
description="Клієнти для наших сервісів для різних платформ."
>
<div className="container mt-8 flex flex-col gap-3">
<div className="flex w-full overflow-hidden rounded-xl">
<div className="flex w-1/5 bg-gradient-to-r from-rose-500 to-primary">
<span className="m-auto h-20 text-4xl font-black">#IRC</span>
</div>
<div className="flex w-full columns-3 flex-col bg-primary lg:flex-row">
<div className="card m-4 border-2 border-secondary">
<div className="card__header">
<h3>Apple</h3>
</div>
<div className="card__body flex items-center">
<div className="m-auto">
<FaApple className="h-20 w-20" />
</div>
</div>
<div className="card__body">
Available on MacOS & iOS. Requires MacOS Ventura or iOS 16+.
</div>
<div className="card__footer mt-auto">
<a
target="_blank"
rel="noopener noreferrer"
href="#"
className="m-auto flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
>
App Store
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
</a>
</div>
</div>
<div className="card m-4 border-2 border-secondary">
<div className="card__header">
<h3>Android</h3>
</div>
<div className="card__body flex items-center">
<div className="m-auto">
<FaAndroid className="h-20 w-20" />
</div>
</div>
<div className="card__body">Sideloading also available.</div>
<div className="card__footer mt-auto">
<a
target="_blank"
rel="noopener noreferrer"
href="#"
className="m-auto flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
>
F-Droid
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
</a>
<a
target="_blank"
rel="noopener noreferrer"
href="#"
className="mt-4 flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
>
Play Store
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
</a>
</div>
</div>
<div className="card m-4 border-2 border-secondary">
<div className="card__header">
<h3>Web</h3>
</div>
<div className="card__body flex items-center">
<div className="m-auto">
<GlobeAltIcon className="h-20 w-20" />
</div>
</div>
<div className="card__body">
<a href="https://sr.ht/~emersion/gamja/">gamja</a> веб клієнт
для IRC.
</div>
<div className="card__footer mt-auto">
<a
target="_blank"
rel="noopener noreferrer"
href="https://irc.dead.guru"
className="m-auto flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
>
irc.dead.guru
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
</a>
</div>
</div>
</div>
</div>
{/* */}
<div className="flex w-full overflow-hidden rounded-xl">
<div className="flex w-1/5 bg-gradient-to-r from-green-500 to-primary">
<span className="m-auto h-20 text-4xl font-black">#MATRIX</span>
</div>
<div className="flex w-full columns-3 flex-col bg-primary lg:flex-row">
<div className="card m-4 border-2 border-secondary">
<div className="card__header">
<h3>Apple</h3>
</div>
<div className="card__body flex items-center">
<div className="m-auto">
<FaApple className="h-20 w-20" />
</div>
</div>
<div className="card__body">
Available on MacOS & iOS. Requires MacOS Ventura or iOS 16+.
</div>
<div className="card__footer mt-auto">
<a
target="_blank"
rel="noopener noreferrer"
href="#"
className="m-auto flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
>
App Store
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
</a>
</div>
</div>
<div className="card m-4 border-2 border-secondary">
<div className="card__header">
<h3>Android</h3>
</div>
<div className="card__body flex items-center">
<div className="m-auto">
<FaAndroid className="h-20 w-20" />
</div>
</div>
<div className="card__body">Sideloading also available.</div>
<div className="card__footer mt-auto">
<a
target="_blank"
rel="noopener noreferrer"
href="#"
className="m-auto flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
>
F-Droid
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
</a>
<a
target="_blank"
rel="noopener noreferrer"
href="#"
className="mt-4 flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
>
Play Store
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
</a>
</div>
</div>
<div className="card m-4 border-2 border-secondary">
<div className="card__header">
<h3>Web</h3>
</div>
<div className="card__body flex items-center">
<div className="m-auto">
<GlobeAltIcon className="h-20 w-20" />
</div>
</div>
<div className="card__body">
fluffychat веб клієнт для matrix.
</div>
<div className="card__footer mt-auto">
<a
target="_blank"
rel="noopener noreferrer"
href="https://xmpp.dead.guru:5281/conversejs"
className="m-auto flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
>
https://fluffychat.im/
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
</a>
</div>
</div>
</div>
</div>
{/* */}
<div className="flex w-full overflow-hidden rounded-xl">
<div className="flex w-1/5 bg-gradient-to-r from-yellow-500 to-primary">
<span className="m-auto h-20 text-4xl font-black">#XMPP</span>
</div>
<div className="flex w-full columns-3 flex-col bg-primary lg:flex-row">
<div className="card m-4 border-2 border-secondary">
<div className="card__header">
<h3>Apple</h3>
</div>
<div className="card__body flex items-center">
<div className="m-auto">
<FaApple className="h-20 w-20" />
</div>
</div>
<div className="card__body">
Available on MacOS & iOS. Requires MacOS Ventura or iOS 16+.
</div>
<div className="card__footer mt-auto">
<a
target="_blank"
rel="noopener noreferrer"
href="#"
className="m-auto flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
>
App Store
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
</a>
</div>
</div>
<div className="card m-4 border-2 border-secondary">
<div className="card__header">
<h3>Android</h3>
</div>
<div className="card__body flex items-center">
<div className="m-auto">
<FaAndroid className="h-20 w-20" />
</div>
</div>
<div className="card__body">Sideloading also available.</div>
<div className="card__footer mt-auto">
<a
target="_blank"
rel="noopener noreferrer"
href="#"
className="m-auto flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
>
F-Droid
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
</a>
<a
target="_blank"
rel="noopener noreferrer"
href="#"
className="mt-4 flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
>
Play Store
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
</a>
</div>
</div>
<div className="card m-4 border-2 border-secondary">
<div className="card__header">
<h3>Web</h3>
</div>
<div className="card__body flex items-center">
<div className="m-auto">
<GlobeAltIcon className="h-20 w-20" />
</div>
</div>
<div className="card__body">
<a href="https://conversejs.org/">Converse.js</a> веб клієнт для
XMPP.
</div>
<div className="card__footer mt-auto">
<a
target="_blank"
rel="noopener noreferrer"
href="https://xmpp.dead.guru:5281/conversejs"
className="m-auto flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
>
https://xmpp.dead.guru:5281/conversejs
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
</a>
</div>
</div>
</div>
</div>
</div>
</Layout>
);
};
export default Firmware;

7
src/pages/e/index.md Normal file
View File

@@ -0,0 +1,7 @@
---
id: e
title: Dead Channel Settings
sidebar_label: e
---
## TBD

300
src/pages/index.tsx Normal file
View File

@@ -0,0 +1,300 @@
import "react-responsive-carousel/lib/styles/carousel.min.css"; // requires a loader
import React from "react";
import { Carousel } from "react-responsive-carousel";
import Head from "@docusaurus/Head";
import Link from "@docusaurus/Link";
import useBaseUrl from "@docusaurus/useBaseUrl";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import Layout from "@theme/Layout";
import { SocialCard, SocialCardProps } from "../components/homepage/SocialCard";
const features = [
{
title: "Основа мережі — Спілкування",
imageUrl: "img/homepage/messages.svg",
description: (
<>
Мережу в першу чергу, створюють люди, а не технології. Спілкування
дозволяє нам зберігати зв'язок з друзями та родиною, а також будувати
новий, цікавий і вільний світ.
</>
),
},
{
title: "Захист",
imageUrl: "img/homepage/encryption.svg",
description: (
<>
Ми намагаємося захистити вашу приватність, використовуючи шифрування де
це можливо та необхідно. Ви можете використовувати власні ключі
шифрування, щоб забезпечити максимальний рівень захисту. Ми також
відкриті для аудиту та вдосконалення наших методів шифрування та
захисту.
</>
),
},
{
title: "Незалежність",
imageUrl: "img/homepage/platforms.svg",
description: (
<>
Свобода спілкування не повинна бути обмежена. Ми намагаємося
підтримувати якомога більше платформ, щоб ви могли спілкуватися з
друзями та родиною, незалежно від того, який пристрій ви використовуєте.
</>
),
},
{
title: "Open Source",
imageUrl: "img/homepage/opensource.svg",
description: (
<>
Мережа побудована на основі відкритих проектів, що дозволяє вам приймати
участь в розвитку та вдосконаленні мережі.
</>
),
},
];
const SocialCards: SocialCardProps[] = [
{
color: "bg-[#3875EA]",
link: "https://irc.dead.guru",
children: (
<img alt="irc" className="m-auto h-10" src="/img/services/irc.svg" />
),
},
{
color: "bg-[#FFFFFF]",
link: "https://matrix.to/#/#adhd_lab:hypogea.org",
children: (
<img
alt="matrix"
className="m-auto h-14"
src="/img/services/Matrix_logo.svg"
/>
),
},
{
color: "bg-[#95c0d9]",
link: "https://xmpp.dead.guru:5281/",
children: (
<img
alt="xmpp"
className="m-auto h-14"
src="/img/services/XMPP_logo.svg"
/>
),
},
{
color: "bg-[#f05539]",
link: "https://git.dead.guru/",
children: (
<img alt="git" className="m-auto h-14" src="/img/services/git_logo.svg" />
),
},
{
color: "bg-[#9e9b9a]",
link: "https://mine.dead.guru",
children: (
<img
alt="minecraft"
className="m-auto h-16"
src="/img/services/minecraft_logo.svg"
/>
),
},
{
color: "bg-[#39322b]",
link: "https://bookhouse.hypogea.org/",
children: (
<img
alt="Calibre"
className="m-auto h-12"
src="/img/services/Calibre_logo.png"
/>
),
},
{
color: "bg-[#6366f1]",
link: "https://pi.dead.guru",
children: (
<img
alt="reddit"
className="m-auto h-20"
src="/img/services/Pixelfed_logo.svg"
/>
),
},
];
function Home() {
const context = useDocusaurusContext();
const { siteConfig } = context;
return (
<Layout>
<Head>
<meta property="og:title" content="Dead Network" />
<meta
property="og:image"
content="https://meshtastic.org/design/web/social-preview-1200x630.png"
/>
<meta
property="og:description"
content="Надаємо різноманітні сервіси-платформи, які дають змогу окремим особам і спільнотам спілкуватися, ділитися та розвиватися разом."
/>
<meta property="og:url" content="https://dead.guru/" />
<meta name="twitter:card" content="summary_large_image" />
</Head>
<header style={{ textAlign: "center" }} className="hero hero--primary">
<div className="container">
<h1 className="hero__title">
<img
style={{ paddingTop: "2rem", paddingBottom: "2rem" }}
alt="Dead Logo"
className="header__logo"
src={useBaseUrl("img/dead-banner.png")}
/>
</h1>
<p className="hero__subtitle">{siteConfig.tagline}</p>
<div className="indexCtas">
<Link className="button button--lg" to="/docs/introduction">
Дізнатися більше
</Link>
<Link className="button button--lg" to="/docs/getting-started">
Розпочати
</Link>
</div>
</div>
</header>
<main className="flex flex-col gap-4">
<Carousel autoPlay infiniteLoop showStatus={false} showThumbs={false}>
{features.map((feature) => (
<div key={feature.title} className="flex p-12">
<div className="w-1/2">
<img
className="my-auto h-40"
src={feature.imageUrl}
alt={feature.title}
/>
</div>
<div className="my-auto w-1/2">
<h3 className="text-xl font-medium">{feature.title}</h3>
<p>{feature.description}</p>
</div>
</div>
))}
</Carousel>
<div className="bg-primaryDark mx-auto flex w-full lg:w-auto flex-col gap-4 p-4 shadow-inner">
<h3 className="text-xl font-bold">Приєднуйтесь до нас.</h3>
<div className="flex w-full overflow-x-auto">
{SocialCards.map((card) => (
<SocialCard key={card.link} color={card.color} link={card.link}>
{card.children}
</SocialCard>
))}
</div>
</div>
<div className="container mx-auto flex w-auto flex-col">
<h2 className="mb-2 text-xl font-medium">
Доєднайтеся до "Мертвої" мережі в 3 кроки.
</h2>
<ul
className="mx-auto"
style={{
position: "relative",
display: "grid",
gap: "1.5rem",
gridTemplateColumns: "repeat(auto-fill, minmax(280px, 1fr))",
paddingLeft: "0",
}}
>
<div className="card">
<div
className="card__header"
style={{ display: "flex", justifyContent: "space-between" }}
>
<h3>1. Оберіть платформу</h3>
</div>
<div
className="card__body"
style={{ display: "flex", justifyContent: "center" }}
>
<p>
Для старту почніть з вибору платформи:
<ul>
<li>IRC</li>
<li>XMPP</li>
<li>Matrix</li>
<li>
Або взагалі проігноруйте сервіси повідомлень і почніть з{" "}
<a href="">
<strong>ігрових серверів</strong>
</a>
!
</li>
</ul>
</p>
</div>
</div>
<div className="card">
<div
className="card__header"
style={{ display: "flex", justifyContent: "space-between" }}
>
<h3>2. Завантажте і зареєструйтесь!</h3>
</div>
<div
className="card__body"
style={{ display: "flex", justifyContent: "center" }}
>
<p>
<a href="/downloads">
<strong>Завантажте</strong>
</a>{" "}
додаток для обраної платформи, перегляньте наш{" "}
<a href="">
<strong>мануал по налаштуванню</strong>
</a>{" "}
і зареєструйтесь в сервісі мережі.
</p>
</div>
</div>
<div className="card">
<div
className="card__header"
style={{ display: "flex", justifyContent: "space-between" }}
>
<h3>3. Приєднайтесь до чату</h3>
</div>
<div
className="card__body"
style={{ display: "flex", justifyContent: "center" }}
>
<p>
В залежності від ваших інтересів і обраної платформи, ви
можете приєднатися до:
<ul>
<li>Загальний чат</li>
<li>ADHD Lab</li>
<li>Ігрові чати</li>
<li>HAM загальний чат</li>
</ul>
</p>
</div>
</div>
</ul>
</div>
<br />
</main>
</Layout>
);
}
export default Home;

View File

@@ -0,0 +1,110 @@
import React from "react";
import { Showcase } from "../../../utils/apiTypes";
import { mapUrl } from "../../../utils/map";
import { CardTags } from "./CardTags";
export interface CardProps {
network: Showcase;
}
export const Card = React.memo(({ network }: CardProps) => (
<div className="card">
<div className="card__image">
<div style={{ height: "140px" }}>
<img src={mapUrl(network.nodes ?? [])} alt={network.title} />
</div>
</div>
<div className="card__body">
<h4>{network.title}</h4>
<small>{network.summary}</small>
</div>
<div className="card__footer">
<a
href={`?id=${network.id}`}
className="button button--primary button--block"
style={{ marginBottom: "0.5rem" }}
>
Read more
</a>
<CardTags tags={network.tags} />
</div>
</div>
));
export const PlaceholderCard = (): JSX.Element => (
<div
className="card"
style={{
animation: "pulse 2s infinite",
transform: "scale(1)",
}}
>
<div className="card__image">
<div
style={{
height: "140px",
}}
/>
</div>
<div className="card__body">
<div
style={{
width: "30%",
height: "2rem",
borderRadius: "0.4rem",
backgroundColor: "gray",
marginBottom: "1rem",
}}
/>
<div
style={{
width: "100%",
height: "1rem",
borderRadius: "0.4rem",
backgroundColor: "gray",
marginBottom: "0.5rem",
}}
/>
<div
style={{
width: "100%",
height: "1rem",
borderRadius: "0.4rem",
backgroundColor: "gray",
}}
/>
</div>
<div className="card__footer">
<button
className="button disabled button--primary button--block"
style={{ marginBottom: "0.5rem" }}
>
&nbsp;
</button>
<div
style={{
display: "flex",
gap: "0.5rem",
}}
>
<div
style={{
width: "4rem",
height: "1.5rem",
borderRadius: "0.4rem",
backgroundColor: "gray",
}}
/>
<div
style={{
width: "4rem",
height: "1.5rem",
borderRadius: "0.4rem",
backgroundColor: "gray",
}}
/>
</div>
</div>
</div>
);

View File

@@ -0,0 +1,29 @@
import React from "react";
import { ShowcaseTag } from "../../../utils/apiTypes";
export interface CardTagsProps {
tags: ShowcaseTag[];
}
export const CardTags = ({ tags }: CardTagsProps) => {
return (
<div>
{tags.map(({ color, label }) => {
return (
<span
className="badge"
key={label}
style={{
backgroundColor: color,
marginRight: "0.3rem",
userSelect: "none",
}}
>
{label}
</span>
);
})}
</div>
);
};

View File

@@ -0,0 +1,90 @@
import React from "react";
import { FiHeart } from "react-icons/fi";
import useSWR from "swr";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import { fetcher } from "../../../utils/swr";
import { ShowcaseTag } from "../../../utils/apiTypes";
// import { TagList, Tags } from '../../../utils/showcase';
import { PlaceholderTagSelect, TagSelect } from "./TagSelect";
export const Filters = (): JSX.Element => {
const { siteConfig } = useDocusaurusContext();
const { data, error } = useSWR<ShowcaseTag[]>(
`${siteConfig.customFields.API_URL}/showcase/tags`,
fetcher,
);
return (
<section className="margin-top--l margin-bottom--lg container">
{data && !error ? (
<ul
style={{
padding: "0",
display: "flex",
alignItems: "center",
flexWrap: "wrap",
}}
>
{data.map((tag) => {
const { label, color } = tag;
const id = `showcase_checkbox_id_${tag};`;
return (
<div
key={tag.id}
style={{
boxSizing: "border-box",
position: "relative",
display: "inline-flex",
alignItems: "center",
height: "2rem",
marginTop: "0.5rem",
marginRight: "0.5rem",
fontSize: "0.875rem",
lineHeight: "1.25rem",
verticalAlign: "middle",
userSelect: "none",
}}
>
<TagSelect
tag={tag}
id={id}
label={label}
icon={
tag.label === "Favorite" ? (
<span
style={{
display: "flex",
marginLeft: "0.5rem",
color: "rgb(190 24 93)",
}}
>
<FiHeart />
</span>
) : (
<span
style={{
backgroundColor: color,
width: 10,
height: 10,
borderRadius: "50%",
marginLeft: 8,
}}
/>
)
}
/>
</div>
);
})}
</ul>
) : (
<PlaceholderTagSelect />
)}
</section>
);
};

View File

@@ -0,0 +1,318 @@
import React from "react";
import useSWR from "swr";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import { Showcase } from "../../../utils/apiTypes";
import { fetcher } from "../../../utils/swr";
interface NetworkProps {
id: string;
}
export const Network = ({ id }: NetworkProps): JSX.Element => {
const { siteConfig } = useDocusaurusContext();
const { data, error } = useSWR<Showcase>(
`${siteConfig.customFields.API_URL}/showcase/${id}`,
fetcher,
);
const githubData = useSWR(
`https://api.github.com/users/${data?.author?.githubUsername}`,
fetcher,
).data;
return (
<div>
{data && !error ? (
<div className="container">
<h1>{data.title}</h1>
<p>{data.summary}</p>
{githubData && (
<div className="avatar">
<img
src={githubData.avatar_url}
alt={githubData.name}
className="avatar__photo"
/>
<div className="avatar__intro">
<div className="avatar__name">{githubData.name}</div>
<div className="avatar__subtitle">{githubData.bio}</div>
</div>
</div>
)}
<div className="markdown">{data.body}</div>
<div
className="card"
style={{
marginLeft: "auto",
marginRight: "auto",
maxWidth: "900px",
}}
>
<div
className="card__header"
style={{
margin: "8px",
}}
>
<h2>Bill of Materials</h2>
</div>
<div className="card__body">
{data.materials?.map((material) => (
<div
key={material.id}
style={{
borderTop: "2px solid gray",
display: "flex",
}}
>
<div
style={{
width: "4rem",
display: "flex",
}}
>
<img
src={material.image}
height="auto"
width="100%"
style={{
margin: "auto",
padding: "4px",
display: "block",
maxWidth: "60px",
maxHeight: "60px",
width: "auto",
height: "auto",
}}
/>
</div>
<div className="avatar__intro">
<div className="avatar__name">{material.name}</div>
<small className="avatar__subtitle">
{material.details}
</small>
</div>
<a
target="_blank"
rel="noreferrer"
href={material.url}
className="button button--outline button--secondary"
style={{
marginTop: "auto",
marginBottom: "auto",
}}
>
View
</a>
</div>
))}
</div>
</div>
</div>
) : (
<div>
{error && <div>{JSON.stringify(error)}</div>}
{!data && <PlaceholderNetwork />}
</div>
)}
</div>
);
};
export const PlaceholderNetwork = (): JSX.Element => {
return (
<div
className="container"
style={{
display: "flex",
flexDirection: window.innerWidth > 768 ? "row" : "column",
gap: "2rem",
}}
>
<div
style={{
width: window.innerWidth > 768 ? "60%" : "100%",
}}
>
<div
className="card"
style={{
width: "100%",
animation: "pulse 2s infinite",
transform: "scale(1)",
display: "flex",
flexDirection: "column",
gap: "2rem",
padding: "2rem",
}}
>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
height: "4rem",
}}
/>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
height: "12rem",
}}
/>
<div style={{ display: "flex", gap: "1rem" }}>
<div
style={{
borderRadius: "999px",
backgroundColor: "gray",
height: "4rem",
width: "4rem",
minWidth: "4rem",
}}
/>
<div
style={{
display: "flex",
flexDirection: "column",
gap: "1rem",
width: "100%",
}}
>
<div
style={{
width: "100%",
borderRadius: "0.4rem",
backgroundColor: "gray",
height: "1rem",
}}
/>
<div
style={{
width: "100%",
borderRadius: "0.4rem",
backgroundColor: "gray",
height: "2rem",
}}
/>
</div>
</div>
</div>
</div>
<div
style={{
width: window.innerWidth > 768 ? "40%" : "100%",
}}
>
<div
className="card"
style={{
width: "100%",
animation: "pulse 2s infinite",
transform: "scale(1)",
display: "flex",
flexDirection: "column",
gap: "2rem",
padding: "2rem",
}}
>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
height: "12rem",
}}
/>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
height: "2rem",
}}
/>
<div style={{ display: "flex", gap: "0.5rem" }}>
<div
style={{
width: "7rem",
height: "1.8rem",
borderRadius: "0.4rem",
backgroundColor: "gray",
}}
/>
<div
style={{
width: "7rem",
height: "1.8rem",
borderRadius: "0.4rem",
backgroundColor: "gray",
}}
/>
<div
style={{
width: "7rem",
height: "1.8rem",
borderRadius: "0.4rem",
backgroundColor: "gray",
}}
/>
</div>
<div
style={{ display: "flex", flexDirection: "column", gap: "1rem" }}
>
<div style={{ display: "flex", gap: "1rem" }}>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
height: "2.5rem",
width: "20%",
}}
/>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
height: "2.5rem",
width: "60%",
}}
/>
<a
className="button disabled button--primary button--block"
style={{ width: "20%" }}
>
&nbsp;
</a>
</div>
<div style={{ display: "flex", gap: "1rem" }}>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
height: "2.5rem",
width: "20%",
}}
/>
<div
style={{
borderRadius: "0.4rem",
backgroundColor: "gray",
height: "2.5rem",
width: "60%",
}}
/>
<a
className="button disabled button--primary button--block"
style={{ width: "20%" }}
>
&nbsp;
</a>
</div>
</div>
</div>
</div>
</div>
);
};

View File

@@ -0,0 +1,67 @@
import React from "react";
import { Showcase } from "../../../utils/apiTypes";
import { Card, PlaceholderCard } from "./Card";
interface NetworkSectionProps {
title: string;
icon?: JSX.Element;
iconColor?: string;
networks?: Showcase[];
}
export const NetworkSection = ({
title,
icon,
iconColor,
networks,
}: NetworkSectionProps): JSX.Element => {
return (
<div className="margin-top--lg container">
<div
className="margin-bottom--sm"
style={{
display: "flex",
alignItems: "center",
}}
>
<h2>{title}</h2>
{icon && (
<span
style={{
marginBottom: "0.5rem",
marginLeft: "0.5rem",
fontSize: "1.25rem",
lineHeight: "1.75rem",
color: iconColor,
}}
>
{icon}
</span>
)}
</div>
<ul
style={{
position: "relative",
display: "grid",
gap: "1.5rem",
gridTemplateColumns: "repeat(auto-fill, minmax(280px, 1fr))",
paddingLeft: "0",
}}
>
{networks ? (
<>
{networks.map((network) => (
<Card key={network.title} network={network} />
))}
{networks.length === 0 && <h2>No result</h2>}
</>
) : (
<div>
<PlaceholderCard />
</div>
)}
</ul>
</div>
);
};

View File

@@ -0,0 +1,52 @@
import React from "react";
import { FiHeart, FiSearch } from "react-icons/fi";
import useSWR from "swr";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import { useSelectedTags } from "../../../hooks/useSelectedTags";
import { useFilteredNetworks } from "../../../hooks/useFilteredNetworks";
import { Showcase } from "../../../utils/apiTypes";
import { fetcher } from "../../../utils/swr";
import { NetworkSection } from "./NetworkSection";
export const Networks = (): JSX.Element => {
const { siteConfig } = useDocusaurusContext();
const { data, error } = useSWR<Showcase[]>(
`${siteConfig.customFields.API_URL}/showcase`,
fetcher,
);
const selectedTags = useSelectedTags();
const filteredNetworks = useFilteredNetworks(data ?? []);
return (
<section className="margin-top--lg margin-bottom--xl">
{!error ? (
selectedTags.length === 0 ? (
<>
<NetworkSection
title="Our favorites"
icon={<FiHeart />}
iconColor="rgb(190 24 93)"
networks={data?.filter((network) =>
network.tags.find((tag) => tag.label === "Favorite"),
)}
/>
<NetworkSection title="All networks" networks={data} />
</>
) : (
<NetworkSection
title="Results"
icon={<FiSearch />}
networks={filteredNetworks}
/>
)
) : (
<div>{JSON.stringify(error)}</div>
)}
</section>
);
};

View File

@@ -0,0 +1,111 @@
import "url-search-params-polyfill";
import React from "react";
import { useHistory, useLocation } from "@docusaurus/router";
import { ShowcaseTag } from "../../../utils/apiTypes";
import { toggleListItem } from "../../../utils/showcase";
interface Props extends React.ComponentProps<"input"> {
icon: React.ReactElement<React.ComponentProps<"svg">>;
label: React.ReactNode;
tag: ShowcaseTag;
}
export function readSearchTags(search: string): string[] {
return new URLSearchParams(search).getAll("tags");
}
function replaceSearchTags(search: string, newTags: string[]) {
const searchParams = new URLSearchParams(search);
searchParams.delete("tags");
newTags.forEach((tag) => searchParams.append("tags", tag));
return searchParams.toString();
}
export const TagSelect = React.forwardRef<HTMLLabelElement, Props>(
({ icon, label, tag }) => {
const location = useLocation();
const history = useHistory();
const [selected, setSelected] = React.useState(false);
React.useEffect(() => {
const tags = readSearchTags(location.search);
setSelected(tags.includes(tag.label));
}, [tag, location]);
const toggleTag = React.useCallback(() => {
const tags = readSearchTags(location.search);
const newTags = toggleListItem(tags, tag.label);
const newSearch = replaceSearchTags(location.search, newTags);
history.push({ ...location, search: newSearch });
}, [tag, location, history]);
return (
<button
style={{
display: "flex",
alignItems: "center",
}}
className={`button button--sm button--outline button--secondary ${
selected ? "button--active" : ""
}`}
onClick={() => {
toggleTag();
}}
>
{label}
{icon}
</button>
);
},
);
export const PlaceholderTagSelect = (): JSX.Element => (
<div
style={{
boxSizing: "border-box",
position: "relative",
display: "inline-flex",
alignItems: "center",
height: "2rem",
marginTop: "0.5rem",
marginRight: "0.5rem",
fontSize: "0.875rem",
lineHeight: "1.25rem",
verticalAlign: "middle",
userSelect: "none",
}}
>
<div
style={{
width: "7rem",
height: "1.8rem",
borderRadius: "0.4rem",
backgroundColor: "gray",
animation: "pulse 2s infinite",
transform: "scale(1)",
}}
/>
<div
style={{
width: "7rem",
height: "1.8rem",
borderRadius: "0.4rem",
backgroundColor: "gray",
animation: "pulse 2s infinite",
transform: "scale(1)",
marginLeft: 8,
}}
/>
<div
style={{
width: "7rem",
height: "1.8rem",
borderRadius: "0.4rem",
backgroundColor: "gray",
animation: "pulse 2s infinite",
transform: "scale(1)",
marginLeft: 8,
}}
/>
</div>
);

View File

@@ -0,0 +1,35 @@
import "url-search-params-polyfill";
import React from "react";
import { useLocation } from "@docusaurus/router";
import Layout from "@theme/Layout";
import { Filters } from "./_components/Filters";
import { Network } from "./_components/Network";
import { Networks } from "./_components/Networks";
const Showcase = (): JSX.Element => {
const location = useLocation();
const id = new URLSearchParams(location.search).get("id");
return (
<Layout
title="Showcase"
description="Portfolio of projects from the Dead community"
>
<main className="margin-vert--lg">
{id ? (
<Network id={id} />
) : (
<>
<Filters />
<Networks />
</>
)}
</main>
</Layout>
);
};
export default Showcase;

151
src/pages/tools/OEM.tsx Normal file
View File

@@ -0,0 +1,151 @@
import React, { useEffect, useState } from "react";
import { fromByteArray, toByteArray } from "base64-js";
import { Protobuf } from "@meshtastic/meshtasticjs";
import Layout from "@theme/Layout";
const OEM = (): JSX.Element => {
const [oemAesKey, setOemAesKey] = useState<Uint8Array>(new Uint8Array());
const [oemFont, setOemFont] = useState<Protobuf.ScreenFonts>(
Protobuf.ScreenFonts.FONT_MEDIUM,
);
const [oemIconBits, setOemIconBits] = useState<Uint8Array>(new Uint8Array());
const [oemIconHeight, setOemIconHeight] = useState<number>(0);
const [oemIconWidth, setOemIconWidth] = useState<number>(0);
const [oemText, setOemText] = useState<string>("");
const [oemBytes, setOemBytes] = useState<Uint8Array>(new Uint8Array());
useEffect(() => {
setOemBytes(
Protobuf.OEMStore.toBinary({
oemAesKey,
oemFont,
oemIconBits,
oemIconHeight,
oemIconWidth,
oemText,
}),
);
}, [oemAesKey, oemFont, oemIconBits, oemIconHeight, oemIconWidth, oemText]);
const enumOptions = Protobuf.ScreenFonts
? Object.entries(Protobuf.ScreenFonts).filter(
(value) => typeof value[1] === "number",
)
: [];
const readFile = (file: File) => {
return new Promise((resolve: (value: string) => void, reject) => {
const reader = new FileReader();
reader.onload = (res) => {
resolve(res.target.result as string);
};
reader.onerror = (err) => reject(err);
reader.readAsText(file);
});
};
return (
<Layout title="OEM Generator" description="OEM Bin Generator">
<div className="container mt-8 flex flex-col gap-3">
<span>AES Key</span>
<div className="flex gap-2">
<button
onClick={() => {
const key = new Uint8Array(128 / 8);
setOemAesKey(crypto.getRandomValues(key));
}}
className="cursor-pointer rounded-md bg-tertiary p-2 hover:brightness-90"
>
Generate 128bit
</button>
<button
onClick={() => {
const key = new Uint8Array(256 / 8);
setOemAesKey(crypto.getRandomValues(key));
}}
className="mr-auto cursor-pointer rounded-md bg-tertiary p-2 hover:brightness-90"
>
Generate 256bit
</button>
</div>
<input
type="text"
name="oemAesKey"
value={fromByteArray(oemAesKey)}
onChange={(e) => {
setOemAesKey(toByteArray(e.target.value));
}}
/>
<span>Font</span>
<select
onChange={(e) => {
setOemFont(parseInt(e.target.value));
}}
>
{enumOptions.map(([name, value]) => (
<option key={name} value={value}>
{name}
</option>
))}
</select>
<span>Logo XBM</span>
<input
type="file"
name="file"
onChange={(e) => {
readFile(e.target.files[0]).then((data) => {
setOemIconBits(
new Uint8Array(
data.split(",").map((s) => parseInt(s.trim(), 16)),
),
);
});
}}
/>
<span>Logo Height</span>
<input
type="number"
name="oemIconHeight"
onChange={(e) => {
setOemIconHeight(parseInt(e.target.value));
}}
/>
<span>Logo Width</span>
<input
type="number"
name="oemIconWidth"
onChange={(e) => {
setOemIconWidth(parseInt(e.target.value));
}}
/>
<span>Boot Text</span>
<input
type="text"
name="oemText"
onChange={(e) => {
setOemText(e.target.value);
}}
/>
<a
className="cursor-pointer rounded-md bg-tertiary p-2 hover:brightness-90"
download="OEM.bin"
onClick={() => {
const blob = new Blob([oemBytes], {
type: "application/octet-stream",
});
window.open(URL.createObjectURL(blob));
}}
>
Download
</a>
{oemBytes.toString()}
</div>
</Layout>
);
};
export default OEM;