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,27 @@
import React from "react";
export const BatteryCalculator = (): JSX.Element => {
return (
<div className="card">
<div className="card__header">
<h3>Battery Calculator</h3>
</div>
<div className="card__body" style={{ display: "flex", gap: "2rem" }}>
<div>
<input placeholder="Search" />
<input placeholder="Search" />
<input placeholder="Search" />
<input placeholder="Search" />
</div>
</div>
<div className="card__footer">
<button
type="button"
className="button button--secondary button--block"
>
See All
</button>
</div>
</div>
);
};

16
src/components/Button.tsx Normal file
View File

@@ -0,0 +1,16 @@
import React from "react";
import { HTMLMotionProps, motion } from "framer-motion";
export const Button = ({ children, ...props }: HTMLMotionProps<"div">) => {
return (
<motion.div
{...props}
whileHover={{ scale: 1.1, backgroundColor: "var(--tertiary)" }}
whileTap={{ scale: 1.0 }}
className="m-auto flex cursor-pointer rounded-full bg-secondary p-3 shadow-md"
>
<div className="m-auto">{children}</div>
</motion.div>
);
};

View File

@@ -0,0 +1,13 @@
import React from "react";
export interface ColorModeProps {
children: React.ReactNode;
}
export const Dark = ({ children }: ColorModeProps): JSX.Element => {
return <div className="hideLight">{children}</div>;
};
export const Light = ({ children }: ColorModeProps): JSX.Element => {
return <div className="hideDark">{children}</div>;
};

View File

@@ -0,0 +1,274 @@
// import React from 'react';
// import data from '/docs/hardware/supported/devices.json'
// function checkVersionOverrides(selectedDevice, version, value) {
// var versionOverride = selectedDevice.versionOverrides[version]
// var device = selectedDevice
// var objectSegment = value.split('.')
// while (objectSegment.length > 1) {
// console.log(objectSegment)
// let test = objectSegment.shift()
// console.log('test', test, 'og objectSegment', objectSegment)
// versionOverride = versionOverride[test]
// device = device[test]
// }
// if (versionOverride) {
// return versionOverride
// } else return device
// // if (selectedDevice.versionOverrides[version][value]) {
// // return selectedDevice.versionOverrides[version][value]
// // } else {
// // console.log("no", selectedDevice, value, selectedDevice[value])
// // return selectedDevice[value]
// // }
// }
// export const MeshtasticFeatures = ({device, version}): JSX.Element => {
// const selectedDevice = data[device]
// return (
// <table>
// <thead>
// <th style={{align: "center"}}>
// Meshtastic Feature
// </th>
// <th style={{align: "center"}}>
// Device Support
// </th>
// </thead>
// <tbody>
// <tr>
// <td style={{align: "center"}}>
// Support Status
// </td>
// <td style={{align: "center"}}>
// {checkVersionOverrides(selectedDevice, version, 'supportStatus')}
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// Bluetooth
// </td>
// <td style={{align: "center"}}>
// {checkVersionOverrides(selectedDevice, version, "features.bluetoothCapable")}
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// Module - Canned Message
// </td>
// <td style={{align: "center"}}>
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// Module - External Notification
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// Module - Range Test
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// Module - Rotary Encoder
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// Module - Store and Forward
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// Module - Telemetry (aka Environmental Measurement)
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// Router - Always Powered
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// Router - Solar Powered
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// WiFi
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// </tbody>
// </table>
// );
// };
// export const HardwareSpecifications = ({device, version}): JSX.Element => {
// const selectedDevice = data[device]
// return (
// <table>
// <thead>
// <th style={{align: "center"}}>
// Specification
// </th>
// <th style={{align: "center"}}>
// Value
// </th>
// </thead>
// <tbody>
// <tr>
// <td style={{align: "center"}}>
// Bluetooth
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// Bluetooth Antenna
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// Chipset
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// Driver
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// GPS
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// Flash
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// Frequency - 433MHz
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// Frequency - 868MHz
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// Frequency - 915MHz
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// Frequency - 923MHz
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// LoRa Transceiver
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// PSRAM
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// RAM
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// WiFi
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// <tr>
// <td style={{align: "center"}}>
// WiFi Antenna
// </td>
// <td style={{align: "center"}}>
// VALUE
// </td>
// </tr>
// </tbody>
// </table>
// );
// };

