Initial
This commit is contained in:
44
src/pages/credits/_components/Avatar.tsx
Normal file
44
src/pages/credits/_components/Avatar.tsx
Normal 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
165
src/pages/credits/index.tsx
Normal 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
|
||||
we’re 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;
|
||||
146
src/pages/downloads/_components/DownloadCard.tsx
Normal file
146
src/pages/downloads/_components/DownloadCard.tsx
Normal 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"> </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>
|
||||
);
|
||||
};
|
||||
151
src/pages/downloads/_components/FirmwareCard.tsx
Normal file
151
src/pages/downloads/_components/FirmwareCard.tsx
Normal 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"> </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>
|
||||
);
|
||||
};
|
||||
18
src/pages/downloads/_components/HeaderText.tsx
Normal file
18
src/pages/downloads/_components/HeaderText.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
288
src/pages/downloads/index.tsx
Normal file
288
src/pages/downloads/index.tsx
Normal 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
7
src/pages/e/index.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
id: e
|
||||
title: Dead Channel Settings
|
||||
sidebar_label: e
|
||||
---
|
||||
|
||||
## TBD
|
||||
300
src/pages/index.tsx
Normal file
300
src/pages/index.tsx
Normal 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;
|
||||
110
src/pages/showcase/_components/Card.tsx
Normal file
110
src/pages/showcase/_components/Card.tsx
Normal 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" }}
|
||||
>
|
||||
|
||||
</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>
|
||||
);
|
||||
29
src/pages/showcase/_components/CardTags.tsx
Normal file
29
src/pages/showcase/_components/CardTags.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
90
src/pages/showcase/_components/Filters.tsx
Normal file
90
src/pages/showcase/_components/Filters.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
318
src/pages/showcase/_components/Network.tsx
Normal file
318
src/pages/showcase/_components/Network.tsx
Normal 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%" }}
|
||||
>
|
||||
|
||||
</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%" }}
|
||||
>
|
||||
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
67
src/pages/showcase/_components/NetworkSection.tsx
Normal file
67
src/pages/showcase/_components/NetworkSection.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
52
src/pages/showcase/_components/Networks.tsx
Normal file
52
src/pages/showcase/_components/Networks.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
111
src/pages/showcase/_components/TagSelect.tsx
Normal file
111
src/pages/showcase/_components/TagSelect.tsx
Normal 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>
|
||||
);
|
||||
35
src/pages/showcase/index.tsx
Normal file
35
src/pages/showcase/index.tsx
Normal 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
151
src/pages/tools/OEM.tsx
Normal 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;
|
||||
Reference in New Issue
Block a user