49
src/components/Modal.tsx Normal file
View File

@@ -0,0 +1,49 @@
import React from "react";
import { AnimatePresence, motion } from "framer-motion";
import { Dialog } from "@headlessui/react";
export interface ModalProps {
open: boolean;
onClose: () => void;
children: React.ReactNode;
}
export const Modal = ({ open, onClose, children }: ModalProps): JSX.Element => {
return (
<AnimatePresence initial={false} exitBeforeEnter={true}>
<Dialog
as="div"
className="fixed inset-0 z-10 overflow-y-auto"
open={open}
onClose={onClose}
>
<div className="min-h-screen px-0.5 text-center md:px-4">
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<Dialog.Overlay className="fixed inset-0 backdrop-blur-md" />
</motion.div>
<span
className="inline-block h-screen align-middle"
aria-hidden="true"
>
&#8203;
</span>
<div className="inline-block w-full transform text-left align-middle transition-all 2xl:max-w-7xl">
<div className="group relative">
<div className="animate-tilt absolute -inset-0.5 rotate-2 rounded-lg bg-accent shadow-md transition duration-1000 group-hover:opacity-100 group-hover:duration-200" />
<div className="relative flex flex-col overflow-hidden rounded-2xl bg-base shadow-md md:aspect-[2/1] md:flex-row md:bg-primary">
{children}
</div>
</div>
</div>
</div>
</Dialog>
</AnimatePresence>
);
};

View File

@@ -0,0 +1,25 @@
import React from "react";
import Layout from "@theme/Layout";
export interface PageLayoutProps {
title: string;
description: string;
children: React.ReactNode;
}
export const PageLayout = ({
title,
description,
children,
}: PageLayoutProps): JSX.Element => {
return (
<Layout title={title} description={description}>
{children}
</Layout>
);
};
export interface ColorModeProps {
children: React.ReactNode;
}

View File

@@ -0,0 +1,31 @@
import React from "react";
import { FiExternalLink } from "react-icons/fi";
export interface SocialCardProps {
children: React.ReactNode;
color: string;
link: string;
}
export const SocialCard = ({
children,
color,
link,
}: SocialCardProps): JSX.Element => {
return (
<div
className={`group relative flex h-24 w-36 min-w-max flex-shrink-0 rounded-xl shadow-xl ${color} m-2`}
>
{children}
<a
className="absolute top-0 left-0 right-0 bottom-0 hidden rounded-xl border border-accent bg-secondary bg-opacity-95 text-2xl shadow-xl group-hover:flex"
href={link}
rel="noreferrer"
target="_blank"
>
<FiExternalLink className="m-auto" />
</a>
</div>
);
};

View File

@@ -0,0 +1,345 @@
import React, { useEffect } from "react";
import { Protobuf, Types } from "@meshtastic/meshtasticjs";
interface Region {
freq_start: number;
freq_end: number;
duty_cycle: number;
spacing: number;
power_limit: number;
}
interface Modem {
bw: number;
cr: number;
sf: number;
}
const RegionData = new Map<Protobuf.Config_LoRaConfig_RegionCode, Region>([
[
Protobuf.Config_LoRaConfig_RegionCode.US,
{
freq_start: 902.0,
freq_end: 928.0,
duty_cycle: 100,
spacing: 0,
power_limit: 30,
},
],
[
Protobuf.Config_LoRaConfig_RegionCode.EU_433,
{
freq_start: 433.0,
freq_end: 434.0,
duty_cycle: 10,
spacing: 0,
power_limit: 12,
},
],
[
Protobuf.Config_LoRaConfig_RegionCode.EU_868,
{
freq_start: 869.4,
freq_end: 869.65,
duty_cycle: 10,
spacing: 0,
power_limit: 27,
},
],
[
Protobuf.Config_LoRaConfig_RegionCode.CN,
{
freq_start: 470.0,
freq_end: 510.0,
duty_cycle: 100,
spacing: 0,
power_limit: 19,
},
],
[
Protobuf.Config_LoRaConfig_RegionCode.JP,
{
freq_start: 920.8,
freq_end: 927.8,
duty_cycle: 100,
spacing: 0,
power_limit: 16,
},
],
[
Protobuf.Config_LoRaConfig_RegionCode.ANZ,
{
freq_start: 915.0,
freq_end: 928.0,
duty_cycle: 100,
spacing: 0,
power_limit: 30,
},
],
[
Protobuf.Config_LoRaConfig_RegionCode.RU,
{
freq_start: 868.7,
freq_end: 869.2,
duty_cycle: 100,
spacing: 0,
power_limit: 20,
},
],
[
Protobuf.Config_LoRaConfig_RegionCode.KR,
{
freq_start: 920.0,
freq_end: 923.0,
duty_cycle: 100,
spacing: 0,
power_limit: 0,
},
],
[
Protobuf.Config_LoRaConfig_RegionCode.TW,
{
freq_start: 920.0,
freq_end: 925.0,
duty_cycle: 100,
spacing: 0,
power_limit: 0,
},
],
[
Protobuf.Config_LoRaConfig_RegionCode.IN,
{
freq_start: 865.0,
freq_end: 867.0,
duty_cycle: 100,
spacing: 0,
power_limit: 30,
},
],
[
Protobuf.Config_LoRaConfig_RegionCode.NZ_865,
{
freq_start: 864.0,
freq_end: 868.0,
duty_cycle: 100,
spacing: 0,
power_limit: 36,
},
],
[
Protobuf.Config_LoRaConfig_RegionCode.TH,
{
freq_start: 920.0,
freq_end: 925.0,
duty_cycle: 100,
spacing: 0,
power_limit: 16,
},
],
[
Protobuf.Config_LoRaConfig_RegionCode.UA_433,
{
freq_start: 433.0,
freq_end: 434.7,
duty_cycle: 10,
spacing: 0,
power_limit: 10,
},
],
[
Protobuf.Config_LoRaConfig_RegionCode.UA_868,
{
freq_start: 868.0,
freq_end: 868.6,
duty_cycle: 1,
spacing: 0,
power_limit: 14,
},
],
[
Protobuf.Config_LoRaConfig_RegionCode.LORA_24,
{
freq_start: 2400.0,
freq_end: 2483.5,
duty_cycle: 100,
spacing: 0,
power_limit: 10,
},
],
[
Protobuf.Config_LoRaConfig_RegionCode.UNSET,
{
freq_start: 902.0,
freq_end: 928.0,
duty_cycle: 100,
spacing: 0,
power_limit: 30,
},
],
]);
const modemPresets = new Map<Protobuf.Config_LoRaConfig_ModemPreset, Modem>([
[
Protobuf.Config_LoRaConfig_ModemPreset.SHORT_FAST,
{
bw: 250,
cr: 8,
sf: 7,
},
],
[
Protobuf.Config_LoRaConfig_ModemPreset.SHORT_SLOW,
{
bw: 250,
cr: 8,
sf: 8,
},
],
[
Protobuf.Config_LoRaConfig_ModemPreset.MEDIUM_FAST,
{
bw: 250,
cr: 8,
sf: 9,
},
],
[
Protobuf.Config_LoRaConfig_ModemPreset.MEDIUM_SLOW,
{
bw: 250,
cr: 8,
sf: 10,
},
],
[
Protobuf.Config_LoRaConfig_ModemPreset.LONG_FAST,
{
bw: 250,
cr: 8,
sf: 11,
},
],
[
Protobuf.Config_LoRaConfig_ModemPreset.LONG_MODERATE,
{
bw: 125,
cr: 8,
sf: 11,
},
],
[
Protobuf.Config_LoRaConfig_ModemPreset.LONG_SLOW,
{
bw: 125,
cr: 8,
sf: 12,
},
],
[
Protobuf.Config_LoRaConfig_ModemPreset.VERY_LONG_SLOW,
{
bw: 62.5,
cr: 8,
sf: 12,
},
],
]);
export const FrequencyCalculator = (): JSX.Element => {
const [modemPreset, setModemPreset] =
React.useState<Protobuf.Config_LoRaConfig_ModemPreset>(
Protobuf.Config_LoRaConfig_ModemPreset.LONG_FAST,
);
const [region, setRegion] =
React.useState<Protobuf.Config_LoRaConfig_RegionCode>(
Protobuf.Config_LoRaConfig_RegionCode.US,
);
const [channel, setChannel] = React.useState<Types.ChannelNumber>(
Types.ChannelNumber.PRIMARY,
);
const [numChannels, setNumChannels] = React.useState<number>(0);
const [channelFrequency, setChannelFrequency] = React.useState<number>(0);
useEffect(() => {
const selectedRegion = RegionData.get(region);
const selectedModemPreset = modemPresets.get(modemPreset);
const calculatedNumChannels = Math.floor(
(selectedRegion.freq_end - selectedRegion.freq_start) /
(selectedRegion.spacing + selectedModemPreset.bw / 1000),
);
setNumChannels(calculatedNumChannels);
let updatedChannel = channel;
if (updatedChannel >= calculatedNumChannels) {
updatedChannel = 0;
}
setChannel(updatedChannel);
setChannelFrequency(
selectedRegion.freq_start +
selectedModemPreset.bw / 2000 +
updatedChannel * (selectedModemPreset.bw / 1000),
);
}, [modemPreset, region, channel]);
return (
<div className="flex flex-col border-l-[5px] shadow-md my-4 border-accent rounded-lg p-4 bg-secondary gap-2">
<div className="flex gap-2">
<label>Modem Preset:</label>
<select
value={modemPreset}
onChange={(e) =>
setModemPreset(
parseInt(
e.target.value,
) as Protobuf.Config_LoRaConfig_ModemPreset,
)
}
>
{Array.from(modemPresets.keys()).map((key) => (
<option key={key} value={key}>
{Protobuf.Config_LoRaConfig_ModemPreset[key]}
</option>
))}
</select>
</div>
<div className="flex gap-2">
<label>Region:</label>
<select
value={region}
onChange={(e) => setRegion(parseInt(e.target.value))}
>
{Array.from(RegionData.keys()).map((key) => (
<option key={key} value={key}>
{Protobuf.Config_LoRaConfig_RegionCode[key]}
</option>
))}
</select>
</div>
<div className="flex gap-2">
<label>Channel:</label>
<select
value={channel}
onChange={(e) => setChannel(parseInt(e.target.value))}
>
{Array.from(Array(numChannels).keys()).map((key) => (
<option key={key} value={key}>
{key + 1}
</option>
))}
</select>
</div>
<div className="flex gap-2">
<label className="font-semibold">Number of channels:</label>
<input type="number" disabled value={numChannels} />
</div>
<div className="flex gap-2">
<label className="font-semibold">Channel Frequency:</label>
<input type="number" disabled value={channelFrequency} />
</div>
</div>
);
};

208
src/css/custom.css Normal file
View File

@@ -0,0 +1,208 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
/* You can override the default Infima variables here. */
:root {
--ifm-color-primary: #eab167;
--ifm-color-primary-dark: rgb(175, 102, 33);
--ifm-color-primary-darker: rgb(165, 85, 31);
--ifm-color-primary-darkest: rgb(136, 75, 26);
--ifm-color-primary-light: rgb(203, 130, 70);
--ifm-color-primary-lighter: rgb(212, 164, 102);
--ifm-color-primary-lightest: rgb(224, 199, 146);
--ifm-list-item-margin: 0;
--ifm-code-font-size: 95%;
--ifm-z-index-fixed: 1;
--accent: #eab167;
--base: #f3f4f6;
--primary: #ffffff;
--secondary: #e5e7eb;
--tertiary: #d1d5db;
--mute: #6b7280;
--primaryInv: #242526;
--secondaryInv: #18191a;
--tertiaryInv: #4c4e50;
}
[data-theme="dark"] {
--base: #38393b;
--primary: #242526;
--secondary: #18191a;
--tertiary: #4c4e50;
--mute: #9ca3af;
--primaryInv: #ffffff;
--secondaryInv: #e5e7eb;
--tertiaryInv: #d1d5db;
}
.docusaurus-highlight-code-line {
background-color: rgb(72, 77, 91);
display: block;
margin: 0 calc(-1 * var(--ifm-pre-padding));
padding: 0 var(--ifm-pre-padding);
}
.header-github-link:hover {
opacity: 0.6;
}
.header-github-link:before {
content: "";
width: 24px;
height: 24px;
display: flex;
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")
no-repeat;
}
html[data-theme="dark"] .header-github-link:before {
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")
no-repeat;
}
@keyframes pulse {
0% {
transform: scale(0.98);
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.7);
}
70% {
transform: scale(1);
box-shadow: 0 0 0 10px rgba(0, 0, 0, 0);
}
100% {
transform: scale(0.98);
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
}
}
.cta--button {
--ifm-button-border-color: var(--ifm-link-color);
color: var(--ifm-link-color);
}
.split-container {
display: flex;
}
.split-item {
flex: 1;
}
.indexCtasBody {
--ifm-button-size-multiplier: 1.6;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
margin-top: 24px;
}
.indexCtasBody a:last-of-type {
margin: 20px 36px;
}
.indexCtas {
--ifm-button-size-multiplier: 1.6;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
margin-top: 24px;
}
.indexCtas a {
color: var(--ifm-font-color-base-inverse);
--ifm-button-border-color: var(--ifm-font-color-base-inverse);
}
.indexCtas a:hover {
color: rgb(77, 77, 77);
--ifm-button-border-color: rgb(77, 77, 77);
}
.indexCtas a:last-of-type {
margin: 20px 36px;
}
.theme-doc-markdown {
max-width: none;
}
a + .navbar__link {
display: flex;
gap: 2px;
}
a + .navbar__link > svg {
margin-top: auto;
margin-bottom: auto;
}
@layer base {
.markdown {
@apply prose prose-lg;
}
.markdown img {
@apply rounded-lg mx-auto;
}
[data-theme="dark"] .markdown {
@apply prose-invert;
}
[data-theme="dark"] .hideDark {
@apply hidden;
}
[data-theme="dark"] .hideLight {
@apply block;
}
.hideLight {
@apply hidden;
}
.hideDark {
@apply block;
}
}
.markdown :where(li):not(:where([class~="not-prose"] *)) {
margin-bottom: 0;
margin-top: 0;
}
.markdown
:where(ul ul, ul ol, ol ul, ol ol):not(:where([class~="not-prose"] *)) {
margin-bottom: 0;
margin-top: 0;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: var(--ifm-heading-font-weight);
}
h1 {
font-size: var(--ifm-h1-font-size);
}
h2 {
font-size: var(--ifm-h2-font-size);
}
h3 {
font-size: var(--ifm-h3-font-size);
}
h4 {
font-size: var(--ifm-h4-font-size);
}
h5 {
font-size: var(--ifm-h5-font-size);
}
h6 {
font-size: var(--ifm-h6-font-size);
}

View File

@@ -0,0 +1,29 @@
import React from "react";
import { Showcase } from "../utils/apiTypes";
import { useSelectedTags } from "./useSelectedTags";
const filterNetworks = (
showcaseNetworks: Showcase[],
selectedTags: string[],
) => {
if (selectedTags.length === 0) {
return showcaseNetworks;
}
return showcaseNetworks.filter((showcaseNetwork) => {
if (showcaseNetwork.tags.length === 0) {
return false;
}
return selectedTags.every((queryTag) =>
showcaseNetwork.tags.find((searchTag) => searchTag.label === queryTag),
);
});
};
export const useFilteredNetworks = (networks: Showcase[]) => {
const selectedTags = useSelectedTags();
return React.useMemo(
() => filterNetworks(networks, selectedTags),
[selectedTags],
);
};

View File

@@ -0,0 +1,16 @@
import React from "react";
import { useLocation } from "@docusaurus/router";
import { readSearchTags } from "../pages/showcase/_components/TagSelect";
export const useSelectedTags = () => {
const location = useLocation();
const [selectedTags, setSelectedTags] = React.useState<string[]>([]);
React.useEffect(() => {
const tags = readSearchTags(location.search);
setSelectedTags(tags);
}, [location]);
return selectedTags;
};

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;

2
src/theme/NotFound.d.ts vendored Normal file
View File

@@ -0,0 +1,2 @@
/// <reference types="react" />
export default function NotFound(): JSX.Element;

33
src/theme/NotFound.js Normal file
View File

@@ -0,0 +1,33 @@
import React from "react";
import Translate, { translate } from "@docusaurus/Translate";
import { PageMetadata } from "@docusaurus/theme-common";
import Layout from "@theme/Layout";
export default function NotFound() {
return (
<>
<PageMetadata
title={translate({
id: "theme.NotFound.title",
message: "Page Not Found",
})}
/>
<Layout>
<main className="container margin-vert--xl">
<div className="row">
<div className="col col--6 col--offset-3">
<h1 className="hero__title">
<Translate
id="theme.NotFound.title"
description="The title of the 404 page"
>
404 - Page Not Found
</Translate>
</h1>
<img src="/design/chirpy.png" alt='Chirpy' />
</div>
</div>
</main>
</Layout>
</>
);
}

65
src/utils/apiTypes.ts Normal file
View File

@@ -0,0 +1,65 @@
export interface Showcase {
id: string;
title: string;
summary: string;
body: string;
createdAt: Date;
updatedAt: Date;
tags: ShowcaseTag[];
nodes?: Node[];
materials?: Material[];
author?: Author;
authorId?: string;
}
export interface ShowcaseTag {
id: string;
label: string;
description: string;
color: string;
showcases?: Showcase[];
}
export interface Node {
id: string;
latitude: string;
longitude: string;
showcase?: Showcase;
showcaseId?: string;
}
export interface Material {
id: string;
name: string;
details: string;
image: string;
url: string;
showcases?: Showcase[];
}
export interface Author {
id: string;
githubUsername: string;
bio: string;
showcase?: Showcase[];
}
export interface DeviceFirmwareResource {
id: string;
title: string;
page_url?: string;
zip_url?: string;
}
export interface FirmwareReleases {
releases: {
stable: DeviceFirmwareResource[];
alpha: DeviceFirmwareResource[];
};
pullRequests: DeviceFirmwareResource[];
}

1
src/utils/breakpoints.ts Normal file
View File

@@ -0,0 +1 @@
export const BREAKPOINTS = { sm: 640, md: 768, lg: 1024, xl: 1280 };

23
src/utils/calculateADC.ts Normal file
View File

@@ -0,0 +1,23 @@
export default function calculateADC() {
//const variables
const BAT_MILLIVOLTS_FULL = 4.2;
const BAT_MILLIVOLTS_EMPTY = 3.27;
const BAT_FULL_PERCENT = 1;
//variable
const batteryChargePercent =
parseFloat(
(<HTMLInputElement>document.getElementById("batteryChargePercent")).value,
) / 100;
const operativeAdcMultiplier = parseFloat(
(<HTMLInputElement>document.getElementById("operativeAdcMultiplier")).value,
);
const result =
(operativeAdcMultiplier *
((BAT_FULL_PERCENT - 1) * BAT_MILLIVOLTS_EMPTY -
BAT_FULL_PERCENT * BAT_MILLIVOLTS_FULL)) /
((batteryChargePercent - 1) * BAT_MILLIVOLTS_EMPTY -
batteryChargePercent * BAT_MILLIVOLTS_FULL);
(<HTMLInputElement>(
document.getElementById("newOperativeAdcMultiplier")
)).value = result.toFixed(4);
}

13
src/utils/map.ts Normal file
View File

@@ -0,0 +1,13 @@
import { Node } from "./apiTypes";
export const mapUrl = (nodes: Node[]): string => {
const width = 900;
const height = 400;
const access_token =
"pk.eyJ1Ijoic2FjaGF3IiwiYSI6ImNrNW9meXozZjBsdW0zbHBjM2FnNnV6cmsifQ.3E4n8eFGD9ZOFo-XDVeZnQ";
const nodeCoords = nodes.map(
({ latitude, longitude }) => `pin-l+67ea94(${longitude},${latitude})`,
);
return `https://api.mapbox.com/styles/v1/mapbox/satellite-v9/static/${nodeCoords}/auto/${width}x${height}@2x?access_token=${access_token}`;
};

22
src/utils/showcase.ts Normal file
View File

@@ -0,0 +1,22 @@
export const sortBy = <T>(array: T[], getter: (item: T) => unknown): T[] => {
const sortedArray = [...array];
sortedArray.sort((a, b) =>
getter(a) > getter(b) ? 1 : getter(b) > getter(a) ? -1 : 0,
);
return sortedArray;
};
export const difference = <T>(...arrays: T[][]): T[] => {
return arrays.reduce((a, b) => a.filter((c) => !b.includes(c)));
};
export const toggleListItem = <T>(list: T[], item: T): T[] => {
const itemIndex = list.indexOf(item);
if (itemIndex === -1) {
return list.concat(item);
} else {
const newList = [...list];
newList.splice(itemIndex, 1);
return newList;
}
};

1
src/utils/swr.ts Normal file
View File

@@ -0,0 +1 @@
export const fetcher = (url: string) => fetch(url).then((res) => res.json());